Cloudflare Error 520: Web server is returning an unknown error
Cloudflare reached your origin and something came back — but the reply was empty, truncated, or so broken the edge could not treat it as HTTP. Cousin of a generic 502, with Cloudflare’s branding.
What visitors see
Error 520
Web server is returning an unknown error
The web server reported a gateway-level error or sent a response Cloudflare could not understand.
Cloudflare Ray ID: …
Only orange-cloud traffic sees this page. Grey-cloud or a direct hit to the origin IP skips Cloudflare’s branded error — which is exactly how you diagnose whether the origin itself is sending garbage.
520 vs 502 vs 521 vs 522 vs 524 vs 525/526
| Code | What Cloudflare managed | Typical cause |
|---|---|---|
| 520 | Connected; reply empty / invalid / unexpected | Crash mid-response, empty body, protocol mismatch, oversized headers |
| 502 | Proxy got a bad upstream HTTP reply | Same family as 520; nginx/ALB often say 502 for the same root causes |
| 521 | TCP to origin refused | Nothing listening, firewall REJECT |
| 522 | TCP to origin timed out | Firewall DROP, wrong IP, host unreachable |
| 524 | Connected; HTTP response too slow | App hung / slow DB — cousin of 504 |
| 525 / 526 | TLS handshake or cert validation failed | Origin not speaking TLS, cipher mismatch, expired/self-signed cert under Full (strict) |
| 503 | Origin (or CF) said unavailable | Overload, maintenance, drained pool |
Rule of thumb: 520 ≈ “I got something, but it was not usable HTTP.” Fix the origin’s response shape — do not open more firewall ports (that is 521/522 territory) and do not raise read timeouts (that is 524).
Frequent real causes
- Origin crash mid-response — app segfaults, OOM-kills (exit 137), or the worker dies after headers but before the body finishes. Cloudflare sees a truncated or empty reply.
- Empty body with an odd status — origin closes after writing nothing, or returns a status line Cloudflare does not accept as a complete response.
- HTTP/1.1 vs HTTP/2 / protocol mismatch — origin or an intermediate proxy speaks a dialect the edge did not expect (wrong ALPN, broken HTTP/2 SETTINGS, speaking plain HTTP on a Full-mode HTTPS port can also surface nearby as 525).
- Upstream closes early — nginx → app pool: the app ECONNRESETs; nginx may pass a broken reply upstream to Cloudflare.
- Headers too large — giant
Set-Cookiestacks or custom headers blow past Cloudflare’s header limits; related to HTTP 431 on other stacks. - App writing garbage — raw binary where HTTP should be, double status lines, missing
\r\n\r\n, or a reverse proxy buffering a partial frame. - Keep-alive / Content-Length lies — declared length does not match bytes sent; connection reused incorrectly; intermittent 520s that vanish on retry.
Diagnose from outside and on the origin
# Bypass Cloudflare: grey-cloud the record, or hit the origin IP with the real Host:
curl -svo /tmp/origin.out --connect-timeout 5 \
https://ORIGIN_IP/ -H "Host: example.com" -k
# Inspect what actually came back:
head -c 400 /tmp/origin.out; echo
curl -sI https://ORIGIN_IP/ -H "Host: example.com" -k
# Compare orange-cloud vs direct:
curl -sI https://example.com/ # through Cloudflare
curl -sI https://ORIGIN_IP/ -H "Host: example.com" -k
# On the origin around the Ray ID timestamp:
sudo journalctl -u nginx -u your-app -n 120 --no-pager
sudo tail -n 80 /var/log/nginx/error.log /var/log/nginx/access.log
# Look for upstream prematurely closed, segfaults, OOM, header-too-large
If direct-to-origin already returns an empty body, a truncated chunked stream, or non-HTTP bytes, Cloudflare 520 is expected — fix the origin before touching Cloudflare settings. Empty or garbage JSON bodies often show up client-side as Unexpected end of JSON input.
Fix checklist
- Confirm 520 vs neighbours — refused → 521; connect timeout → 522; slow but valid → 524; TLS fail → 525/526.
- Reproduce without Cloudflare — grey-cloud or
curlorigin IP + Host; capture the raw reply. - Read origin error logs — crash, OOM, upstream reset, “header too large”, protocol errors at the Ray ID time.
- Stabilize the app — stop crash loops; add request timeouts; ensure every path returns a complete status + headers + body (or a deliberate close).
- Shrink headers — fewer/smaller cookies; drop debug headers; raise origin limits only after you know the size is legitimate.
- Align HTTP version / proxying — if you forced HTTP/2 to origin, try HTTP/1.1; fix ALPN; ensure the process on 443 really speaks TLS+HTTP.
- After replies look sane — purge Cloudflare cache if a bad edge page stuck; watch for residual 502 from other proxies in the chain.
Related
All Cloudflare error codes · Cloudflare Error 523 · 502 Bad Gateway · Cloudflare Error 521 · Cloudflare Error 522 · Cloudflare Error 524 · Cloudflare Error 525 · Cloudflare Error 526 · HTTP 503 · HTTP 504 · Unexpected end of JSON input · ECONNRESET · Anvil HTTP status · Wirebook: when sites get tired
Questions
What does Cloudflare Error 520 mean?
Cloudflare connected to your origin but received an empty, invalid, or unexpected response it could not treat as normal HTTP — often a crash mid-response or a protocol mismatch.
How is 520 different from 502?
Same family. 502 is the generic gateway status; 520 is Cloudflare’s branded page when the origin reply is empty or unusable.
How is 520 different from 521 or 522?
521 never got a TCP accept (refused). 522 never finished connecting (timeout). 520 connected and got a bad reply.
How is 520 different from 524?
524 means the origin was too slow. 520 means the reply arrived (or partially arrived) but was empty or invalid — not merely late.
Why does it work on the origin IP but 520 through Cloudflare?
Cloudflare is stricter about complete, well-formed responses and header size. Grey-cloud can hide intermittent truncations that the edge rejects.
Can oversized headers cause 520?
Yes. Giant Cookie / Set-Cookie / custom header sets can make Cloudflare reject the origin reply. Trim headers and compare with curl -sI.
What should I check first on the server?
Reproduce with curl to the origin IP + Host header, then read nginx/app error logs around the Ray ID for crashes, upstream resets, and header-too-large messages.
Published .