HelpWithWebGet Help Now
← Back to Blog
Security12 min read

How to Stop Bots on Shopify: What Works, What Doesn't

Shopify bot protection explained: what custom theme scripts can filter, why browser-side checks are bypassable, and where server-side validation is required.

ByDino Bartolome
Glowing padlock symbolising digital security
Photo by FlyD on Unsplash

Last reviewed: August 6, 2026.

If your Shopify store is getting hammered — junk contact-form submissions, fake account signups, carts filling with limited stock, checkout attempts that never convert — you have a bot problem, and the internet will happily sell you the wrong fix for it.

The short version of what follows: custom scripts in your theme can reduce basic, unsophisticated bot traffic, but browser-side JavaScript on its own is not complete protection. Anything running in the visitor's browser can be read, modified, skipped or replayed by someone who wants to. Real enforcement has to happen somewhere the visitor doesn't control — a Shopify app, an app proxy endpoint, or another server-side workflow.

That distinction runs through this whole article, because it's the thing most Shopify "bot protection" advice gets wrong.

How Shopify bots affect stores

Not all bot traffic hurts you the same way, and the fix depends entirely on which one you have.

  • Form spam. Junk submissions to contact forms, newsletter signups and product reviews. Mostly a nuisance and an inbox problem, occasionally a vector for phishing replies.
  • Fake account signups. Bulk-created customer accounts, sometimes to farm discount codes or first-order promotions.
  • Credential stuffing. Automated login attempts using username and password pairs leaked from other sites, looking for accounts to take over.
  • Card testing. Repeated small checkout attempts using stolen card numbers to find which ones still work. This one costs you real money in gateway fees and can affect your processor standing.
  • Cart and inventory abuse. Automated add-to-cart on limited or high-demand stock, so genuine customers see "sold out". Whether this actually holds inventory depends on how your store reserves stock.
  • Scraping. Competitors pulling prices and product data. Usually harmless to operations, but it inflates your session counts and distorts analytics.

These have different fixes. Adding a CAPTCHA to your contact form does nothing about card testing. Rate-limiting add-to-cart does nothing about credential stuffing. So start by working out which one you actually have.

Identify which type of bot problem you have

Before changing anything, get evidence. Guessing here leads to stacking defences that don't address the real source and degrade the experience for genuine customers.

Look at:

  • Which endpoint is being hit. Are the requests going to /cart/add, /account/login, /contact, or checkout? Your Shopify admin, app logs and any analytics you run will tell you. The endpoint identifies the attack.
  • Order and checkout data. A spike in failed or cancelled transactions with small order values points at card testing. Shopify's fraud analysis and your payment provider's dashboard are the useful sources here.
  • Timing. Traffic arriving at exact intervals, or in bursts that start and stop cleanly, is automated. Human traffic has ragged edges.
  • Account activity. A rise in failed logins concentrated on a small number of accounts is credential stuffing. Spread across many accounts, it's enumeration.
  • Session behaviour. Sessions that touch one endpoint repeatedly and nothing else are not shoppers.

Write down what you find before you start installing things. If you can't say which endpoint is being abused, you are not ready to pick a fix.

What custom Shopify scripts can do

You can add JavaScript to the theme files you're allowed to edit, and use it for storefront behaviour, custom forms, cart controls and lightweight bot filtering. Used sensibly, custom scripts are good at raising the cost of low-effort automation:

  • Honeypot fields — a form field hidden from humans that naive bots fill in anyway.
  • Minimum form-completion time — rejecting submissions completed implausibly fast.
  • Interaction checks — looking for mouse movement, keyboard input, touch or focus events before treating a submission as human.
  • Repeated-submission detection — noticing the same session submitting the same form over and over.
  • Suspicious-behaviour scoring — combining several weak signals into one score rather than relying on any single check.
  • Conditional CAPTCHA challenges — only showing a challenge when the score looks bad, so ordinary customers never see one.
  • Short-lived browser tokens — issuing a token that expires quickly, to be validated server-side.
  • Sending requests to a Shopify app proxy or external validation endpoint — so the actual decision is made somewhere the visitor can't tamper with.

That last point is the one that matters. Client-side checks are useful as signal collection. They should not be the thing that makes the final allow-or-block decision.

A honeypot field

Warning: every snippet in this article is a client-side signal, not an enforcement mechanism. All of it can be bypassed by an attacker who reads your theme source — which is public. Treat these as inputs to a server-side decision, never as the decision itself.
<!-- Visually hidden from humans; naive bots fill it in anyway. -->
<div aria-hidden="true" style="position:absolute;left:-9999px">
  <label>Company website
    <input type="text" name="website_url" tabindex="-1" autocomplete="off">
  </label>
</div>

If website_url arrives non-empty, the submission is very likely automated. Note that this only catches bots that blindly fill every field — anything targeting your store specifically will skip it.

Submission timing

// Record when the form became interactive.
const form = document.querySelector('#contact-form');
const renderedAt = Date.now();

form.addEventListener('submit', (event) => {
  const elapsedMs = Date.now() - renderedAt;
  // A human does not read and complete a contact form in under 3 seconds.
  form.querySelector('[name="elapsed_ms"]').value = String(elapsedMs);
  // Do NOT block here based on elapsedMs alone — send it and let the
  // server decide. A bot can simply set this value to whatever it likes.
});

Basic interaction tracking

// Collect weak signals. None of these prove humanity on their own.
const signals = { moved: false, typed: false, touched: false, focused: false };

window.addEventListener('mousemove', () => { signals.moved = true; }, { once: true, passive: true });
window.addEventListener('keydown',   () => { signals.typed = true; }, { once: true });
window.addEventListener('touchstart',() => { signals.touched = true; }, { once: true, passive: true });
document.addEventListener('focusin', () => { signals.focused = true; }, { once: true });

// These events can be synthesised by an automated browser. Treat a positive
// result as "no evidence of automation", not as "confirmed human".

Sending a token to a backend for validation

// Ask our own backend (via a Shopify app proxy) whether this submission
// should be allowed. The decision happens server-side.
async function requestDecision(payload) {
  const response = await fetch('/apps/bot-check/validate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload),
  });
  if (!response.ok) return { allow: false, reason: 'validation_unavailable' };
  return response.json(); // { allow, challenge, reason }
}

// The server must re-check everything. Never trust a client that says
// "I already validated myself" — an attacker can call this endpoint directly
// with any payload they like.

What browser-side scripts cannot do

This is the part usually left out. A determined attacker can:

  • Disable JavaScript entirely and submit forms or hit endpoints directly, skipping every check you wrote.
  • Call your endpoints directly with a plain HTTP client, never loading your theme at all.
  • Forge browser events — headless and instrumented browsers can generate real mouse, keyboard and touch events that are indistinguishable from a person's at the DOM level.
  • Replay tokens — capture a valid token once and reuse it across thousands of requests unless the server enforces single use and expiry.
  • Read and bypass your logic — your theme JavaScript is downloaded by every visitor, including the attacker. Any threshold, field name or scoring rule in it is public knowledge.

So a check like "reject if the form was completed in under three seconds" stops a script somebody wrote in an afternoon. It does not stop anyone who spends an hour looking at your code.

Stronger protection requires server-side validation — through a Shopify app, an app proxy, a supported backend service, or another server-controlled workflow. That's where the token gets verified, the rate limit gets counted, and the decision actually gets made.

Protecting contact, newsletter, and account forms

Before you build anything: Shopify already provides hCaptcha protection on several standard storefront forms, including account creation and login, contact forms, newsletter signups, password recovery and blog comments, where applicable to your theme and setup. It's designed to challenge suspicious visitors while leaving ordinary customers alone.

Check whether it's enabled and working on your store before adding anything else.

Do not automatically layer a second CAPTCHA on top without first identifying the actual source of abuse. Two challenge systems on one form means more friction, more accessibility problems, more ways for the form to break, and more abandoned submissions from real customers — often with no measurable reduction in spam, because you never established that the challenge was the missing piece.

If Shopify's protection is on and spam is still getting through, then the useful next steps are:

  1. Confirm which form is the target, and whether the submissions arrive through the form or by direct POST to the endpoint.
  2. Add a honeypot and timing signals, and send them to a server-side endpoint for the decision.
  3. Rate-limit per session and per source at the server, not in the browser.

If your contact form is broken or misbehaving in ways that look like spam but aren't, that's a different problem — see fixing a contact form that isn't working.

Protecting carts and inventory

Cart abuse is where theme JavaScript is most often misused. A script in your theme cannot securely enforce a purchase limit, because the visitor can call /cart/add directly without ever running it.

Controls that are actually worth implementing:

  • Quantity limits — enforced server-side. Shopify Functions can apply cart and checkout validation logic that runs on Shopify's side rather than in the browser.
  • Repeated add-to-cart monitoring — track how often a session or source adds the same variant, and act on it server-side.
  • Suspicious request patterns — identical intervals, no product-page views before the add, no session history.
  • Inventory reservation abuse — understand when your store actually reserves stock. Items sitting in a cart behave differently from items in an active checkout, and that difference determines whether "cart hoarding" is a real problem for you or a cosmetic one.
  • App-based rate limiting — a Shopify app can see and act on request patterns that theme code cannot.
  • Server-side validation — the final check on quantity and eligibility must happen where the customer can't reach it.

A theme script that greys out the add-to-cart button after two clicks is a user-experience nicety. It is not an inventory control. Anyone who cares will bypass it in seconds.

For custom cart and storefront work, see Shopify customization help.

Shopify checkout bot protection

Checkout is not an ordinary theme page, and you should not try to treat it like one.

Do not go looking for ways to inject arbitrary custom JavaScript into checkout. Checkout customization is deliberately restricted — it's the part of the platform handling payment details, and Shopify limits what can run there for good reasons. Advice telling you to paste tracking or blocking scripts into checkout is usually out of date, and following it can break the page or violate the terms you agreed to.

What's actually true:

  • Checkout customization is restricted. You cannot freely edit it the way you edit a product template.
  • Advanced checkout customization generally requires supported checkout extensions — checkout UI extensions and Shopify Functions — rather than raw script injection.
  • Some advanced functionality requires Shopify Plus. Per Shopify's developer documentation, checkout UI extensions for the information, shipping and payment steps are available only to stores on a Shopify Plus plan.
  • Custom pixels are for analytics and event tracking, not blocking bots. They exist to record what happened. They are not an enforcement mechanism and cannot stop a request.
  • Shopify's dedicated auto-checkout bot protection is aimed at limited-release and high-demand product drops, and may require Shopify Plus. It is built for the drop scenario specifically — it is not a general-purpose switch that hardens every store against every bot.

If your problem is card testing at checkout, the levers that matter are your payment provider's fraud controls, Shopify's own fraud analysis, and — where available to your plan — Shopify's checkout bot protection. Not theme JavaScript.

When Shopify Plus is required

Worth being blunt about this, because it changes what's realistic on your budget:

  • Checkout UI extensions for the information, shipping and payment steps are Plus-only, per Shopify's developer documentation.
  • Shopify's auto-checkout bot protection for high-demand drops may require Plus.
  • Deeper checkout branding and control is generally a Plus-tier capability.

If you are not on Plus, you still have: Shopify's built-in hCaptcha on standard forms, Shopify Functions for cart and checkout validation, app proxy endpoints for your own server-side checks, apps from the Shopify App Store, and your payment provider's fraud tooling. That is a genuinely useful set of tools. It just doesn't include rewriting checkout.

Check your plan and the current Shopify documentation before you design around a feature — plan requirements change, and it's cheaper to confirm now than after you've built.

Server-side validation architecture

Here's the shape that actually works. Every step after the first happens somewhere the visitor cannot modify:

Browser behaviour checks
  → short-lived token
    → Shopify app proxy or backend endpoint
      → server-side validation
        → allow, challenge, rate-limit, or reject

How the pieces fit:

  1. Browser behaviour checks collect signals — honeypot state, elapsed time, whether any interaction events fired. These are inputs, and they're all forgeable.
  2. A short-lived token is issued and tied to a single submission, with a short expiry. The server records which tokens it has issued.
  3. A Shopify app proxy lets a request to your storefront (for example /apps/your-app/validate) be forwarded to your own backend. Shopify signs proxied requests, so your backend can confirm the request genuinely came through Shopify rather than being sent directly by an attacker — verify that signature rather than trusting the request.
  4. Server-side validation re-checks everything: is the token real, unexpired and unused? Does the request rate look reasonable for this session or source? Do the submitted signals hold together? The server never assumes the client did its job.
  5. The outcome is one of four things: allow it, challenge it, rate-limit it, or reject it. Preferring "challenge" and "rate-limit" over "reject" keeps false positives from costing you real customers.

The critical property: an attacker calling your endpoint directly, with no browser involved, still hits step 4. That's the whole point. If skipping the JavaScript lets you skip the check, you haven't built protection — you've built a speed bump.

A note on scale: if you're getting volumetric floods rather than targeted abuse, that's a different problem with different tools — see DDoS protection help.

Should you put Cloudflare in front of your Shopify store?

Generally, no — and this is worth stating clearly because it's common advice that causes real problems.

Do not add a separate Cloudflare orange-cloud proxy in front of a standard Shopify storefront as a default solution. Shopify already runs its storefronts on Cloudflare infrastructure. Adding your own proxy layer on top is an unsupported configuration that can cause connectivity failures, SSL and certificate errors, checkout problems and domain-verification issues — and when it breaks, you're troubleshooting a setup Shopify doesn't support.

Shopify's own documentation specifies how to point a custom domain at a Shopify store. Follow that. If you're using Cloudflare purely as a DNS host with proxying turned off, and following Shopify's documented DNS records, that's a different situation — but the orange cloud in front of the storefront is the part to avoid.

If you need help with DNS or Cloudflare configuration generally, see Cloudflare setup help.

Practical implementation checklist

In order. Don't skip to the bottom.

  1. Identify the abuse. Which endpoint, what pattern, what evidence? Write it down.
  2. Check what's already on. Is Shopify's built-in hCaptcha active on the affected forms? Is your payment provider's fraud screening configured?
  3. Fix the specific problem, not bots in general. Match the control to the abuse you documented in step 1.
  4. Add client-side signals — honeypot, timing, interaction — as inputs only.
  5. Put the decision server-side, behind an app proxy or app, with signature verification and single-use, short-expiry tokens.
  6. Rate-limit server-side, per session and per source.
  7. Use Shopify Functions for cart and checkout validation rules that must actually hold.
  8. Prefer challenge and rate-limit over hard rejection, so false positives cost you a moment rather than a customer.
  9. Log every decision with enough context to tell later whether you blocked a bot or a customer.
  10. Re-measure. Did the abuse actually drop? If not, revisit step 1 — you probably fixed the wrong thing.
  11. Do not stack CAPTCHAs hoping volume solves it.
  12. Do not put an unsupported proxy in front of checkout.

If you want a second pair of eyes on custom theme or app code before it goes live, a code audit is usually cheaper than debugging a broken checkout later.

Frequently asked questions

Can I stop Shopify bots with theme JavaScript alone? You can reduce basic automated traffic — the kind that fills in every field and submits instantly. You cannot stop a determined attacker, because your theme code is public and they can skip it entirely by calling your endpoints directly. Use it for signals; enforce server-side.

Does Shopify already protect my forms? Shopify provides hCaptcha on several standard storefront forms, including account creation and login, contact, newsletter, password recovery and comment forms, where applicable. Confirm it's active on your store before adding a second system.

Should I add reCAPTCHA or Turnstile on top of Shopify's hCaptcha? Not by default. Identify what's actually getting through first. Two challenge systems on one form adds friction and accessibility problems for genuine customers, and often doesn't reduce spam — because the challenge usually wasn't the gap.

Can I block bots at Shopify checkout with custom code? No, not with arbitrary injected JavaScript. Checkout is restricted. Supported customization goes through checkout extensions and Shopify Functions, and some of that is Plus-only. Custom pixels track events; they don't block anything.

Will Cloudflare in front of my store stop bots? Putting your own Cloudflare proxy in front of a standard Shopify storefront is unsupported and can break SSL, checkout and domain verification. Shopify already uses Cloudflare infrastructure. This is not the fix.

What actually stops card testing? Your payment provider's fraud controls, Shopify's fraud analysis, and — if available on your plan — Shopify's checkout bot protection. Theme JavaScript has no role here. Talk to your payment provider early; they see the pattern across many merchants.

Do bots holding items in carts really cost me sales? It depends on when your store reserves inventory. Items in a cart and items in an active checkout are treated differently. Establish which is happening before investing in cart-hoarding defences.

How do I know if my fix worked? Measure the same signal you used to identify the problem — requests to the abused endpoint, failed logins, junk submissions per day. If that number hasn't moved, the fix didn't address the cause.

Get help protecting a Shopify store

If you're dealing with an active problem and want someone to identify what's actually happening before spending money on apps, that's something I do. The work is usually: find the abused endpoint, confirm what Shopify already provides, design the server-side check, and implement it through an app proxy or Shopify Functions rather than theme code that can be walked around.

Related: Shopify customization help · website security and malware removal · contact form repair · code audit · ongoing website support

Reach out via the contact form and describe what you're seeing — which endpoint, what pattern, since when. That's enough to give you a straight answer about whether it needs custom work or a setting you already have.

Need Help With Your Website?

I fix these problems every day. Send me a message and I'll take a look.

Get Help Now
CallTextMessage