Fix Cloudflare 521 and 522 Errors for a Django Origin
Fix Cloudflare Error 521 web server is down and Error 522 connection timed out in front of Django: check that Nginx listens, allow Cloudflare IPs through the firewall, match the SSL mode to the origin, and fix origin DNS records.
Problem statement
Visitors see a Cloudflare error page instead of your Django site:
- Error 521: Web server is down โ Cloudflare reached your server's IP, but the connection was refused.
- Error 522: Connection timed out โ Cloudflare could not complete a TCP connection to your server at all.
Both are about the network path from Cloudflare to your origin, before Nginx or Django handle anything. A 521 means nothing accepted the connection on the port Cloudflare used (80 or 443). A 522 means packets were dropped: a firewall blocking Cloudflare's IPs, a wrong origin IP in DNS, or a server too overloaded to accept connections.
- Check the origin IP in Cloudflare DNS matches the server's current public IP.
- 521: make sure Nginx is running and listening on the port your SSL mode uses (443 for Full, 80 for Flexible).
- 522: allow Cloudflare's IP ranges on ports 80 and 443 in
ufwand the cloud firewall, and remove rate-limits or fail2ban bans on them. - Set SSL/TLS mode to Full (strict) and install a valid origin certificate.
- If the server is overloaded, fix capacity; Cloudflare times out connections it cannot open in about 15 seconds.
Step-by-step solution
1. Test the origin directly
Bypass Cloudflare by connecting to the origin IP with the right Host and SNI:
curl -svo /dev/null --resolve example.com:443:203.0.113.10 https://example.com/
curl -svo /dev/null --resolve example.com:80:203.0.113.10 http://example.com/
Connection refused reproduces a 521; a timeout reproduces a 522 (from your network, which may be allowed where Cloudflare is not). Compare the IP with the A record in the Cloudflare dashboard; after rebuilding a server, the DNS record often still points at the old one.
2. Fix 521: something must listen
sudo systemctl status nginx
sudo ss -ltnp | grep -E ':(80|443) '
Match the listener to the SSL mode:
- Full / Full (strict): Cloudflare connects to port 443. Nginx needs a
listen 443 sslblock with a certificate. - Flexible: Cloudflare connects to port 80. (Avoid Flexible; see below.)
If Nginx listens only on 80 and the zone uses Full, every request is a 521.
3. Fix 522: let Cloudflare through
Allow Cloudflare's published ranges (from https://www.cloudflare.com/ips-v4 and ips-v6) on 80 and 443:
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
Also check the provider firewall or security group, and any IP blocking you added: fail2ban jails that ban by $remote_addr will ban Cloudflare edge IPs unless Nginx restores the real client IP first, and Nginx limit_req zones keyed on the connecting IP throttle Cloudflare as one huge client. Restore real IPs:
# /etc/nginx/conf.d/cloudflare-realip.conf (regenerate when Cloudflare updates its list)
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 2400:cb00::/32;
# ... all ranges ...
real_ip_header CF-Connecting-IP;
4. Use Full (strict) SSL
Flexible mode sends traffic from Cloudflare to your origin over plain HTTP, which also causes redirect loops with SECURE_SSL_REDIRECT. Use Full (strict) with either a Let's Encrypt certificate or a free Cloudflare Origin CA certificate on Nginx, plus:
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
5. Check capacity
If 522s appear only at peaks, the server's accept queue overflows or Nginx runs out of worker connections. Check dmesg for SYN flooding messages, Nginx worker_connections, and CPU. Cloudflare's caching of static assets and anonymous pages can reduce origin load substantially.
Verification check: the curl --resolve tests succeed against port 443, Cloudflare's dashboard shows no 52x errors in Analytics, and tail -f /var/log/nginx/access.log shows real visitor IPs, not Cloudflare ranges.
Explanation
Cloudflare acts as a reverse proxy: visitors connect to its edge, and the edge opens its own connection to your origin. Errors in the 52x range describe that second connection. A 521 is an active refusal (TCP reset), meaning the host answered but nothing listens on the port. A 522 means the TCP handshake never completed, so packets were dropped or the host is unreachable. Because Cloudflare connects from its own IP ranges, a firewall that allows your office but not those ranges produces a 522 that you cannot reproduce from your desk.
Edge cases / notes
- Error 520 is an unknown error: the origin returned something empty or invalid, often a crashed upstream behind Nginx.
- Error 523 means the origin is unreachable at the routing level, usually a wrong IP; 525 and 526 are TLS handshake and certificate validation failures.
- Locked-down origins that only accept Cloudflare can use Authenticated Origin Pulls or a Cloudflare Tunnel instead of IP lists.
- IPv6. If your A record is fine but an AAAA record points at an address without a listener, some connections fail.
Next steps
- Configure the full setup with Deploy Django Behind Cloudflare.
- For slow requests that hit Cloudflare's limit, see Fix Cloudflare 524 Timeouts on Slow Django Requests.
FAQ
What is the difference between Cloudflare 521 and 522?
521 means your server refused the connection; 522 means the connection attempt timed out with no answer.
Why can I reach my server but Cloudflare cannot?
Your firewall may allow your IP but not Cloudflare's ranges, or fail2ban may have banned Cloudflare edge IPs.
Should I use Flexible SSL?
No. Use Full (strict) with a certificate on the origin. Flexible sends traffic unencrypted and causes redirect loops.
Do I need to allow Cloudflare IPs?
If your firewall restricts inbound web traffic, yes. Otherwise Cloudflare cannot reach your origin.