Short answer: Cloudflare error 522 “Connection timed out” means Cloudflare could not finish a TCP connection to your origin server. It sent SYN packets for 19 seconds and no SYN-ACK came back. Less often, the connection opened but your server never acknowledged the request within 90 seconds. The visitor’s browser and Cloudflare are both fine, so the fault is on your server or in front of it. In practice it is one of four things: a firewall dropping Cloudflare’s IP ranges, a DNS record with the wrong IP, a server that is down or too busy to accept connections, or fail2ban or a rate limit that has banned Cloudflare addresses.
# 1. From your own computer: does the origin answer when you skip Cloudflare?
# 203.0.113.10 = your server's IP, example.com = your domain
curl -sk --connect-timeout 10 -o /dev/null -w 'connect=%{time_connect}s code=%{http_code}\n' \
--resolve example.com:443:203.0.113.10 https://example.com/
# SSL/TLS mode Flexible? Cloudflare dials port 80, so test that instead:
# --resolve example.com:80:203.0.113.10 http://example.com/
# 2. On the server: is the web server listening on 80 and 443?
sudo ss -tlnp '( sport = :80 or sport = :443 )'
# 3. On the server: do SYNs arrive, and do SYN-ACKs go back out?
sudo tcpdump -ni any 'tcp[tcpflags] & tcp-syn != 0 and (port 80 or port 443)'
# 4. Firewall and bans
sudo ufw status verbose
sudo nft list ruleset | grep -iE 'drop|reject'
sudo fail2ban-client banned--resolve sends the request straight to your server’s IP with the right hostname, so you see the origin without Cloudflare in the way. -k skips the certificate check, which you need if the origin uses a Cloudflare Origin CA certificate. Use it for this test only. A code=200 (or any real status) means the server works for you. Then the problem is something that treats Cloudflare differently. A hang that ends in connect=0.000000s code=000 means nobody can reach it, and the server itself is the place to look. If your firewall already lets in only Cloudflare’s ranges, this test will hang on purpose. Go straight to step 3.
What error 522 means, next to 520, 521, 523 and 524
Cloudflare is a reverse proxy, so two connections are involved: one from the visitor to Cloudflare, and a second from Cloudflare to your server. Every 52x error describes a failure on the second one. The number tells you how far the connection got:
| Error | What Cloudflare saw | Usual cause at the origin |
|---|---|---|
| 520 | The connection opened, but the answer was empty, reset or malformed | A crash mid-request, nginx return 444, oversized headers |
| 521 | The origin refused the connection with a reset | Web server not running, firewall set to reject |
| 522 | No SYN-ACK within 19 s, or no ACK for the request within 90 s | Firewall dropping Cloudflare, wrong IP in DNS, server down or overloaded |
| 523 | No route to the origin IP at all | Wrong or unroutable address, routing problem upstream |
| 524 | Connected and sent the request, but got no response within 125 s | A slow request or a backed-up application |
Cloudflare lists the 19-second and 90-second limits as not configurable. You cannot raise them, so the fix is always to make the origin answer. Read next to error 521, the two codes give a useful rule of thumb: a firewall that rejects Cloudflare tends to show up as 521, and one that silently drops its packets as 522.
Cause 1: the firewall drops Cloudflare’s IP ranges
Cloudflare names this the most common cause. Every request reaches your server from a Cloudflare edge address, not from the visitor. So your firewall has to allow Cloudflare’s published ranges on the ports Cloudflare uses. That means port 80 for the SSL/TLS mode Flexible, and port 443 for Full and Full (strict) when the visitor uses HTTPS. A classic mistake is to switch the mode to Full while the firewall still opens only port 80. Every HTTPS request then times out.
Confirm it with tcpdump on the server while you reload the site:
- SYNs (
Flags [S]) arrive from Cloudflare addresses, but noFlags [S.]goes back: your host firewall is dropping them. tcpdump sees packets before the firewall does, which is why this pattern is so clear. - Nothing arrives at all: the packets never reach the server. Check the DNS record (cause 2) and anything that filters traffic in front of the server (cause 6).
[S]in and[S.]out: the handshake works now. Look at load (cause 4) or at bans that come and go (cause 3).
To allow Cloudflare with ufw, loop over the official lists. Cloudflare publishes them as plain text, one range per line:
for net in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
sudo ufw allow proto tcp from "$net" to any port 80,443 comment 'Cloudflare'
done
sudo ufw status numbered | grep Cloudflare # one rule per range, v4 and v6With nftables or iptables, look for a policy drop chain without an accept rule for 80 and 443. Note that nft list ruleset also shows rules that iptables-nft and Docker created. If you want the origin to accept web traffic only from Cloudflare, add the rules above and remove any “allow 80/443 from anywhere” rule, such as ufw’s Nginx Full. Leave SSH alone. The ufw guide covers the safe order for changing rules on a live server.
Cause 2: the DNS record points at the wrong IP
Proxied records hide your origin IP from dig, which only shows Cloudflare addresses. So compare the record in the dashboard (DNS → Records) with the addresses the server really has:
ip -4 -br addr show scope global
ip -6 -br addr show scope globalThis goes wrong after a migration, after you swap a server, or when an old IP is still in a record that points at a machine that no longer exists. An address with no host behind it answers nothing, so the result is 522, or 523 if the address cannot be routed at all. With AAAA records, Cloudflare prefers IPv4 when a hostname has both an A and an AAAA address. A stale AAAA mostly hurts hostnames that have only an AAAA record. There, the IPv6 address must be correct and your firewall must allow Cloudflare’s IPv6 ranges too. Setting up the records from scratch is covered in pointing a domain at your VPS.
Cause 3: fail2ban or a rate limit banned Cloudflare
This cause is easy to miss, because the site works for most people. Behind Cloudflare, nginx and Apache log the Cloudflare edge address as the client, unless you restore the visitor’s real IP. A jail that watches the web logs, such as nginx-limit-req or nginx-botsearch, then bans a Cloudflare address because of somebody else’s traffic. Visitors routed through that address get 521 if the ban rejects packets, which fail2ban’s stock iptables, nftables and ufw actions do. They get 522 if it drops them, as a ban action switched to drop and most hand-written rate limits do. Everyone else gets the site, so the reports can cluster on a few Cloudflare locations. The error page names the data centre that served it.
curl -s https://www.cloudflare.com/ips-v4 -o /tmp/cf-ips.txt
sudo fail2ban-client banned | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | python3 -c '
import sys, ipaddress
nets = [ipaddress.ip_network(l.strip()) for l in open("/tmp/cf-ips.txt") if l.strip()]
for line in sys.stdin:
ip = ipaddress.ip_address(line.strip())
if any(ip in n for n in nets):
print("banned Cloudflare IP:", ip)'
sudo fail2ban-client unban 162.158.10.20 # unban each one it printsUnbanning only clears today’s symptom. The lasting fix has three parts. First, restore real visitor IPs so the logs name the right client. nginx does this with its realip module, which Debian and Ubuntu build in:
{
for net in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
echo "set_real_ip_from $net;"
done
echo "real_ip_header CF-Connecting-IP;"
} | sudo tee /etc/nginx/conf.d/cloudflare-realip.conf
sudo nginx -t && sudo systemctl reload nginxOn Apache, enable mod_remoteip with sudo a2enmod remoteip and give it RemoteIPHeader CF-Connecting-IP plus one RemoteIPTrustedProxy line per range. Second, add Cloudflare’s ranges to ignoreip in /etc/fail2ban/jail.local so an edge address can never be banned again. This prints a line you can paste under [DEFAULT]:
echo "ignoreip = 127.0.0.1/8 ::1" $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6)Third, accept that a local firewall ban on a visitor’s real IP does nothing for proxied traffic, because those packets still come from Cloudflare. If you want web jails to block anyone, use fail2ban’s cloudflare-token action, which places the ban at Cloudflare through its API. The same reasoning applies to hand-made per-source connection limits (iptables connlimit or hashlimit, or a security suite’s connection tracking). A few Cloudflare addresses carry the traffic of thousands of visitors, so they hit any per-IP limit first.
Cause 4: the server is down or too busy to accept connections
A server that has crashed, is stuck at boot, or is busy rebooting looks exactly like a firewall drop from outside. Open the console first. On a VMHeaven VPS, the browser VNC console in the panel works even when the server’s network is down or firewalled. If the machine is up, check whether it can still take new connections. When nginx runs out of worker_connections, or every Apache worker is busy, the kernel’s accept queue fills up and new SYNs are dropped. Cloudflare reports that as 522.
uptime; free -h
nstat -az TcpExtListenOverflows TcpExtListenDrops # run twice, a minute apart
sudo ss -ltn '( sport = :80 or sport = :443 )' # Recv-Q close to Send-Q = queue full
sudo dmesg -T | grep -iE 'conntrack|syn flooding'
sudo grep -i 'worker_connections are not enough' /var/log/nginx/error.log
sudo grep -i 'MaxRequestWorkers' /var/log/apache2/error.log- Overflow counters that keep growing, or
Recv-Qsitting at the backlog size: the web server is not accepting fast enough. Find what holds the workers (slow PHP, a locked database, swapping) before you raise limits. nf_conntrack: table full, dropping packet: the kernel’s connection tracking table is full, and every new connection is dropped until entries expire. Comparesysctl net.netfilter.nf_conntrack_countwithnet.netfilter.nf_conntrack_max.- High load with CPU near 100%: see VPS high CPU usage to find the process. If the load is real traffic and not a runaway job, the server is too small for the site.
Raising worker_connections, MaxRequestWorkers or PHP-FPM’s pm.max_children helps only if there is memory for the extra workers. Otherwise you trade 522s for the OOM killer. When the site has simply outgrown the server, size the next one to how it works: a Standard KVM carries more vCores and memory per plan for many parallel workers, while a Hi-CPU KVM is meant for workloads that lean on one or two cores rather than many.
Cause 5: keep-alive is turned off at the origin
This is the least likely item on the list, but Cloudflare names it. Cloudflare reuses open connections to your origin instead of starting a new TCP handshake for every request, and its connection-limits page asks you to keep HTTP keep-alive on. With keep-alive off, every request needs a fresh handshake, which adds up under load. nginx keeps connections alive for 75 seconds by default, and Apache has KeepAlive On by default. Look for someone switching that off:
sudo nginx -T 2>/dev/null | grep -n keepalive_timeout # "keepalive_timeout 0;" disables it
sudo grep -RniE '^\s*KeepAlive\s' /etc/apache2/ # "KeepAlive Off" disables itCause 6: the packets are dropped before your server
If the DNS record is right, the host firewall allows Cloudflare, and tcpdump shows nothing while visitors get 522, the traffic is lost before it reaches the machine. Your provider’s network firewall, a security group, or a filter in front of the server could be the cause, and so could a routing problem between Cloudflare and your network. That is the provider’s to fix. Cloudflare suggests giving them:
- the error code, the exact time with time zone, and the full URL;
- the Ray ID from the error page;
- an MTR from the origin to a Cloudflare address that used to connect. Find one in the access log from before you restored real IPs, or in
ss -tn.
sudo apt install -y mtr-tiny
mtr -rwc 50 162.158.1.1 # replace with a Cloudflare IP from your logsA few 522s come from Cloudflare-side setup rather than the origin. A Worker on a Custom Domain that fetches its own hostname returns 522. So does an Origin Rule that points at a hostname that does not resolve, or a Pages project whose CNAME does not point at its custom Pages domain. If you changed an Origin Rule’s port, that port has to be open in your firewall as well.
How to stop error 522 from coming back
- Script the Cloudflare allow-list and run it weekly from cron, so new ranges are allowed before traffic arrives from them.
- Restore real IPs before you enable any web jail, and keep Cloudflare’s ranges in
ignoreip. - Use Full (strict) and open 443, so the port Cloudflare dials matches the port your firewall allows.
- Watch the origin, not only the edge: Cloudflare’s Origin Analytics shows TCP connection failures per path, and the HTTP Traffic page filters by edge and origin status code. An external check against a health URL warns you before visitors do.
- Leave headroom: an origin that runs near its connection limit at normal traffic will throw 522s at the first spike.
If you only visit a site that shows 522, nothing on your side causes it or can fix it. Try again in a few minutes, and if it stays, tell the site owner. The error page shows that your browser and Cloudflare both work and that the host does not answer.
Frequently asked
What is the difference between Cloudflare error 521 and 522?
Both mean Cloudflare could not connect to your origin server. 521 means the server refused the connection straight away, usually because the web server is not running or a firewall rejects Cloudflare. 522 means nothing answered within 19 seconds, which points at a firewall silently dropping Cloudflare's IP ranges, a wrong IP in the DNS record, or an overloaded or offline server.
How long does Cloudflare wait before showing error 522?
Cloudflare waits 19 seconds for the TCP handshake with your origin, resending the SYN several times in that window. If the connection opens but the server does not acknowledge the request within 90 seconds, that is also a 522. Cloudflare lists both limits as not configurable.
Can a visitor fix Cloudflare error 522?
No. Error 522 means the website's own server is not answering Cloudflare, so clearing the browser cache or switching networks will not help. Try again later, or tell the site owner, who has to fix the server or its firewall.