Introduction
A 520 error is Cloudflare's catch-all for unexpected origin behavior. It means Cloudflare received something from your server, but it wasn't a valid HTTP response that could be forwarded to the visitor. This could be an empty response (connection closed with zero bytes), a malformed HTTP header, an oversized header, or a response that violates HTTP protocol specifications. Because 520 errors are generic, diagnosis requires careful examination of your origin server's behavior.
Symptoms
- Error page shows
Error 520: Web server is returning an unknown error - Response from origin is empty or truncated
- Works intermittently, suggesting server crashes or restarts under load
- May occur only for specific URLs or request types
- Origin logs show connections but incomplete or missing request logs
- Backend application processes may be crashing silently
Common Causes
- Origin server crashes or segfaults before sending response headers
- Empty response due to application error with no error handling
- HTTP response headers exceed Cloudflare's 16KB header size limit
- Invalid or malformed HTTP response headers from origin
- Response body sent before headers (protocol violation)
- Backend application timeout without sending any response
- Web server (nginx/Apache) misconfiguration dropping connections
- Origin returns compressed response with incorrect Content-Length
Step-by-Step Fix
- 1.Check origin server error logs for crashes or errors:
```bash # Check nginx error log tail -100 /var/log/nginx/error.log
# Check Apache error log tail -100 /var/log/apache2/error.log
# Check application logs tail -100 /var/log/php-fpm/error.log tail -100 /var/log/nodejs/app.log ```
- 1.Test origin server directly to see raw response:
```bash # Test with curl verbose output curl -v http://YOUR_ORIGIN_IP/ -H "Host: yourdomain.com"
# Check for empty responses curl -v http://YOUR_ORIGIN_IP/ -H "Host: yourdomain.com" 2>&1 | grep -E "HTTP/|< |Empty"
# Test with telnet for raw response telnet YOUR_ORIGIN_IP 80 GET / HTTP/1.1 Host: yourdomain.com
# Watch for empty response or connection close ```
- 1.Check for header size issues:
```bash # Test with headers dump curl -D - http://YOUR_ORIGIN_IP/ -H "Host: yourdomain.com" -o /dev/null
# Check Set-Cookie headers specifically curl -I http://YOUR_ORIGIN_IP/ -H "Host: yourdomain.com" 2>&1 | grep -i "set-cookie"
# Count header size curl -I http://YOUR_ORIGIN_IP/ -H "Host: yourdomain.com" 2>&1 | wc -c ```
- 1.Reduce response header size if exceeding 16KB limit:
```nginx # nginx: Disable unnecessary headers server { # Remove server tokens server_tokens off;
# Limit cookies set by backend proxy_hide_header Set-Cookie;
# Or use fastcgi_hide_header for PHP backends fastcgi_hide_header X-Powered-By; } ```
- 1.Check for application crashes and memory issues:
```bash # Check for recent segfaults dmesg | grep -i segfault journalctl -k | grep -i segfault
# Check application memory usage ps aux --sort=-%mem | head -20
# Check for OOM kills dmesg | grep -i "out of memory" journalctl | grep -i "oom-killer" ```
- 1.Increase backend timeouts and buffer sizes:
```nginx # In nginx configuration http { # Increase buffer for large headers fastcgi_buffer_size 32k; fastcgi_buffers 8 16k;
# Increase proxy buffer proxy_buffer_size 32k; proxy_buffers 8 16k;
# Timeout settings proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } ```
- 1.Fix PHP-FPM configuration if using PHP:
; In /etc/php/*/fpm/pool.d/www.conf
php_admin_value[memory_limit] = 256M
request_terminate_timeout = 60
catch_workers_output = yes- 1.Check for SSL/TLS handshake issues:
```bash # Test SSL connection with details openssl s_client -connect YOUR_ORIGIN_IP:443 -servername yourdomain.com
# Check certificate validity openssl s_client -connect YOUR_ORIGIN_IP:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -dates ```
- 1.Enable detailed logging at origin:
```nginx # Enable debug logging temporarily error_log /var/log/nginx/error.log debug;
# Log request headers log_format detailed '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' 'request_body: $request_body'; ```
- 1.Test with Cloudflare's header size validation:
# Check if response headers exceed limit
# Cloudflare limit is 16KB for header block
curl -I http://YOUR_ORIGIN_IP/ -H "Host: yourdomain.com" -H "Accept: */*"Verification
After applying fixes:
- 1.Direct curl to origin returns valid HTTP response with headers:
- 2.```bash
- 3.curl -I http://YOUR_ORIGIN_IP/ -H "Host: yourdomain.com"
- 4.# Should show HTTP/1.1 200 OK (or valid redirect)
- 5.
` - 6.No segfaults or crashes in system logs
- 7.Cloudflare-proxied requests succeed:
curl -I https://yourdomain.com/ - 8.Response headers under 16KB total size
- 9.No 520 errors in Cloudflare analytics