A Cloudflare 52x error means the visitor reached Cloudflare and the failure happened behind it, on the way to your origin. Each code points at one hop of that trip, so the fastest diagnosis is to name the hop, then test the origin with the edge out of the way. Redirect loops are the odd one out: every hop works, but the edge and the origin disagree about which scheme the visitor used.

Which hop each error points to

After the visitor has connected to Cloudflare, a proxied request makes up to three more hops: a TCP connection to the origin, a TLS handshake with the origin (only when Cloudflare pulls over HTTPS), and the HTTP exchange itself. Cloudflare’s documentation for each code, checked on 26 September 2026, maps onto those hops like this:

Code What Cloudflare saw Failed hop Check first
521 The origin refused the connection TCP Web server running? Listening on 80 for Flexible, 443 for Full and Full (strict)? Firewall rejecting Cloudflare ranges?
522 No SYN+ACK within 19 seconds, or no ACK of the request within 90 seconds TCP Firewall silently dropping Cloudflare, stale origin IP in DNS, overloaded host, or a Pages custom domain that was never set up
523 No route to the origin IP Network path The A/AAAA record’s address; on AWS, a VPC route such as 172.0.0.0/8 that captures Cloudflare’s 172.64.0.0/13
525 TLS handshake with the origin failed TLS Certificate installed on 443, SNI support, overlapping cipher suites
526 Origin certificate failed validation (Full (strict) only) TLS validation Expiry, revocation, name in CN/SAN, trusted issuer or Origin CA, complete chain
520 Empty, unknown, or unexpected response HTTP Crashes and resets, response headers over 128 KB, broken HTTP/2 to origin, Authenticated Origin Pulls mismatch
524 Connected, but no HTTP response within the 125-second Proxy Read Timeout HTTP Slow endpoints; only Enterprise zones can raise the limit, up to 6,000 seconds
530 Could not resolve the origin hostname; the body carries a 1xxx code Origin DNS The 1xxx code in the response body
ERR_TOO_MANY_REDIRECTS The browser stopped following redirects None: a policy conflict Encryption mode against the origin’s redirect rules

Two readings save time before you run anything. A 521 is a refusal, so it tends to come back quickly; a 522 only appears after Cloudflare has waited out the handshake timeout. And a 520 means the origin said something Cloudflare could not use, while a 524 means it said nothing in time.

If the browser shows its own error page rather than a Cloudflare-branded one, the edge never produced a usable response. Look at DNS, the edge certificate, or a redirect loop instead of the origin.

What a healthy proxied response looks like

Live capture, 26 September 2026. Our own site, requested from our test workstation in South Korea with curl 8.7.1 (some headers trimmed):

$ curl -sI https://and.guide/
HTTP/2 200 
date: Sat, 26 Sep 2026 14:40:24 GMT
content-type: text/html; charset=utf-8
...
server: cloudflare
cf-ray: a413060aacb7ea14-ICN
alt-svc: h3=":443"; ma=86400

and.guide runs on Cloudflare Pages, so there is no separate origin server behind this edge; the capture shows the visitor side of a working request. server: cloudflare and cf-ray confirm the response came through Cloudflare. The cf-ray value is the request’s Ray ID, and its suffix names the data center that handled it.

Live capture, 26 September 2026. Cloudflare’s trace endpoint on the same hostname (our client address and three flag lines trimmed):

$ curl -s https://and.guide/cdn-cgi/trace
fl=523f191
h=and.guide
...
ts=1790433626.000
visit_scheme=https
uag=curl/8.7.1
colo=ICN
sliver=050-tier1
http=http/2
loc=KR
tls=TLSv1.3
sni=plaintext
...
kex=X25519

Cloudflare’s troubleshooting guide points to the colo field of /cdn-cgi/trace as the data center serving you. Here it says ICN (Incheon), matching the -ICN suffix on the Ray ID above.

Do not assume every request lands in the same place.

Live capture, 26 September 2026. Plain-HTTP requests to our apex and to www, seconds apart:

$ curl -s -o /dev/null -D - -w 'ip=%{remote_ip} code=%{http_code}\n' http://and.guide/ | grep -iE '^(HTTP|location|cf-ray|ip=)'
HTTP/1.1 301 Moved Permanently
Location: https://and.guide/
CF-RAY: a4130660c85aea95-ICN
ip=172.66.47.12 code=301
$ curl -s -o /dev/null -D - -w 'ip=%{remote_ip} code=%{http_code}\n' http://www.and.guide/ | grep -iE '^(HTTP|location|cf-ray|ip=)'
HTTP/1.1 301 Moved Permanently
Location: https://and.guide/
CF-RAY: a41306613fb1849d-HKG
ip=172.67.138.236 code=301

The apex was answered from 172.66.47.12 in Incheon; www, which only redirects, was answered from 172.67.138.236 in Hong Kong. Both addresses sit inside Cloudflare’s published 172.64.0.0/13 range, yet the two hostnames were served by different data centers from the same workstation. When you report a failure, quote the Ray ID of the failing response itself, not one from a later retry or a different hostname.

Test the origin with the edge out of the way

The goal is to send the origin the same request Cloudflare would: same hostname in the Host header, same SNI, same port. Pin the name to the origin address instead of editing DNS. curl’s --resolve supplies an address for a host and port pair, and --connect-to redirects only the network connection while leaving SNI and certificate verification on the original hostname. Cloudflare’s own troubleshooting guide uses the second form.

With the origin at 203.0.113.10:

# 521, 522, 523: does anything answer on the port your encryption mode uses?
nc -vz -w 5 203.0.113.10 443
nc -vz -w 5 203.0.113.10 80

# 525, 526, 520: HTTPS as Full or Full (strict) would request it
curl -sv -o /dev/null --resolve www.example.com:443:203.0.113.10 https://www.example.com/

# Redirect loops: plain HTTP on port 80, as Flexible mode requests it
curl -sI --resolve www.example.com:80:203.0.113.10 http://www.example.com/

# 526: certificate names, issuer, and a hostname check
openssl s_client -connect 203.0.113.10:443 -servername www.example.com \
  -verify_hostname www.example.com </dev/null 2>/dev/null \
  | grep -E 'subject=|issuer=|Verify return code'

# 524: time to first byte against the 125-second limit
curl -s -o /dev/null --resolve www.example.com:443:203.0.113.10 \
  -w 'connect=%{time_connect}s ttfb=%{time_starttransfer}s\n' \
  https://www.example.com/slow-report

# 520: size of the response headers against the 128 KB limit
curl -s -o /dev/null -D - --resolve www.example.com:443:203.0.113.10 \
  https://www.example.com/ | wc -c

Read the results with three caveats:

  • Your firewall may treat you differently. If the origin only accepts Cloudflare’s ranges (listed at https://www.cloudflare.com/ips/), a direct test from your laptop will time out even when Cloudflare connects fine. Run it from an allowed host.
  • Origin CA certificates fail public verification by design. Only Cloudflare trusts them. Pass Cloudflare’s published Origin CA root to curl with --cacert (RSA and ECC versions are linked from the Origin CA docs) to check what Full (strict) will see.
  • Same failure directly means an origin problem. If the direct test works, compare what differs from Cloudflare’s request: source address, port for your mode, or SNI.

Cloudflare’s 520 page also suggests switching the record to DNS-only as a quick workaround. That publishes your origin address and removes the edge’s protection, so prefer pinned curl tests; the Cloudflare DNS setup guide covers what the proxy toggle changes. On Cloudflare Pages there is no origin to test; Cloudflare lists a missing custom-domain setup or an incorrect CNAME as causes of 522s there, and the Pages custom domain guide walks through both.

For deeper certificate checks behind a 525 or 526, the command-line TLS inspection guide goes through chains and SANs step by step.

Encryption modes decide the port, the scheme, and the checks

The encryption mode decides which port Cloudflare connects to and what it checks, so a mode that does not match the origin shows up as a 521, 525, or 526, or as a redirect loop.

Mode Cloudflare to origin Origin certificate Typical failure
Off Plain HTTP; visitors get no HTTPS Not used Redirect loop if HSTS is enabled
Flexible Always plain HTTP on port 80 Not required 521 if nothing listens on 80; loop if the origin forces HTTPS
Full Same scheme as the visitor Any certificate, even expired or self-signed 525 if port 443 has no working TLS
Full (strict) Same scheme as the visitor Unexpired, public CA or Origin CA, matching CN/SAN 526 on any validation failure
Strict (SSL-Only Origin Pull) Always HTTPS, validated As Full (strict) Enterprise zones only

Two details from the mode pages matter in practice. Flexible only applies to HTTPS on port 443; HTTPS on other ports falls back to Full. And Cloudflare now lists Automatic SSL/TLS as the default setting, though it is still rolling out and some older zones only have the custom choice. It fetches your origin over HTTP and HTTPS with a Cloudflare-SSLDetector user agent, compares the content, and upgrades the mode starting at 1 percent of traffic, then in 10 percent steps. It never downgrades, so an origin certificate that expires under Full (strict) still produces 526 errors.

Reading a redirect chain with curl

Browsers do not detect loops; they count. The Fetch standard returns a network error once a request’s redirect count reaches 20, and curl follows up to 50 by default before exiting with code 47. Lower the limit so failures come back quickly, and print the counters:

  • %{num_redirects} is the number of redirects curl followed.
  • %{url_effective} is, in curl’s words, “the URL that was fetched last”. When the limit stops curl, that is not a final destination.

Live capture, 26 September 2026. httpbin.org’s /redirect/10 route issues ten redirects; we allowed five:

$ curl -sSIL --max-redirs 5 -w 'redirects=%{num_redirects} last=%{url_effective}\n' https://httpbin.org/redirect/10 2>&1 | grep -iE '^(HTTP|location|redirects=|curl:)'
curl: (47) Maximum (5) redirects followed
HTTP/2 302 
location: /relative-redirect/9
HTTP/2 302 
location: /relative-redirect/8
HTTP/2 302 
location: /relative-redirect/7
HTTP/2 302 
location: /relative-redirect/6
HTTP/2 302 
location: /relative-redirect/5
HTTP/2 302 
location: /relative-redirect/4
redirects=5 last=https://httpbin.org/relative-redirect/5

curl received six redirect responses, followed five of them, and stopped at the sixth. The error line prints first only because stderr is unbuffered. Every location is different, so this is a chain that is too long, not a loop.

A genuine loop looks different. We rebuilt the Flexible-mode loop on 127.0.0.1 with two small Python standard-library servers. The “edge” terminates TLS for shop.test on port 8443 and always fetches from the origin over plain HTTP on port 8080. The origin redirects every request to https://<Host><path>, as a typical force-HTTPS rule does:

# Excerpt from the lab origin
def do_GET(self):
    if TRUST_XFP and self.headers.get("X-Forwarded-Proto") == "https":
        self.send_response(200)
        ...
        return
    # This server only ever receives plain HTTP, so it redirects every request.
    self.send_response(301)
    self.send_header("Location", f"https://{self.headers['Host']}{self.path}")

Live capture, 26 September 2026. The loop, with --connect-to sending shop.test:443 to the local edge (four identical responses trimmed):

$ curl -skSIL --max-redirs 5 --connect-to shop.test:443:127.0.0.1:8443 -w 'redirects=%{num_redirects} last=%{url_effective}\n' https://shop.test/ 2>&1
curl: (47) Maximum (5) redirects followed
HTTP/1.0 301 Moved Permanently
Server: BaseHTTP/0.6 Python/3.13.2
Date: Sat, 26 Sep 2026 14:41:56 GMT
Location: https://shop.test/
Content-Length: 0

HTTP/1.0 301 Moved Permanently
Server: BaseHTTP/0.6 Python/3.13.2
Date: Sat, 26 Sep 2026 14:41:56 GMT
Location: https://shop.test/
Content-Length: 0

...

redirects=5 last=https://shop.test/

That is the loop signature: every Location is the URL that was just requested, and url_effective equals the starting URL. The -k flag is only there because the lab edge uses a throwaway self-signed certificate.

Why Flexible mode loops, and three ways out

The mechanism takes four steps:

  1. The visitor requests https://www.example.com/.
  2. In Flexible mode, Cloudflare fetches http://www.example.com/ from the origin on port 80.
  3. The origin sees plain HTTP and answers 301 Location: https://www.example.com/.
  4. Cloudflare passes the 301 to the browser, which requests the same HTTPS URL and returns to step 2 until it gives up.

Live capture, 26 September 2026. The same lab after starting the origin with TRUST_XFP=1, so it stops redirecting when the edge reports an HTTPS visitor, followed by a direct request to the origin without that header:

$ curl -skSIL --max-redirs 5 --connect-to shop.test:443:127.0.0.1:8443 -w 'redirects=%{num_redirects} last=%{url_effective}\n' https://shop.test/ 2>&1
HTTP/1.0 200 OK
Server: BaseHTTP/0.6 Python/3.13.2
Date: Sat, 26 Sep 2026 14:42:06 GMT
Content-Length: 0

redirects=0 last=https://shop.test/
$ curl -sSI http://127.0.0.1:8080/ -H 'Host: shop.test'   # direct to origin, no X-Forwarded-Proto
HTTP/1.0 301 Moved Permanently
Server: BaseHTTP/0.6 Python/3.13.2
Date: Sat, 26 Sep 2026 14:42:06 GMT
Location: https://shop.test/
Content-Length: 0

The second command is the request Flexible mode makes, and its answer is the whole loop. In production, the equivalent direct test is the port 80 curl -sI --resolve command from the origin checklist above.

Cloudflare’s redirect-loop page gives the first two fixes; the third is a stopgap. In order of preference:

  1. Encrypt the origin hop. Install a certificate from a public CA or a free Cloudflare Origin CA certificate, then switch to Full (strict). Cloudflare then connects over HTTPS for HTTPS visitors, and the origin’s redirect no longer fires.
  2. Move the redirect to the edge. Remove the origin’s HTTPS redirect and enable Always Use HTTPS, which redirects every http request to https for all hosts in the zone.
  3. Stopgap: make the origin redirect scheme-aware. Cloudflare sets X-Forwarded-Proto to the protocol the visitor used (and sends CF-Visitor: {"scheme":"https"}), so the origin can skip the redirect for HTTPS visitors. Trust these headers only if the origin accepts connections from Cloudflare alone, and remember the origin hop stays unencrypted.
# Stopgap for Flexible mode: redirect only visitors who really used HTTP
if ($http_x_forwarded_proto = "http") {
    return 301 https://$host$request_uri;
}

The same page lists the other loop patterns: an origin that redirects HTTPS to HTTP under Full or Full (strict), or with Always Use HTTPS enabled; HSTS combined with mode Off; and Redirect Rules or Page Rules that contradict each other. One quick filter helps: an https:// URL answered with a redirect to that same https:// URL cannot come from Always Use HTTPS, which only upgrades http requests. Look at the origin or at your redirect rules. Canonical-host redirects add their own hops; the www and apex redirect guide shows how to keep them to one.

Collecting evidence before you escalate

Cloudflare also sends the Ray ID to your origin as a Cf-Ray request header, and its header documentation recommends logging it so you can match proxied requests to your server logs. One caveat from the same page: with Argo Smart Routing or Tiered Caching, the three-letter code in the copy your origin receives names the data center that connected to the origin, not the one the visitor reached.

For nginx:

log_format cfray '$remote_addr [$time_local] "$request" $status '
                 'rt=$request_time ray=$http_cf_ray';
access_log /var/log/nginx/access.log cfray;

On the Cloudflare side, any plan can search sampled Security Events by Ray ID, Log Explorer can query it, and Enterprise zones can add it to Cloudflare Logs. Cloudflare notes that Ray IDs are not guaranteed to be unique, so pair each one with a timestamp.

When you open a ticket or ask your host for help, bring:

  • the exact URL and the time, with its time zone
  • the cf-ray value from the failing response, including the data center suffix
  • curl -sv output through Cloudflare and the matching output from the pinned origin test
  • origin error-log lines from the same minute, found by Ray ID where possible
  • for a 520, the two HAR files, with and without Cloudflare, that Cloudflare’s 520 documentation asks for

With the hop named and a direct test in hand, you know whether the fix belongs to the origin (a port, a firewall rule, a certificate, a redirect) or to a Cloudflare setting such as the encryption mode.