Troubleshooting

Load Testing a Site Behind Cloudflare

Updated October 27, 2026•10 min read•CDN•WAF

Written by

Reviewed by the LoadTester editorial team. Review process: see the editorial policy.

Published
2026-10-27
Last reviewed
2026-10-27
Author
Kristian Razum
Load Testing a Site Behind Cloudflare: LoadTester illustration

Load testing a site behind Cloudflare usually fails in one of two ways: the test traffic is blocked or challenged (HTTP 403, 429, challenge pages) because a few IPs sending thousands of requests look like a bot, or it succeeds against Cloudflare's cache and never measures your origin. Decide first whether you are testing the edge or the origin. Then either allowlist the load generator's IPs with a WAF custom rule using the Skip action, or test the origin directly. Check cf-cache-status on responses to see what you actually measured.

What goes wrong when you test through Cloudflare

Run a load test against a Cloudflare-proxied hostname and a few seconds in you typically see one or more of these:

What the generator recordsWhat it means
403 with header cf-mitigated: challenge, HTML body with a challenge pageA managed or JS challenge was issued. Load generators cannot solve it, so every request "fails" quickly.
403, Cloudflare block page, error 1020 (Access denied)A WAF custom rule or managed rule blocked the request.
429, Cloudflare page with error 1015 (You are being rate limited)A rate limiting rule matched the generator's IPs.
Very low latency, near-zero origin CPU, cf-cache-status: HITYou are measuring Cloudflare's cache, not your application.
520-526 errorsCloudflare reached for your origin and failed. These are real origin problems (see the table below).

The first three mean the test is invalid: the error rate is Cloudflare protecting you, and the latency of challenge pages tells you nothing. The fourth means the test is valid for the edge but says nothing about origin capacity. Only the 52x errors are real findings about your stack.

The security features that usually fire are Bot Fight Mode (or Super Bot Fight Mode on paid plans), rate limiting rules, WAF managed rules, Browser Integrity Check (which flags clients with missing or unusual headers), Security Level, and, if someone enabled it, Under Attack mode, which challenges every visitor.

Decide what you are testing: edge or origin

These are two different questions, and mixing them produces numbers that answer neither.

ApproachWhat it measuresUse it whenWatch out for
Through Cloudflare, cache as configuredReal user experience including cache hit ratioPre-launch check of the full path, cache rules validationMostly measures the CDN; origin may barely be touched
Through Cloudflare, uncacheable requestsOrigin capacity plus the edge path, TLS to origin, WAF overheadAPIs, logged-in pages, checkout flowsNeeds a WAF skip rule for generator IPs
Directly to origin, bypassing CloudflareOrigin capacity aloneCapacity planning, finding the app's kneeOrigin firewall may only allow Cloudflare IPs; no edge effects

For capacity planning, testing the origin is almost always what you want. Your origin will see the cache-miss traffic in production, and the question is how much of it it can handle. Testing the edge is useful once, to confirm the cache behaves the way you think, but Cloudflare's network will not be your bottleneck.

You might be testing the cache

Every proxied response carries a cf-cache-status header. Sample a few responses before the test, and log the header during it if your tool allows:

curl -sI https://www.example.com/ | grep -i cf-cache-status
curl -sI https://www.example.com/assets/app.js | grep -i cf-cache-status
  • HIT: served from cache. The origin was not involved.
  • MISS / EXPIRED: fetched from origin this time, will be cached.
  • DYNAMIC: not eligible for caching, always goes to origin. By default Cloudflare caches static file extensions but not HTML, so normal HTML pages and API responses usually show this.
  • BYPASS: a rule or origin header (for example Cache-Control: private or a cookie) told Cloudflare not to cache.

If your test targets cached assets, you are measuring Cloudflare. Cross-check with origin metrics: if the generator reports 2,000 RPS and the origin access log shows 40, the cache absorbed 98% of the load.

Cache-busting, carefully

Appending a random query string (?cb=8f3a1) forces a miss under Cloudflare's default cache key, which includes the query string. It works, but use it deliberately:

  • It models a 0% hit ratio, which is harsher than production. Fine for a stress test, misleading if reported as "expected peak performance".
  • It fills the edge cache with entries nobody will request again.
  • If your zone ignores or sorts query strings in the cache key, it does nothing; check cf-cache-status shows MISS.

A more realistic approach is to mix: a weighted scenario where most requests hit cacheable URLs and a share (matching your production hit ratio from Cloudflare analytics) are uncacheable or busted.

Allowlist the load generator in the WAF

To test through the edge without being challenged or rate limited, create a WAF custom rule that matches the generator's source IPs and uses the Skip action. LoadTester publishes its worker IPs at https://app.loadtester.org/api/ips; other tools and cloud generators have their own published ranges, or you use the egress IPs of your own machines.

  1. Create an account-level IP list (under Lists in your Cloudflare account settings) containing the generator IPs, so the rule stays readable and you can update addresses in one place. Or put the addresses inline.
  2. In the zone's WAF custom rules, create a rule with an expression like:
    (ip.src in $loadtest_generators and http.host eq "www.example.com")
    
    # or inline
    (ip.src in {192.0.2.10 192.0.2.11 198.51.100.0/24})
  3. Action: Skip. Select the components to skip: remaining custom rules, rate limiting rules, managed rules, and Super Bot Fight Mode, plus other listed features such as Browser Integrity Check and Security Level if they are firing.
  4. Place the rule first in the custom rules order, and turn on logging for it so you can see it matching in Security Events.

Things the skip rule does not cover:

  • Bot Fight Mode (the free-plan feature) cannot be skipped with WAF custom rules. If it is on, turn it off for the duration of the test, or test the origin directly. Super Bot Fight Mode on paid plans can be skipped.
  • HTTP DDoS protection runs separately from the WAF. A very high request rate from a few IPs can still trigger it. Check the DDoS section of Security Events if requests are mitigated after the skip rule is in place.
  • Under Attack mode challenges everyone; disable it for the test window.

Treat the rule as temporary. A broad skip for a set of IPs is an open door for anything else that uses those addresses. Scope it by hostname, remove or disable it after the test, and never use it for shared cloud IP ranges you do not control. Domain verification on the LoadTester side means only domains you have proven you control can be targeted from those workers, but your skip rule is still yours to keep tight.

Test the origin directly

To bypass the edge, point the generator at the origin while keeping the real hostname for the Host header and TLS SNI:

# quick check from a shell: resolve the hostname to the origin IP
curl -sv --resolve www.example.com:443:203.0.113.20 https://www.example.com/health

# or use a separate DNS-only (grey-cloud) hostname that points at the origin,
# e.g. origin-test.example.com, with a certificate valid for it

Common blockers and what to do about them:

  • Origin firewall only allows Cloudflare IP ranges (a recommended setup). Add the generator IPs to the origin firewall or security group for the test window.
  • Authenticated Origin Pulls (mTLS between Cloudflare and origin). Direct requests will fail the client-certificate check. Test against a separate origin listener without the requirement, or test through the edge.
  • Cloudflare Tunnel. The origin has no public listener, so the only path is through the edge. Use the allowlist approach above.
  • Origin certificate is a Cloudflare Origin CA certificate, which public clients do not trust. Use a hostname with a publicly trusted certificate, or disable verification in the tool only for this test.

When testing the origin directly, remember that it no longer sees Cloudflare's headers (CF-Connecting-IP), so logic that relies on them (rate limits by client IP, geolocation) behaves differently.

Cloudflare 520-527 errors explained

When requests do get through, the 52x codes are the most useful signals, because they describe what happened between Cloudflare and your origin:

CodeNameMeaning under load
520Web server returned an unknown errorOrigin closed the connection or sent an empty/invalid response. Same root causes as a 502 under load: crashes, keep-alive races, oversized headers.
521Web server is downOrigin refused the TCP connection: process down, or the origin firewall blocking Cloudflare.
522Connection timed outTCP handshake to origin did not complete. Overloaded origin, full SYN/accept backlog, or packets dropped by a firewall.
523Origin is unreachableRouting or DNS problem to the origin. Rarely load-related.
524A timeout occurredConnected, but no HTTP response within 100 s (default proxy read timeout). Slow requests; see 504 gateway timeout under load.
525SSL handshake failedTLS handshake with origin failed. Under load, often an origin running out of TLS workers or connections.
526Invalid SSL certificateOrigin certificate invalid in Full (strict) mode. Configuration, not load.
527Railgun errorRailgun connection failure. Railgun has been discontinued, so you should not see this on a current setup.

A 504 seen through Cloudflare was generated by something behind it (your ALB or nginx), not by Cloudflare.

AWS WAF, CloudFront and Akamai

The same two problems (blocked traffic and cached responses) apply to every CDN and WAF. The specifics differ; the approach does not.

AWS WAF and CloudFront

  • Blocking: rate-based rules and the Bot Control managed rule group are the usual triggers, returning 403 by default. Add an IP set with the generator addresses and a rule with the Allow action at a higher priority (lower number) than the rate-based and managed rules. Allow is a terminating action, so later rules are not evaluated for those requests.
  • Caching: CloudFront reports cache results in the X-Cache header (Hit from cloudfront, Miss from cloudfront). The same edge-vs-origin decision applies; to test origin capacity, target the ALB or origin directly.
  • Policy: AWS publishes policies covering penetration testing and simulated DDoS events. A normal load test of your own application is not a DDoS simulation, but read the current policy before very large tests through CloudFront.

Akamai

Akamai security configurations include rate controls and bot management that will flag a load generator the same way. Add the generator IPs to an allowlist (Akamai manages IP allowlists as network lists) and exempt it from rate and bot controls in the security policy, or coordinate with whoever manages your Akamai configuration. For caching, check the cache-status debug headers your configuration exposes, or confirm against origin request counts.

Notify people and check the terms

  • Tell your team and your provider's account contacts if the test is large. A sudden traffic spike can page the on-call engineer and trigger automated mitigation.
  • Read your CDN plan's terms. Testing your own zone is normal, but providers expect large tests to be coordinated, and enterprise contracts often have a process for it.
  • Test only what you own. Never load test a third-party domain behind a CDN, even if it is a dependency of your site. LoadTester enforces this through domain verification (a DNS TXT record or a file at /.well-known/loadtester-verify.txt).
  • Schedule it. Run large edge tests outside peak hours so real users are not caught by temporary rule changes. The production load testing checklist covers the rest.

Diagnosis table

Symptom / signalLikely causeFix
403 with cf-mitigated: challengeBot protection or Security Level challengeWAF Skip rule for generator IPs; turn off Bot Fight Mode for the test
403, error 1020WAF custom or managed rule blockSkip rule; check Security Events for the rule ID
429, error 1015Rate limiting ruleSkip rate limiting rules for generator IPs
Still mitigated after skip ruleBot Fight Mode or HTTP DDoS protectionDisable Bot Fight Mode; check DDoS events; lower the rate or test origin
Latency tiny, origin idle, cf-cache-status: HITTesting the cacheTest uncacheable paths, cache-bust a share, or go direct to origin
520 under loadOrigin closing connections / crashingDebug as a 502
521 / 522 under loadOrigin refusing or not accepting connectionsOrigin capacity, backlog, firewall
524Origin response took > 100 sDebug as a 504; move long work async
Direct-to-origin test gets TLS errorsOrigin CA cert or Authenticated Origin PullsUse a publicly trusted hostname or a test listener

Re-test and confirm the setup

Before the real run, do a short validation run at low load (for example 20 RPS for 1 minute) and check three things:

  1. No mitigation: zero 403/429 responses, and Security Events shows your skip rule matching the generator IPs (or no events at all for a direct-to-origin test).
  2. Correct cache behavior: cf-cache-status values match your intent, and the origin access log shows the request rate you expect it to receive.
  3. Origin metrics move: origin CPU and request count rise with the test. If they do not, you are not testing what you think.

Then run the actual test as a step-ramp in fixed-RPS mode, and watch the 52x codes alongside p95 and p99. A rising 520/522 rate is your origin's capacity limit showing through the edge, which is exactly the finding the test was meant to produce. For how to set the steps and interpret the curve, see why latency increases under load and how to read load test results. Finally, remove or disable the skip rule and restore Bot Fight Mode or any other settings you changed.

FAQ

Will Cloudflare block my load test?

Often, yes. A high request rate from a few IP addresses matches the patterns that Bot Fight Mode, rate limiting rules and managed WAF rules are designed to stop, so you get 403 challenges or 429 responses. Add a WAF custom rule with the Skip action for the generator IPs, or test the origin directly.

Can I skip Bot Fight Mode with a WAF custom rule?

No. The free-plan Bot Fight Mode cannot be bypassed with the Skip action, so it has to be turned off for the test window or the origin tested directly. Super Bot Fight Mode on paid plans can be skipped with a custom rule.

Should I load test through Cloudflare or against the origin?

For capacity planning, test the origin, because that is the part of the system that can run out of capacity. Testing through the edge is useful to validate cache rules and the full request path, but with a high cache hit ratio it mostly measures Cloudflare.

How do I know if my load test is hitting Cloudflare's cache?

Check the cf-cache-status response header. HIT means the response came from cache without touching the origin; DYNAMIC and BYPASS mean the request went to the origin. Comparing the generator's request rate with the origin access log shows how much the cache absorbed.

What does a Cloudflare 522 mean during a load test?

Cloudflare could not complete a TCP connection to your origin in time. Under load this usually means the origin is overloaded, its listen backlog is full, or a firewall is dropping connections. It is a real origin capacity finding, not a test artifact.

Run this as a real load testFree plan: 10 VUs, 50 RPS, live p95/p99 and error charts, no credit card. Starter $19/mo (250 VUs) or Pro $49/mo (2,000 VUs) with a 7-day free trial when you need more.
Start freeCompare plans