Skip to content
Control Plane Labs

522 Connection Timed Out

Server error response defined by developers.cloudflare.com.

Last updated July 27, 2026

Common causes at a glance

  • A firewall DROP rule silently discarding traffic from Cloudflare IP ranges
  • An overloaded origin whose listen backlog is full and dropping new SYNs
  • A DNS A record pointing at an IP the origin no longer owns
  • TCP keepalives disabled at the origin, so idle pooled connections die unnoticed

Reproduce it

curl -sS --connect-timeout 20 --resolve www.example.com:443:203.0.113.10 https://www.example.com/

What 522 Connection Timed Out means

Cloudflare documents two distinct timeouts behind this one code. Before a connection is established, the origin has 19 seconds to return a SYN+ACK, across a retry backoff of 1, 1, 1, 1, 1, 2, 4, 8 seconds. After a connection exists, it has 90 seconds to acknowledge the resource request. Both windows are generous, so hitting either means something is badly wrong rather than merely slow.

The word to hold onto is silence. A DROP rule, a full accept queue, a security group that never got the Cloudflare ranges, or a stale A record all produce the same symptom: no answer. Compare 504, where a connection exists and the response is late.

There is no RFC

522 is unregistered. It is absent from the IANA HTTP Status Code Registry and defined solely by Cloudflare’s troubleshooting docs. Any client following RFC 9110 §15 treats an unrecognised 5xx as 500, so dashboards that bucket by class fold 522 into generic server errors and hide the signal you need. The more expensive consequence is diagnostic: the edge generates this page after its handshake attempt expires, so no request reached your application. Nothing appears in your access log, and that absence is confirmation rather than a dead end.

What actually causes it

Cloudflare names the blocked-IP case as the single most common cause, and it recurs because the block is usually invisible to whoever set it. A DROP rule produces no rejection, no client-side log entry, and no symptom for traffic from anywhere else. Rate-limiting rules are worse: they pass normal traffic and only start dropping once a data centre’s connection volume crosses a threshold, so the 522 appears intermittently and correlates with load rather than configuration. The stale A record is its own trap, since the hostname resolves fine and points at a machine that belongs to somebody else now.

How to debug it

The signature of 522 is a hang. Time the handshake at the origin IP and watch it run out rather than fail fast:

curl -sv --connect-timeout 20 --resolve www.example.com:443:203.0.113.10 \
https://www.example.com/ 2>&1 | grep -E 'Trying|Connected|timed out'

If that hangs from your laptop too, the block is not Cloudflare-specific and it is an ordinary reachability problem. If your laptop connects fine and only Cloudflare times out, the allowlist is the prime suspect: compare your rules against the published Cloudflare ranges rather than the copy in your runbook. Check ss -ltn for a saturated Recv-Q on the listening socket too.

Working examples

curl

# 522 hangs; 521 fails immediately. Timing is the discriminator.
curl -sv --connect-timeout 20 \
  --resolve www.example.com:443:203.0.113.10 \
  https://www.example.com/ 2>&1 | grep -E 'Trying|Connected|timed out'

# Where the handshake time actually goes.
curl -s -o /dev/null \
  -w 'dns=%{time_namelookup} connect=%{time_connect} total=%{time_total}\n' \
  https://www.example.com/

Python (requests)

import socket, time

# Time a raw TCP handshake to the origin. A full timeout is the 522 signature;
# ConnectionRefusedError instead would mean 521.
start = time.monotonic()
try:
    socket.create_connection(("203.0.113.10", 443), timeout=20)
    print("handshake ok in", round(time.monotonic() - start, 2), "s")
except socket.timeout:
    print("silent drop after", round(time.monotonic() - start, 2), "s -> 522")
except ConnectionRefusedError:
    print("refused -> this is a 521, not a 522")

import requests
print(requests.get("https://www.example.com/", timeout=30).status_code)

Node (fetch)

// The edge answers with 522 well before your own client timeout fires.
const started = Date.now();
const res = await fetch("https://www.example.com/");
console.log(res.status, Date.now() - started, "ms");

// Classify it: timeout means 522, ECONNREFUSED means 521.
import net from "node:net";

const sock = net.connect({ host: "203.0.113.10", port: 443 });
sock.setTimeout(20_000);
sock.on("connect", () => { console.log("origin reachable"); sock.destroy(); });
sock.on("timeout", () => { console.log("SYN dropped -> 522"); sock.destroy(); });
sock.on("error", (e) => console.log(e.code));

Reference and tooling

The authoritative list of assigned status codes is theIANA HTTP Status Code Registry. For a searchable copy with the semantics summarised, use theHTTP status code reference on this site. To see exactly what a server is sending, theheader inspector shows the raw status line and response headers, and thecurl builder assembles the request flags for you.

Other 5xx codes