A customer emails to say your shop "won't let them in". They saw a Cloudflare page with a four-digit code, gave up, and bought somewhere else. Your server logs show nothing, because the request never got past Cloudflare. This happens on WooCommerce stores, WordPress lead-gen sites, headless frontends on Astro or Next.js and any custom app proxied through Cloudflare. The cause is almost always a rule on your own zone, and Cloudflare records which one.
This guide walks through the order that works: get the Ray ID, look it up, read which feature acted, then narrow that feature so it stops catching buyers and payment callbacks. Every behaviour described below comes from Cloudflare’s developer documentation as of October 2026.
What Cloudflare error 1015, 1020 and 1009 mean
The code tells you where to look. A post in r/CloudFlare on 21 October 2026 summed up the codes, and each one matches Cloudflare’s own troubleshooting pages:
- 1015 (HTTP 429): a rate limiting rule matched. Cloudflare’s advice is to review thresholds and, if a rule counts over a very short period such as one second, try 10 seconds.
- 1020 (HTTP 403): a security rule with a Block action and no custom response. The 1020 page names API Shield schema validation and JWT validation as examples, and states that WAF custom rules and managed rules no longer return 1020. They serve the WAF block page instead.
- 1009 (HTTP 403): a country or region ban set in IP Access rules. Country blocks there are Enterprise-only; on other plans a country block is a custom rule, and that shows the WAF block page.
- 1010 (HTTP 403): Browser Integrity Check rejected the browser signature.
- “Just a moment…” or a challenge page: a Managed Challenge from a custom rule, a rate limiting rule, Bot Fight Mode or Under Attack Mode.
The 1020 detail saves hours. If you are combing through custom rules looking for the one that returned 1020, you are in the wrong list.
How to find the Cloudflare Ray ID
Ask the customer for a screenshot of the whole error page. It carries the code, the Ray ID and a UTC timestamp. The OP of the same thread added this in a reply:
the code’s the easy part, the bit people miss is the ray id on the same screen. paste it into security events and it shows which of your own rules fired.
(u/Hot_Bee_5451, r/CloudFlare, 22 October 2026)
A Ray ID is attached to every request through Cloudflare and sent to your origin in the cf-ray header. Cloudflare warns that Ray IDs are not guaranteed unique, so match the timestamp and IP as well. If the customer cannot send a screenshot, ask for their public IP address and the approximate time and country. For challenge failures there is a second route, suggested in another thread:
Well you can provide the RayID that is created when it runs that test to the website admin and they can look up the exact reason you failed.
(u/Logvin, r/CloudFlare, 21 October 2026)
How to reproduce a Cloudflare block with curl
A screenshot tells you what happened once. Reproducing the request tells you whether it still happens after your fix. From a terminal, request the page the customer could not open and read the headers:
curl -sI https://example.com/checkout/
curl -sI "https://example.com/?wc-api=wc_stripe"Three headers matter. server: cloudflare confirms the answer came from the edge. cf-ray gives you a fresh Ray ID to look up in Security Events. And a cf-mitigated: challenge header means the response is a challenge page: Cloudflare’s challenge detection guide says the header is set for every challenge page type, challenge is its only value, and the body is always text/html, whatever resource was requested.
That last detail explains a common headless failure. An Astro or Next.js frontend that calls its own API with fetch expects JSON, receives an HTML challenge, and fails with a parse error that looks like a code bug. Check response.headers.get("cf-mitigated") before parsing, log it, and you will know within one deploy whether Cloudflare is the cause. Your own IP and browser may not trip the same rule as the customer’s, so a clean result from your desk proves less than a clean result in Security Events.
How to search a Ray ID in Cloudflare Security Events
In the dashboard, open Security > Analytics and the Events tab. Add a filter for Ray ID, or for the client IP, and set the time range to cover the complaint. Convert the UTC time from the error page to your own timezone first; Cloudflare’s 1020 page points this out explicitly.
Two limits from the Security Events documentation matter here. Data is kept for up to 31 days. And the list uses sampled logs, so on a busy zone a single request may not appear. If the Ray ID search returns nothing, narrow the time window to a few minutes and filter by IP; a smaller window shows more events. One request can also produce several events if several features acted on it.
Which Cloudflare rule blocked the visitor
Expand the event. The Service field names the feature, and for rules you also get the rule name or ID. What each one usually means:
- Custom rules: your own expression. Common culprits are country blocks, ASN blocks aimed at hosting providers (which also catch VPN users) and over-broad path rules on
/wp-login.phpor/checkout. - Managed rules: a Cloudflare or OWASP signature matched something in the request. Long search queries, form fields containing code snippets and some page-builder payloads trip these.
- Rate limiting rules: the 1015 case. Shoppers behind one office or mobile carrier IP share a counter when the rule counts by IP.
- Bot Fight Mode or Super Bot Fight Mode: the bot products. Cloudflare’s bot documentation says both may challenge API or mobile app traffic.
- Security Level or Under Attack Mode: the zone-wide interstitial.
A deleted rule shows as “Rule unavailable”. Check the audit log to see who removed or changed it.
How to create a Cloudflare skip rule without opening the whole site
The fix is a custom rule with the Skip action, placed above the rule that blocked, matching only the traffic you trust. Cloudflare’s skip options let you skip all remaining custom rules, all rate limiting rules, all managed rules and all Super Bot Fight Mode rules. Through the API you can also skip Browser Integrity Check, Security Level, User Agent Blocking, Zone Lockdown and Hotlink Protection. Custom rules run in order, so position matters.
Keep the expression narrow. For the WooCommerce Stripe extension, which uses only /?wc-api=wc_stripe as its webhook endpoint:
(http.request.uri.query contains "wc-api=wc_stripe" and ip.src in $stripe_webhooks)Here $stripe_webhooks is an IP list you create from Stripe’s published webhook IP addresses. For a store integration calling the WooCommerce REST API from a fixed server:
(starts_with(http.request.uri.path, "/wp-json/wc/") and ip.src in $erp_servers)For a false positive that hit real shoppers, often the better fix is to change the original rule instead of adding an exception: raise the rate limit period, scope a country block to /wp-login.php, or switch a Block to a Managed Challenge. Leave Log matching requests on for the skip rule so you can see it working.
How to exclude one Cloudflare managed rule instead of the whole WAF
When the event shows a managed rule, do not skip all managed rules for the checkout. Cloudflare’s managed rules exceptions can skip all remaining managed rules, one or more managed rulesets, or one or more individual rules, and the exception has its own expression. Copy the rule ID from the event, create an exception for that rule only, and limit it to the path and method that triggered it, for example POST requests to the form endpoint that carried the payload.
Scope matters. An exception defined at zone level does not stop a match from a managed ruleset deployed at account level, because account-level rulesets run first in each phase. If you manage several shops under one Cloudflare account, check where the managed rules are deployed before writing the exception.
Cloudflare rate limiting settings that stop catching shoppers
The rate limiting rules page lists what each plan can set, and those numbers decide how fine your fix can be:
| Plan | Fields in the rule expression | Counting period | Mitigation timeout | Counting by |
|---|---|---|---|---|
| Free | Path, Verified Bot | 10 s | 10 s | IP |
| Pro | adds Host, URI, Full URI, Query | up to 1 min | up to 1 h | IP |
| Business | adds Method, Source IP, User Agent | up to 10 min | up to 1 day | IP, IP with NAT support |
| Enterprise with Advanced Rate Limiting | general request and header fields | up to 65,535 s | up to 1 day | adds headers, cookies, ASN, country and more |
Counting by IP is the reason an office or a mobile carrier can trip a rule built for a single bot. Three changes from Cloudflare’s rate limiting best practices reduce that:
- Exclude known partners. Cloudflare’s example allows 10 requests per minute per IP to
/statusunless the address is in an IP list namedpartner_ips, usingnot ip.src in $partner_ipsin the expression. Source IP is available in rate limiting expressions only from Business upward; on Free and Pro, put the partner list in a custom skip rule that skips all rate limiting rules, since custom rules can match on the source IP. - Count only failures. From Business upward a custom counting expression is available, so a login rule can count only
POSTrequests that returned 401 or 403. A customer who logs in on the first try never adds to the counter. - Match the exact path. The same page warns that a rule for
/validate/otpwill not match/api/otp/validate. Check the real path in analytics before writing the rule, and keep/wp-json/and checkout AJAX endpoints out of rules meant for login pages.
On Enterprise, the Request rate analysis tab in Security Analytics shows the request rate distribution for the top 50 clients and can pre-fill a rate limiting rule from the selected rate. Cloudflare recommends deploying such a rule with the Log action first.
Can a WAF rule bypass Bot Fight Mode
No. The Bot Fight Mode page says you cannot bypass or skip it with WAF custom rules or Page Rules, because it does not run on the Ruleset Engine, so Skip, Bypass and Allow have no effect. The skip options page repeats that only Super Bot Fight Mode can be skipped. One exception is documented: Bot Fight Mode does not trigger if an IP Access rule matches the request first.
So on the Free plan, if Bot Fight Mode challenges your payment provider, your ERP or your uptime monitor, you have three choices: an IP Access allow rule for that provider’s addresses, turning Bot Fight Mode off, or moving to a paid plan where Super Bot Fight Mode accepts skip rules. Super Bot Fight Mode also has an Optimize for WordPress setting that lets WordPress loopback requests through, which fixes Site Health errors without a custom rule.
Why Cloudflare blocks Stripe webhooks and WooCommerce REST API calls
Server-to-server calls have no browser, so they cannot solve a JavaScript challenge. Any feature that challenges instead of blocking still breaks them. A store owner described exactly this in r/CloudFlare:
When I turn on Under Attack Mode, the Singapore traffic stops in GA4. But Under Attack Mode makes my site slow, and my server-to-server API calls (WooCommerce REST API) get the Cloudflare “Just a moment…” challenge, so they fail.
(u/lastoneinbatumi, r/CloudFlare, 19 October 2026)
The cost differs by provider. Stripe retries a failed webhook with exponential backoff for up to three days in live mode, so orders stuck as “pending payment” usually recover once you fix the rule. Shopify retries up to eight times over four hours and then removes the subscription, which matters if your app or headless backend receives Shopify webhooks behind your Cloudflare zone. Check each provider’s dashboard for failed deliveries after any security change.
Under Attack Mode is meant as a last resort during an attack. Cloudflare’s Security Level page says to use a configuration rule to turn it on or off for your API or any other part of the domain. Keep it off for /wp-json/, webhook paths and your API host.
The same thread had a second lesson. When blocked traffic keeps appearing in analytics, ask whether it goes through Cloudflare at all:
Is your host configured to only allow traffic to and from Cloudflare? Otherwise it could be reached directly by ip
(u/Wilbo007, r/CloudFlare, 19 October 2026)
Cloudflare’s IP address documentation recommends blocking traffic at the origin that does not come from Cloudflare or your trusted partners. Allow your payment providers’ addresses in that origin firewall too.
Update, . Letting a payment provider’s webhooks through Cloudflare does not mean the endpoint should trust whoever calls it. The Wordfence weekly vulnerability report for 12 to 18 October 2026 listed WooCommerce payment plugins whose webhook handlers accepted forged calls: Asaas Gateway before 2.7.8 matched an empty configured token against an empty request token, and Cardinity 3.2.6 to 3.4.1 checked the HMAC signature against an empty project secret, so anyone could mark orders as paid. Visma Pay before 1.2.2 and iPOSpays up to 1.4.6 had unauthenticated payment bypasses in the same week. Cloudflare cannot tell a forged webhook from a real one, so the check belongs in the application: set the webhook secret in the gateway settings, keep the plugin updated, and confirm that paid orders match transactions in the provider dashboard.
How to test a Cloudflare rule without blocking customers
Cloudflare’s custom rule guide offers the Log action for validating a rule before enforcing it, on Enterprise plans only. On other plans it suggests starting with Managed Challenge, reviewing the matched requests in Security Events, and switching to Block once the matches look right. A real customer can pass a Managed Challenge; a Block gives them nothing to do.
A challenged visitor who passes gets a cf_clearance cookie. The Challenge Passage setting controls how long it stays valid: 30 minutes by default, with Cloudflare recommending 15 to 45 minutes. Two consequences for support tickets. A customer who blocks cookies is challenged again on every page, which reads as “the site keeps checking me”. And Challenge Passage does not apply to rate limiting rules, so passing a challenge does not reset a 1015.
How to show blocked customers what to do next
The default block page tells the visitor nothing about your shop. Two options exist on paid plans. Custom Errors are available on Pro, Business and Enterprise, not on Free, and an Error Page of type WAF block replaces the page shown for WAF blocks across the zone. From Pro upward, a single custom rule with the Block action can also return its own response, with a status code between 400 and 499 and an HTML, JSON or plain text body.
Use that page to ask for what you need: a screenshot with the Ray ID, the time, and an email address or phone number that is not behind the same zone. For an API path, return JSON so integrations log a readable error instead of an HTML page. Cloudflare’s custom error rule examples include one for 1020 errors that matches cf.response.1xxx_code eq 1020.
Cloudflare block troubleshooting table
| What the customer or log shows | Likely rule | Fix |
|---|---|---|
| Error 1015, HTTP 429 | Rate limiting rule | Longer counting period, higher threshold, or skip rate limiting for trusted IPs |
| WAF block page, HTTP 403, no 1xxx code | WAF custom rule or managed rule | Find the rule by Ray ID; narrow the expression or add a skip rule above it |
| Error 1020, HTTP 403 | Another Block without custom response (for example API Shield) | Look in Security Events, not in custom rules |
| Error 1009 | Country block in IP Access rules | Allow the visitor’s IP or move geoblocking to a scoped custom rule |
| Error 1010 | Browser Integrity Check | Skip it for the affected path via API, or turn it off |
| Challenge loop on every page | Under Attack Mode or Security Level | Turn it off, or limit it with a configuration rule |
| Webhook or REST API failing, no human involved | Bot Fight Mode, Under Attack Mode or a challenge rule | IP Access allow or Super Bot Fight Mode skip rule; keep UAM off API paths |
| Nothing in Security Events | Sampling, wrong time zone, or the request never reached Cloudflare | Smaller time window, filter by IP; check DNS and the origin firewall |
How to monitor Cloudflare blocks after the fix
Do not stop at one customer. In Security Events, filter by Action equal to Block or Managed Challenge and group by path. Requests to /checkout, /cart, /my-account, /wp-json/ or your contact form endpoint are the ones costing money. The filtered URL can be shared, so a colleague sees the same view. Up to 500 sampled events can be exported as JSON.
Then check the other side: failed deliveries in Stripe, PayPal or Shopify, WooCommerce orders stuck in pending, and contact form submissions per day. A drop the day after a security change points straight at the rule you added. My Cloudflare edge service covers rule audits of this kind, and the store reliability plan includes this monitoring for WooCommerce shops.
Related reading: whether Cloudflare’s AI bot settings also stop search crawlers is covered in does Cloudflare block Googlebot. How much of a small site’s traffic is automated, and why that tempts owners into aggressive bot rules, is in 72% of my traffic is not a browser. For moving logic to the edge, see Cloudflare Workers and WooCommerce.







