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. 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. 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. 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. 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. 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. 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. 1.Fix PHP-FPM configuration if using PHP:
ini
; In /etc/php/*/fpm/pool.d/www.conf
php_admin_value[memory_limit] = 256M
request_terminate_timeout = 60
catch_workers_output = yes
  1. 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. 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. 1.Test with Cloudflare's header size validation:
bash
# 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. 1.Direct curl to origin returns valid HTTP response with headers:
  2. 2.```bash
  3. 3.curl -I http://YOUR_ORIGIN_IP/ -H "Host: yourdomain.com"
  4. 4.# Should show HTTP/1.1 200 OK (or valid redirect)
  5. 5.`
  6. 6.No segfaults or crashes in system logs
  7. 7.Cloudflare-proxied requests succeed: curl -I https://yourdomain.com/
  8. 8.Response headers under 16KB total size
  9. 9.No 520 errors in Cloudflare analytics