Introduction

CDN 502 Bad Gateway errors occur when the CDN edge server cannot successfully retrieve content from the origin server, causing the CDN to return an error response to users. This error indicates the CDN itself is operational but the connection to your origin infrastructure failed. Common causes include origin server down or unresponsive, firewall blocking CDN IP ranges, SSL/TLS certificate mismatch between origin and CDN configuration, origin response timeout exceeding CDN limits, DNS resolution failures for origin hostname, origin server returning malformed HTTP responses, origin connection pool exhaustion, HTTP/2 or HTTP/3 protocol negotiation failures, origin server overloaded and rejecting connections, and misconfigured origin host header. The fix requires systematic diagnosis of origin accessibility from CDN edge locations, SSL certificate validation, timeout configuration, and origin server health. This guide provides production-proven troubleshooting for 502 errors across AWS CloudFront, Cloudflare, Akamai, Fastly, and other major CDN providers.

Symptoms

  • HTTP 502 Bad Gateway returned by CDN for all or some requests
  • 502 errors intermittent, affecting subset of requests
  • 502 errors from specific geographic regions only
  • CDN error logs show "Origin connection failed" or "Origin timeout"
  • SSL handshake errors between CDN and origin
  • Origin server accessible directly but not through CDN
  • DNS resolution failures for origin hostname
  • Origin server logs show no incoming requests from CDN
  • 502 errors spike after origin deployment or configuration change
  • CDN health checks show origin as unhealthy

Common Causes

  • Origin server crashed, stopped, or not listening on expected port
  • Security group/firewall blocking CDN edge IP ranges
  • Origin SSL certificate expired, self-signed, or hostname mismatch
  • CDN origin protocol policy mismatch (HTTPS configured but origin HTTP only)
  • Origin response time exceeds CDN timeout threshold
  • DNS resolution failures for origin hostname at edge locations
  • Origin server returning invalid HTTP responses (malformed headers)
  • Host header misconfiguration between CDN and origin
  • Origin connection limits (max connections) exceeded
  • Load balancer in front of origin marking targets unhealthy
  • Origin server resource exhaustion (CPU, memory, file descriptors)
  • Path-based routing misconfiguration at origin

Step-by-Step Fix

### 1. Diagnose origin connectivity

Check origin accessibility:

```bash # Test origin directly (bypass CDN) curl -v http://origin-server-ip:80/health curl -v https://origin-server-ip:443/health

# Test with Host header (simulate CDN request) curl -v -H "Host: www.example.com" http://origin-ip/

# If direct access fails, origin is the problem # If direct access works, issue is CDN-specific

# Test from multiple locations # Use tools like: # - curl from different geographic regions # - pingdom.com # - uptimerobot.com

# Check origin server status systemctl status nginx # or apache2, httpd, etc. systemctl status application-service

# Check listening ports ss -tlnp | grep :80 ss -tlnp | grep :443 netstat -tlnp | grep :80 ```

CDN-specific origin health:

```bash # AWS CloudFront - Check origin status aws cloudfront get-distribution --id E1234567890ABC \ | jq '.Distribution.DistributionConfig.Origins'

# Output shows: # { # "Items": [{ # "Id": "my-origin", # "DomainName": "origin.example.com", # "OriginPath": "", # "CustomOriginConfig": { # "HTTPPort": 80, # "HTTPSPort": 443, # "OriginProtocolPolicy": "https-only", # "OriginSSLProtocols": ["TLSv1.2"] # } # }] # }

# Cloudflare - Check origin health (requires Browser Integrity Check) # Cloudflare doesn't have built-in origin health checks # Use Cloudflare Load Balancing for health checks

# Fastly - Check origin status curl "https://api.fastly.com/service/SERVICE_ID/version/1/backend" \ -H "Fastly-Key: API_TOKEN"

# Akamai - Check origin health (requires EdgeWorkers) # Use Property Manager to configure health checks ```

Analyze CDN error logs:

```bash # CloudFront - Check error logs in S3 aws s3 ls s3://cloudfront-logs-bucket/ aws s3 cp s3://cloudfront-logs-bucket/E1234567890ABC.2024-01-15.log . \ && grep "502" *.log

# CloudFront fields to check: # - x-edge-result-type (Error) # - x-edge-error-result-type (OriginError) # - sc-status (502)

# Cloudflare - Check Analytics curl "https://api.cloudflare.com/v4/zones/ZONE_ID/analytics/dashboard" \ -H "Authorization: Bearer API_TOKEN"

# Look for: # - http_status_502 count # - origin_status errors

# Fastly - Real-time error logs curl "https://api.fastly.com/service/SERVICE_ID/stats/realtime" \ -H "Fastly-Key: API_TOKEN"

# Look for 5xx errors in response ```

### 2. Fix firewall and security group rules

AWS CloudFront IP ranges:

```bash # CloudFront uses specific IP ranges that must be allowed # Download current ranges curl https://ip-ranges.amazonaws.com/ip-ranges.json \ | jq '.prefixes[] | select(.service=="CLOUDFRONT")'

# Terraform security group rule resource "aws_security_group_rule" "cloudfront_ingress" { type = "ingress" from_port = 443 to_port = 443 protocol = "tcp" cidr_blocks = [ # CloudFront IP ranges (update regularly) # Or use managed prefix list ] security_group_id = aws_security_group.origin.id }

# Use AWS managed prefix list (recommended) data "aws_managed_prefix_list" "cloudfront" { filter { name = "prefix-list-name" values = ["com.amazonaws.global.cloudfront.origin-facing"] } }

resource "aws_security_group_rule" "cloudfront_ingress" { type = "ingress" from_port = 443 to_port = 443 protocol = "tcp" source_prefix_list_ids = [data.aws_managed_prefix_list.cloudfront.id] security_group_id = aws_security_group.origin.id } ```

Cloudflare IP ranges:

```bash # Cloudflare publishes IP ranges curl https://www.cloudflare.com/ips-v4 # IPv4 ranges curl https://www.cloudflare.com/ips-v6 # IPv6 ranges

# Nginx configuration to allow Cloudflare IPs only # /etc/nginx/conf.d/cloudflare.conf set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; # ... add all Cloudflare ranges real_ip_header CF-Connecting-IP;

# Or use iptables for ip in $(curl https://www.cloudflare.com/ips-v4); do iptables -A INPUT -p tcp --dport 443 -s $ip -j ACCEPT done

# UFW (Ubuntu) for ip in $(curl -s https://www.cloudflare.com/ips-v4); do ufw allow from $ip to any port 443 done ```

General firewall troubleshooting:

```bash # Test if firewall is blocking # From origin server, allow CDN IPs temporarily

# Check current firewall rules iptables -L -n | grep 443 ufw status

# Temporarily allow all for testing (then restrict) iptables -I INPUT -p tcp --dport 443 -j ACCEPT

# If 502 stops, firewall was the issue # Then add specific CDN IP ranges

# Check for rate limiting iptables -L -n | grep limit fail2ban-client status # Check if CDN IPs banned ```

### 3. Fix SSL/TLS certificate issues

Origin certificate validation:

```bash # Check origin certificate openssl s_client -connect origin.example.com:443 -showcerts

# Verify certificate chain openssl verify -CAfile /etc/ssl/certs/ca-bundle.crt origin.crt

# Check certificate details openssl x509 -in origin.crt -noout -subject -issuer -dates

# Common issues: # - Certificate expired (notAfter in past) # - Self-signed certificate (issuer = subject) # - Hostname mismatch (CN/SAN doesn't match origin domain) # - Incomplete chain (missing intermediate CA) ```

CloudFront origin SSL configuration:

```bash # CloudFront origin protocol policy # Options: http-only, match-viewer, https-only, ssl-only

aws cloudfront update-distribution \ --id E1234567890ABC \ --distribution-config 'file://origin-config.json'

# origin-config.json - HTTPS with certificate validation { "Origins": { "Items": [{ "DomainName": "origin.example.com", "CustomOriginConfig": { "HTTPPort": 80, "HTTPSPort": 443, "OriginProtocolPolicy": "https-only", "OriginSSLProtocols": ["TLSv1.2"], "OriginReadTimeout": 30, "OriginKeepaliveTimeout": 5 } }] } }

# If origin has self-signed cert (not recommended): # Use HTTP origin protocol policy instead # "OriginProtocolPolicy": "http-only" ```

Cloudflare origin certificate:

```bash # Cloudflare offers Origin CA certificates (free for Cloudflare users) # These are trusted by Cloudflare but not by browsers directly

# Generate Origin CA certificate curl -X POST "https://api.cloudflare.com/v4/certificates" \ -H "Authorization: Bearer API_TOKEN" \ -d '{ "hostnames": ["origin.example.com"], "request_type": "origin-rsa", "requested_validity": 365 }'

# Install on origin server # Nginx configuration server { listen 443 ssl; server_name origin.example.com;

ssl_certificate /etc/ssl/certs/origin-ca.pem; ssl_certificate_key /etc/ssl/private/origin-ca-key.pem;

# Cloudflare-specific SSL settings ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; }

# In Cloudflare dashboard: # SSL/TLS > Origin Server > Install certificate # SSL/TLS > Encryption Mode > Full (strict) ```

### 4. Fix timeout configuration

CloudFront timeout settings:

```bash # CloudFront origin timeout defaults: # - Origin Response Timeout: 10 seconds (custom origins) # - Origin Keep-Alive Timeout: 5 seconds

# Increase timeouts for slow origins aws cloudfront update-distribution \ --id E1234567890ABC \ --distribution-config 'file://timeout-config.json'

# timeout-config.json { "Origins": { "Items": [{ "DomainName": "origin.example.com", "CustomOriginConfig": { "OriginReadTimeout": 60, # Increase to 60 seconds "OriginKeepaliveTimeout": 10, # Increase to 10 seconds "OriginProtocolPolicy": "https-only" } }] } }

# Note: Maximum OriginReadTimeout is 60 seconds # If origin needs more time, optimize origin instead ```

Cloudflare timeout settings:

```bash # Cloudflare timeout defaults: # - First byte timeout: 100 seconds # - Overall timeout: 100 seconds

# Adjust via Page Rules (Enterprise) curl -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/page_rules" \ -H "Authorization: Bearer API_TOKEN" \ -d '{ "targets": [{ "target": "url", "constraint": { "operator": "matches", "value": "example.com/api/*" } }], "actions": [{ "id": "edge_cache_ttl", "value": 3600 }] }'

# For long-running requests, use: # 1. Cache the response at edge # 2. Use Workers to handle async # 3. Optimize origin response time ```

Fastly timeout configuration:

```bash # Fastly backend timeout settings curl -X PUT "https://api.fastly.com/service/SERVICE_ID/version/1/backend/origin" \ -H "Fastly-Key: API_TOKEN" \ -d '{ "connect_timeout": 5000, # Connection timeout (ms) "first_byte_timeout": 60000, # Time to first byte (ms) "between_bytes_timeout": 10000 # Between bytes timeout (ms) }'

# Default values: # - connect_timeout: 1000ms # - first_byte_timeout: 15000ms # - between_bytes_timeout: 10000ms ```

Origin server timeout optimization:

```nginx # Nginx origin - optimize for CDN server { # Upstream keepalive (reduce connection overhead) upstream backend { server 127.0.0.1:8080; keepalive 32; }

location / { proxy_pass http://backend;

# Timeout settings proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s;

# Buffer responses (send quickly to CDN) proxy_buffering on; proxy_buffer_size 4k; proxy_buffers 8 4k;

# Send proper headers add_header X-Response-Time $upstream_response_time; } } ```

### 5. Fix DNS resolution issues

Origin hostname resolution:

```bash # If origin is specified by hostname (not IP), DNS must resolve

# Test DNS resolution dig origin.example.com nslookup origin.example.com

# Check from multiple DNS servers dig @8.8.8.8 origin.example.com dig @1.1.1.1 origin.example.com

# DNS propagation issues # If origin hostname changed, DNS may take time

# Solutions: # 1. Use IP address instead of hostname (CDN allows this) # 2. Reduce DNS TTL before changes # 3. Use multiple A records for redundancy

# CloudFront - Origin with custom domain aws cloudfront update-distribution \ --id E1234567890ABC \ --distribution-config '{ "Origins": { "Items": [{ "DomainName": "origin.example.com", # Must resolve "CustomOriginConfig": {...} }] } }'

# Or use S3 bucket (no DNS issues) # "DomainName": "my-bucket.s3.amazonaws.com" ```

Cloudflare-specific DNS:

```bash # Cloudflare proxies DNS through its network # Ensure origin DNS is not also proxied (DNS-only record)

# In Cloudflare DNS settings: # origin.example.com A 1.2.3.4 DNS only (not proxied) # www.example.com A 1.2.3.4 Proxied (orange cloud)

# If origin record is proxied, circular routing occurs

# Check DNS record status via API curl "https://api.cloudflare.com/v4/zones/ZONE_ID/dns_records" \ -H "Authorization: Bearer API_TOKEN"

# Look for: # "proxied": false # For origin record # "proxied": true # For CDN-facing record ```

### 6. Fix host header issues

Host header configuration:

```bash # CDN must send correct Host header to origin # Origin uses Host header to route to correct virtual host

# CloudFront - Origin custom headers aws cloudfront update-distribution \ --id E1234567890ABC \ --distribution-config '{ "Origins": { "Items": [{ "DomainName": "origin.example.com", "CustomOriginConfig": {...}, "OriginCustomHeaders": [{ "HeaderName": "Host", "HeaderValue": "www.example.com" # Match origin vhost }] }] } }'

# Cloudflare - Host header override # In Rules > Transform Rules > HTTP Headers # Add header: Host = www.example.com

# Or use Workers to modify headers addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)); });

async function handleRequest(request) { const newHeaders = new Headers(request.headers); newHeaders.set('Host', 'www.example.com');

const newRequest = new Request(request, { headers: newHeaders });

return fetch(newRequest); } ```

Nginx origin virtual host:

```nginx # Nginx must match Host header to correct server block server { listen 80; listen 443 ssl; server_name www.example.com; # Must match CDN Host header

# If Host doesn't match, default server responds # May cause 502 if default server not configured

location / { proxy_pass http://backend; } }

# Catch-all for unmatched hosts server { listen 80 default_server; listen 443 ssl default_server; server_name _;

return 444; # Close connection without response } ```

### 7. Implement origin redundancy

Multiple origins with failover:

```bash # CloudFront - Origin groups with failover aws cloudfront create-distribution-with-tags \ --distribution-config 'file://origin-group-config.json'

# origin-group-config.json { "OriginGroups": { "Quantity": 1, "Items": [{ "Id": "origin-group-1", "FailoverCriteria": { "StatusCodes": { "Quantity": 1, "Items": [502, 503, 504] # Failover on these errors } }, "Members": { "Quantity": 2, "Items": [ { "OriginId": "primary-origin", "DomainName": "origin1.example.com" }, { "OriginId": "secondary-origin", "DomainName": "origin2.example.com" } ] } }] }, "DefaultCacheBehavior": { "TargetOriginId": "origin-group-1" } }

# Fastly - Multiple backends with health checks curl -X POST "https://api.fastly.com/service/SERVICE_ID/version/1/backend" \ -H "Fastly-Key: API_TOKEN" \ -d '{ "name": "primary-origin", "address": "origin1.example.com", "port": 443, "connect_timeout": 5000, "first_byte_timeout": 60000, "between_bytes_timeout": 10000, "healthcheck": "origin-healthcheck" }'

curl -X POST "https://api.fastly.com/service/SERVICE_ID/version/1/backend" \ -H "Fastly-Key: API_TOKEN" \ -d '{ "name": "secondary-origin", "address": "origin2.example.com", "port": 443, "connect_timeout": 5000, "first_byte_timeout": 60000, "between_bytes_timeout": 10000, "shield": "secondary" }' ```

Health check configuration:

```bash # Fastly - Health check definition curl -X POST "https://api.fastly.com/service/SERVICE_ID/version/1/healthcheck" \ -H "Fastly-Key: API_TOKEN" \ -d '{ "name": "origin-healthcheck", "url": "/health", "host": "origin.example.com", "timeout": 5000, "check_interval": 10000, "expected_response": 200, "window": 5, "threshold": 3, "initial": 1 }'

# Cloudflare - Load Balancer health check curl -X POST "https://api.cloudflare.com/v4/zones/ZONE_ID/load_balancers/pools" \ -H "Authorization: Bearer API_TOKEN" \ -d '{ "name": "origin-pool", "origins": [{ "name": "origin-1", "address": "origin1.example.com", "enabled": true }], "healthcheck": { "path": "/health", "port": 443, "interval": 30, "retries": 3, "timeout": 5, "type": "https" } }' ```

Prevention

  • Monitor origin health with synthetic checks from multiple regions
  • Set appropriate timeout values based on typical response times
  • Use origin redundancy with automatic failover
  • Implement proper SSL certificate management with auto-renewal
  • Configure firewall rules to allow all CDN IP ranges
  • Use host header override to ensure correct origin routing
  • Enable CDN error logging and alerting for 5xx errors
  • Implement circuit breakers at origin to prevent overload
  • Use connection keepalive to reduce connection overhead
  • Document runbooks for 502 troubleshooting per CDN provider
  • **503 Service Unavailable**: All origin servers unhealthy
  • **504 Gateway Timeout**: Origin response exceeded timeout
  • **CDN cache invalidation failed**: Purge API errors
  • **SSL certificate verify failed**: Origin certificate not trusted
  • **DNS resolution failed**: Origin hostname not resolving