X-Forwarded-Proto loop critical
HTTPS Redirect Loop Behind a Proxy / Load Balancer
TLS terminates at the proxy so the backend sees HTTP and its force-HTTPS rule fires on every request, looping.
What you see
ERR_TOO_MANY_REDIRECTS The page isn't redirecting properly Firefox has detected that the server is redirecting the request for this address in a way that will never complete.
What’s actually happening
Right after putting the site behind a load balancer, CDN, or ngrok, every HTTPS URL loops. curl -IL https://site shows an endless chain of 301s all pointing to the same https:// URL. The backend logs each hit as http (scheme is plain), even though the visitor's bar says https.
Common causes
- The LB/CDN (AWS ALB, Cloudflare, Heroku router) terminates TLS and forwards plain HTTP to the origin, which then 'upgrades' to HTTPS again.
- App framework reads the raw connection scheme instead of X-Forwarded-Proto (Django SECURE_SSL_REDIRECT without SECURE_PROXY_SSL_HEADER, Rails force_ssl, Laravel without trustProxies).
- Nginx 'if ($scheme = http) return 301 https://...' on a backend that only ever receives http from the proxy.
- Proxy isn't sending X-Forwarded-Proto at all, or the app doesn't trust it because the proxy IP isn't in the trusted list.
- Cloudflare SSL mode set to 'Flexible', which always talks HTTP to the origin while serving HTTPS to the browser.
How to fix it
- Confirm the proxy sends the schemeLog or echo X-Forwarded-Proto on an inbound request. If it's missing, configure the proxy to add it (Nginx: proxy_set_header X-Forwarded-Proto $scheme). If it's present and says https, the bug is on the app side.
- Make the force-HTTPS rule honor the headerRedirect only when X-Forwarded-Proto is http. Django: SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO','https'). Rails/Laravel: trust the proxy. Nginx: if ($http_x_forwarded_proto = 'http') return 301 https://$host$request_uri;
- Fix Cloudflare SSL modeSwitch the SSL/TLS encryption mode from Flexible to Full (or Full strict) so the edge speaks HTTPS to your origin. Flexible is the single most common cause of this loop on Cloudflare.
- Only trust forwarded headers from your proxyWhitelist the LB/CDN source ranges as trusted proxies so an attacker can't spoof X-Forwarded-Proto. Then enable the header-aware redirect.
Stop it recurring
Set X-Forwarded-Proto at the proxy and make every force-HTTPS rule read it instead of the raw socket scheme.
Related errors