Introduction
An SSL redirect loop occurs when HTTP requests redirect to HTTPS, and HTTPS requests redirect back to HTTP, creating an endless cycle. The browser eventually gives up with an error like ERR_TOO_MANY_REDIRECTS. This typically happens when multiple layers - web server, application, CDN, or load balancer - each try to enforce HTTPS without coordination.
Symptoms
- Browser error:
ERR_TOO_MANY_REDIRECTS - Page never loads, browser keeps redirecting
- URL keeps changing between http:// and https://
- Some browsers show "redirect loop detected"
- Works when accessing specific protocol directly but not via redirect
curl -Lshows multiple redirects never completing- Redirect chain shows alternating HTTP/HTTPS
Common Causes
- Web server redirects HTTP to HTTPS, app redirects HTTPS to HTTP
- Load balancer terminates SSL, backend expects HTTP, LB redirects HTTP
- CDN configured for HTTPS but origin sends HTTP redirects
- Two redirect rules conflicting (HTTP->HTTPS and HTTPS->HTTP)
- Application framework forcing HTTP while server forcing HTTPS
- WordPress/homepage settings with wrong protocol
- HSTS preload forcing HTTPS but server redirecting back
Step-by-Step Fix
Step 1: Trace the Redirect Chain
```bash # Follow redirects and see the pattern curl -L -v http://example.com 2>&1 | grep -E "< HTTP|< Location"
# Or use wget wget --spider -S http://example.com 2>&1 | grep -E "Location|HTTP"
# Look for alternating http/https in Location headers # Pattern like: # Location: https://example.com # Location: http://example.com # Location: https://example.com # (loop detected) ```
Step 2: Check Each Layer's Redirect Behavior
```bash # Test web server directly (bypass CDN/proxy if possible) curl -I http://origin-server-ip/ -H "Host: example.com"
# Test via CDN curl -I http://example.com
# Test HTTPS curl -I https://example.com
# Compare responses at each layer # Identify which layer is creating the loop ```
Step 3: Fix Web Server Configuration
Nginx - correct redirect configuration:
```nginx # HTTP server - redirect to HTTPS server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }
# HTTPS server - NO redirect back to HTTP server { listen 443 ssl; server_name example.com www.example.com;
ssl_certificate /etc/ssl/certs/example.com.crt; ssl_certificate_key /etc/ssl/private/example.com.key;
# Application code here, no HTTP redirect # If app tries to redirect, configure app to not do that } ```
Apache - correct redirect configuration:
```apache # HTTP virtual host - redirect to HTTPS <VirtualHost *:80> ServerName example.com Redirect permanent / https://example.com/ </VirtualHost>
# HTTPS virtual host - no redirect to HTTP <VirtualHost *:443> ServerName example.com SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.crt SSLCertificateKeyFile /etc/ssl/private/example.com.key
# No redirect back to HTTP here </VirtualHost> ```
Step 4: Handle Load Balancer SSL Termination
```nginx # When load balancer terminates SSL, backend receives HTTP # Backend should NOT redirect HTTP to HTTPS
# Backend Nginx configuration (receiving HTTP from LB) server { listen 80; server_name example.com;
# DO NOT redirect to HTTPS - LB already handles SSL # If you have redirect here, remove it
# Trust X-Forwarded-Proto to know original request was HTTPS set $original_proto $http_x_forwarded_proto;
# Only redirect if original request was actually HTTP # But usually LB handles this, backend just serves content } ```
# Load balancer should:
# 1. Terminate SSL (HTTPS from client)
# 2. Forward HTTP to backend
# 3. Backend serves content without redirecting
# 4. LB might add X-Forwarded-Proto: https headerStep 5: Fix Application-Level Redirects
```php // WordPress - check Site URL settings // Settings > General > WordPress Address and Site Address // Both should be https://
// Or in wp-config.php define('WP_HOME', 'https://example.com'); define('WP_SITEURL', 'https://example.com');
// If app code redirects, find and fix: // Search for redirect logic grep -r "http://" /var/www/html/ grep -r "redirect" /var/www/html/app/ ```
```python # Django - correct SSL settings SECURE_SSL_REDIRECT = True # Only if handling SSL directly SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') # If behind proxy
# Don't set SECURE_SSL_REDIRECT = True if proxy handles SSL ```
```ruby # Rails - force_ssl configuration # In config/environments/production.rb config.force_ssl = true # Only if Rails handles SSL directly
# If behind load balancer: config.force_ssl = false # Let LB handle SSL # Or use proper proxy headers ```
Step 6: Fix CDN Configuration
```bash # Cloudflare - SSL/TLS settings # Check SSL/TLS > Overview # Mode should be "Full" or "Full (Strict)" # "Flexible" can cause loops if origin redirects HTTP->HTTPS
# "Flexible": Cloudflare -> HTTP to origin # If origin redirects HTTP->HTTPS, loop occurs
# "Full": Cloudflare -> HTTPS to origin # Origin needs valid certificate, no redirect needed
# "Full (Strict)": Origin needs valid trusted certificate ```
Step 7: Clear Browser HSTS Cache
```bash # If HSTS cached, browser always tries HTTPS # If HTTPS has issues, can contribute to loop perception
# Chrome: chrome://net-internals/#hsts # Delete domain from HSTS cache
# Firefox: about:preferences > Privacy > Clear Site Data # Or clear all browser data
# Safari: Clear browser history and data ```
Step 8: Test After Fixing
```bash # Test redirect chain curl -L -v http://example.com 2>&1 | grep -E "< HTTP|< Location"
# Should see: # < HTTP/1.1 301 Moved Permanently # < Location: https://example.com/ # < HTTP/2 200 # (ends here, no loop)
# Test direct HTTPS curl -I https://example.com # Should return 200, not redirect
# Browser test # Visit http://example.com - should go to HTTPS and stay ```
Common Pitfalls
- Redirect at both web server and application level
- Load balancer SSL termination with backend still redirecting
- CDN "Flexible" SSL mode with HTTPS-origin redirect
- WordPress site URL set to HTTP after moving to HTTPS
- Not coordinating SSL handling across all layers
- HSTS preload forcing HTTPS with broken HTTPS config
Best Practices
- Handle SSL/HTTPS redirects at ONE layer only
- Use X-Forwarded-Proto header for proxy setups
- Test redirect chain before going live
- Document which layer handles SSL
- Configure CDN SSL mode correctly for origin setup
- Keep application URL settings synchronized with actual SSL setup
- Clear browser cache when testing redirect changes
Related Issues
- HTTP to HTTPS Redirect Not Working
- SSL HSTS Header Configuration
- SSL Certificate Name Mismatch
- 502 Bad Gateway