What 522 means
With Cloudflare proxying your site, a request has two legs: the visitor to Cloudflare, and Cloudflare to your server (the origin). Error 522 is a failure on the second leg, at the very first step. Cloudflare sent a TCP SYN to your origin’s IP and port and never got a SYN-ACK back. After waiting, it gave up and showed the visitor the 522 page: browser working, Cloudflare working, host error.
The important word is timed out. Nothing refused the connection. The packets went somewhere and silence came back. Cloudflare documents the wait as roughly 19 seconds, which matches what visitors see: a long spinner, then the error.
Cloudflare also uses 522 for a rarer case: the connection opened, but the origin never acknowledged the request that Cloudflare sent over it (Cloudflare’s documentation gives about 90 seconds for this). The diagnosis below is almost always about the first case.
If you’re just visiting the site
There’s nothing on your side to fix. The failed connection is between Cloudflare’s servers and the site’s own server. Clearing your cache, switching browsers or turning off your VPN won’t change it. Try again later, or tell the site owner, with the time and the Ray ID printed at the bottom of the error page; that ID lets them find the exact failed request.
521, 522, 523, 524: how far the connection got
Cloudflare’s 52x codes describe the origin connection step by step. Picking the right one saves you from debugging the wrong layer.
| Code | What happened |
|---|---|
| 523 | Cloudflare couldn’t route to the origin IP at all |
| 521 | The origin refused the TCP connection (reset) |
| 522 | The TCP connection got no answer and timed out |
| 525 | TCP connected, the TLS handshake failed |
| 524 | Connected and request sent, but no HTTP response within Cloudflare’s limit (100 seconds by default) |
A 522 therefore tells you the problem is below HTTP and below TLS. Your nginx config, your application, your certificates: none of them got a chance to run. Look at IP addresses, ports, firewalls and the kernel’s ability to accept connections.
Step 1: find the IP Cloudflare is actually connecting to
Public DNS won’t tell you. For a proxied record, dig returns Cloudflare’s addresses:
dig +short www.example.com
# 104.21.x.x
# 172.67.x.x
The real origin is the value of the A (and AAAA) record in the Cloudflare dashboard under DNS → Records. Check it against the server’s actual public address. On the server:
curl -4 https://ifconfig.me ; echo
ip -brief addr
If they differ, you’ve probably found the cause (see “Wrong IP” below). Also note whether there is an AAAA record. If there is, Cloudflare may connect to the origin over IPv6, and an IPv6 address that isn’t reachable or has no listener gives you 522 even when IPv4 is fine.
Step 2: test the origin from outside
Connect to the origin IP directly, sending the right hostname, the way Cloudflare does:
curl -v --connect-timeout 20 -k \
--resolve www.example.com:443:203.0.113.10 https://www.example.com/ -o /dev/null
Three outcomes:
Connected to www.example.com (203.0.113.10) port 443: the origin accepts connections from you. If Cloudflare still gets 522, something treats Cloudflare’s addresses differently. Go to step 3.Failed to connect ... Connection refusedafter a few milliseconds: nothing listens on that port, or a firewall rejects. That’s the 521 family, and from Cloudflare’s side it should show as 521, not 522, unless the refusal is specific to your IP.Connection timed out after 20001 milliseconds: the origin drops everyone. Check the IP, the cloud firewall and the host firewall in that order.
nc -vz -w 10 203.0.113.10 443 gives the same answer without the HTTP part. Test port 80 too if your SSL mode is Flexible (see cause 3).
The port check does the same from outside your network: enter the origin IP, or 203.0.113.10:443 for a single port. It waits 3 seconds and reports both a refusal and a timeout as Closed, so use curl when you need to tell them apart. Don’t point it at the proxied hostname; that resolves to Cloudflare, and the tool can’t open TCP connections to Cloudflare’s own address space, so it reports the host as not checkable.
One caveat. If your origin deliberately admits only Cloudflare’s ranges, every outside test will time out, ours included. That’s correct behaviour, not the bug. In that case go straight to step 3.
Step 3: watch from the origin
This is the most useful test, because it shows where the packets stop. On the origin, capture SYN packets on the port while you reload the site through Cloudflare:
sudo tcpdump -ni any 'tcp port 443 and tcp[tcpflags] & tcp-syn != 0'
On Linux, tcpdump sees incoming packets before the firewall does, so you see them even if iptables then drops them.
IP 172.68.12.34.51544 > 203.0.113.10.443: Flags [S], seq 1849320, win 65535, length 0
IP 172.68.12.34.51544 > 203.0.113.10.443: Flags [S], seq 1849320, win 65535, length 0
IP 172.68.12.34.51544 > 203.0.113.10.443: Flags [S], seq 1849320, win 65535, length 0
The same SYN repeated with no Flags [S.] reply going the other way: the packets reach the server and the server stays silent. That’s a local firewall or a kernel that can’t accept the connection (causes 1 and 4).
No packets at all: they never reach the machine. Either the DNS record points elsewhere (cause 2), or a firewall in front of the server drops them: a cloud security group, a provider’s DDoS filter or a router (causes 1 and 6).
SYN in, SYN-ACK out, still 522: the reply isn’t making it back. Look for a second network interface or a VPN changing the return route, or an upstream filter that only blocks one direction.
The usual causes
1. A firewall drops Cloudflare’s addresses
The single most common cause. A rule with action DROP sends nothing back, so Cloudflare waits and times out. Typical sources:
- fail2ban, CrowdSec or a similar tool banning “abusive” IPs. Behind Cloudflare, every visitor’s request arrives from a Cloudflare address, so a rule that bans the IP making too many requests eventually bans a Cloudflare edge server. Visitors routed through that edge get 522; everyone else is fine. That’s why this version is often intermittent and regional.
- A host firewall in default-deny mode where someone allowed only an office IP, or allowed Cloudflare’s IPv4 ranges but not IPv6.
- A hosting provider’s own DDoS protection or rate limiting. All your traffic comes from a few thousand Cloudflare addresses, which can look like an attack.
Check the server’s firewall counters and ban lists:
sudo iptables -L INPUT -n -v --line-numbers | head -30
sudo nft list ruleset | less
sudo ufw status verbose
sudo fail2ban-client status nginx-limit-req # use your jail's name
Fix: allow Cloudflare’s published ranges (https://www.cloudflare.com/ips-v4 and https://www.cloudflare.com/ips-v6) on 80 and 443 at every layer, and exclude them from automatic bans. Then make your web server log the real visitor IP from the CF-Connecting-IP header (nginx’s real_ip_header CF-Connecting-IP with set_real_ip_from for each Cloudflare range), so fail2ban bans visitors instead of Cloudflare.
If the firewall belongs to your host and you can’t change it, send them the two range lists and ask for 80 and 443 to be allowed from them.
2. The DNS record points at the wrong IP
The origin moved and the record didn’t. Common after migrating to a new server, after a hosting provider renumbers, or on a home connection whose IP changes. Cloudflare sends SYNs to an address where nothing answers, or to a machine that has nothing to do with you.
The tcpdump test shows nothing arriving, and step 1 shows the mismatch. Fix the A/AAAA record in the Cloudflare dashboard. If the IP changes regularly, use a dynamic DNS updater for Cloudflare or switch to a tunnel (below). Remove an AAAA record you can’t keep working.
3. Cloudflare connects to a port you closed
Cloudflare connects to the origin on a port that depends on the SSL/TLS mode, not on what the visitor used. In Flexible mode, a visitor on HTTPS still makes Cloudflare connect to your origin on port 80. If you closed 80 because “the site is HTTPS-only”, Flexible produces 522 (or 521).
The same happens if an Origin Rule rewrites the destination port, or the record is meant for a service on a non-standard port. Check the effective SSL mode for the hostname, including Configuration Rules and old Page Rules that override the zone setting. The better fix is to serve HTTPS on the origin and use Full (strict); the Cloudflare SSL modes guide compares them.
4. The origin can’t accept new connections in time
The server is up, but the kernel isn’t answering SYNs. The tcpdump test shows SYNs arriving with no reply, and no firewall rule explains it. Look for:
nstat -az TcpExtListenOverflows TcpExtListenDrops
ss -ltn 'sport = :443' # Recv-Q near Send-Q means the accept queue is full
sudo journalctl -k | grep -i conntrack
- Listen queue overflow. The application accepts connections slower than they arrive, usually because all workers are busy. The counters above keep rising. Raise the worker capacity first (nginx
worker_connections, PHP-FPMpm.max_children, your app server’s threads), then the backlog (net.core.somaxconn, and nginx’slisten 443 ssl backlog=...). On older kernels the somaxconn default was 128, which is easy to fill. - A full connection-tracking table. The kernel log shows
nf_conntrack: table full, dropping packet. Comparesysctl net.netfilter.nf_conntrack_countwithnet.netfilter.nf_conntrack_maxand raise the maximum, or stop tracking traffic you don’t need to. - A SYN flood aimed directly at the origin IP, which bypasses Cloudflare entirely. If your origin IP is known, lock it down to Cloudflare’s ranges so the flood can’t reach the web server.
This version comes and goes with load, which is the best way to recognise it.
5. KeepAlive turned off on the origin
Cloudflare’s own troubleshooting list includes KeepAlive being disabled. Cloudflare reuses connections to your origin; with keep-alive off, every request needs a new TCP handshake, which multiplies the connection load on a busy origin and makes cause 4 far more likely. In nginx, keepalive_timeout 0; turns it off; in Apache, KeepAlive Off. Turn it back on.
6. A home server or an ISP in the way
Running the origin at home adds layers that Cloudflare must cross: the router needs a port forward to the server, some residential ISPs block inbound 80 and 443, and on carrier-grade NAT you have no public inbound address at all. If the WAN address on your router differs from what your public IP shows, you’re behind CGNAT and no port forward will work.
For a home server, a Cloudflare Tunnel (cloudflared) is the better design. The tunnel daemon makes an outbound connection to Cloudflare, so you need no open inbound ports, no port forward and no fixed IP, and 522 stops being possible. (Tunnel failures show up as different errors.)
Locking the origin to Cloudflare without causing 522
Allowing only Cloudflare to reach your origin is a good idea; it stops people from bypassing Cloudflare and hitting the server directly. It’s also the most common way to cause 522 later. A few rules:
- Allow both the IPv4 and IPv6 lists, on every layer: host firewall, cloud security group, hosting panel.
- Cloudflare’s ranges change rarely, but they do change. Refresh the allowlist from the published lists on a schedule instead of pasting them once.
- Prefer REJECT over DROP for anything that isn’t Cloudflare. It barely changes your security, and when something breaks you get a fast 521 instead of a slow 522.
- If you want a lock that doesn’t depend on IP lists, use Authenticated Origin Pulls (the origin requires Cloudflare’s client certificate) or a tunnel.
An example for ufw (when the lists change, remove the rules for ranges that were dropped):
for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
sudo ufw allow proto tcp from "$ip" to any port 80,443
done
After the fix
- Reload the page and check the Ray ID changed. A 522 with a new Ray ID means the origin is still failing.
- Run the tcpdump command once more and confirm
Flags [S.]goes back for each SYN. - If you use several origins (multiple A records, a load balancer pool), test each IP. One dead origin produces 522 for a share of requests only.
- Run an HTTP check on the public hostname to confirm the full path returns a normal response.
Related errors
- Cloudflare error 525: TCP connected, but the TLS handshake with the origin failed.
- Cloudflare error 526: the handshake worked, but Full (strict) rejected the origin certificate.
- Port 443 closed: the same “nothing answers” problem when there’s no Cloudflare in front.
- ERR_CONNECTION_TIMED_OUT: the browser’s version of a connection that never gets an answer.
Check your own domain now
Free, no sign-up. Runs the exact check this guide describes and shows what to fix.