TroubleshootingWritten by GLWS14 min readUbuntu 24.04 LTS / AlmaLinux 9
Commands run on your server unless a section says otherwise. Replace example usernames, addresses and paths with your own values. Keep a working session open when changing remote access.

What the four errors mean

These errors come from Cloudflare when it cannot talk to your origin server, the VPS. Cloudflare is working. The problem is between it and your server. 521 means the origin refused the connection or is down. 522 means Cloudflare could not complete a connection in time. 525 means the TLS handshake with the origin failed. 526 means the origin's certificate is not valid while Cloudflare's SSL mode requires a valid one.

Always test the origin directly, bypassing Cloudflare, and compare. That tells you quickly whether the server or the setup is at fault.

Isolate the problem

Check that the DNS record points to the right IP address, that the web server is running and that it answers when you talk to it directly. The curl command below sends a request to the origin's IP with your domain name, so the right site and certificate are selected, without going through Cloudflare.

From your own computer (replace the domain and origin IP)
curl -sSI --resolve example.com:443:ORIGIN_IP https://example.com/ | head -n 5
curl -sSI --resolve example.com:80:ORIGIN_IP http://example.com/ | head -n 5
On the VPS
sudo ss -lntp | grep -E ":(80|443) "
systemctl is-active nginx apache2 httpd caddy 2>/dev/null
Cloudflare's dashboard lets you switch a record to "DNS only" (grey cloud) for a moment, which removes Cloudflare from the path. Use it for testing, then turn it back on.

521: the origin is down or refusing connections

Nothing is accepting connections on the port Cloudflare uses, or something is actively rejecting them. Check that the web server runs, that it listens on 443 (and 80), and that the firewall allows these ports. A crashed service, a failed reload after a bad configuration, or a recent firewall change are the usual causes.

On the VPS
sudo systemctl status nginx --no-pager | head -n 8
sudo nginx -t
sudo tail -n 20 /var/log/nginx/error.log
sudo ufw status verbose 2>/dev/null | head -n 20
sudo firewall-cmd --list-all 2>/dev/null

522: connection timed out

Cloudflare reached your IP address but got no answer in time. Typical causes are a firewall silently dropping Cloudflare's addresses, the wrong IP in the DNS record, a server too overloaded to accept connections, or a provider-level firewall. Security tools sometimes cause it: Fail2ban or a rate limiter can ban a Cloudflare address, because all your visitors appear to come from a few Cloudflare IPs.

On the VPS
sudo fail2ban-client status 2>/dev/null
sudo fail2ban-client status sshd 2>/dev/null | tail -n 4
uptime
free -h
dig +short A example.com

Allow Cloudflare to reach the origin

If your firewall only allows certain addresses, allow Cloudflare's published ranges on ports 80 and 443. Fetch the lists from Cloudflare, since they change occasionally, and refresh your rules from time to time. This also lets you block everyone else from the origin, so attackers cannot bypass Cloudflare.

Ubuntu (UFW)
for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do
  sudo ufw allow from "$ip" to any port 80,443 proto tcp
done
sudo ufw status numbered | head -n 20
AlmaLinux (firewalld)
for ip in $(curl -s https://www.cloudflare.com/ips-v4); do
  sudo firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=$ip port port=443 protocol=tcp accept"
done
for ip in $(curl -s https://www.cloudflare.com/ips-v6); do
  sudo firewall-cmd --permanent --add-rich-rule="rule family=ipv6 source address=$ip port port=443 protocol=tcp accept"
done
sudo firewall-cmd --reload
Do this before you remove any broad allow rule for ports 80 and 443, and keep your own SSH access intact. Test from a second session.

Restore real visitor addresses in your logs

Behind Cloudflare, your server logs show Cloudflare's addresses. Tell Nginx to trust Cloudflare's ranges and read the visitor address from the CF-Connecting-IP header, so that logs, rate limits and Fail2ban see the real client, and not Cloudflare itself.

Nginx: a file such as /etc/nginx/conf.d/cloudflare-realip.conf
# generate the set_real_ip_from lines from https://www.cloudflare.com/ips-v4 and ips-v6
set_real_ip_from 173.245.48.0/20;
real_ip_header CF-Connecting-IP;
Generate the full list
for ip in $(curl -s https://www.cloudflare.com/ips-v4) $(curl -s https://www.cloudflare.com/ips-v6); do echo "set_real_ip_from $ip;"; done | sudo tee /etc/nginx/conf.d/cloudflare-realip-ranges.conf
sudo nginx -t && sudo systemctl reload nginx

525: TLS handshake with the origin failed

In Full or Full (strict) mode, Cloudflare connects to your origin over HTTPS. A 525 means that failed: nothing serves TLS on port 443, the server has no certificate for the domain, or it only offers protocols or ciphers that Cloudflare does not accept. Test the TLS handshake against the origin directly.

From your own computer
openssl s_client -connect ORIGIN_IP:443 -servername example.com </dev/null 2>&1 | grep -E "subject=|issuer=|Verify return|alert|handshake"
On the VPS: is a TLS listener configured?
sudo nginx -T 2>/dev/null | grep -nE "listen .*443|ssl_certificate|server_name"

526: the origin certificate is not valid

In Full (strict) mode, the origin must present a certificate that is valid for the domain, not expired, and issued by a trusted authority or by Cloudflare's own Origin CA. Self-signed certificates give a 526. Fix it by installing a Let's Encrypt certificate with the reverse proxy guide, or a free Cloudflare Origin CA certificate. Check the dates and the names on what the origin presents.

From your own computer
echo | openssl s_client -connect ORIGIN_IP:443 -servername example.com 2>/dev/null | openssl x509 -noout -subject -issuer -dates
On the VPS: renew Let's Encrypt certificates
sudo certbot certificates
sudo certbot renew --dry-run
Do not "fix" 526 by choosing the Flexible SSL mode. That sends traffic from Cloudflare to your server unencrypted, and often causes redirect loops too. Keep Full (strict) and repair the certificate.

Check SSL mode and keep it healthy

In the Cloudflare dashboard under SSL/TLS, use Full (strict) once the origin has a valid certificate. If you use Let's Encrypt's HTTP validation, make sure renewal still works behind Cloudflare, as the renewal troubleshooting guide describes. Monitor your site from outside, set a certificate-expiry alert and keep a note of the origin IP address and your firewall rules, because these errors almost always come back to one of them.

From your own computer
curl -sI https://example.com/ | head -n 8
curl -sI https://example.com/ | grep -i -E "server|cf-ray"

Official documentation

These upstream references provide more detail on the commands and workflows in this guide.