# Fix Nginx HTTP to HTTPS Rewrite Causing Infinite Redirect Loop

You configure Nginx to redirect all HTTP traffic to HTTPS, but browsers report ERR_TOO_MANY_REDIRECTS. The redirect chain loops:

bash
http://example.com/page
-> 301 https://example.com/page
-> 301 http://example.com/page
-> 301 https://example.com/page
-> ... (loops forever)

The server is redirecting HTTPS back to HTTP, creating an infinite loop. This is a surprisingly common configuration error with several possible causes.

Cause 1: Rewrite Rule in the Wrong Server Block

```nginx server { listen 80; listen 443 ssl; server_name example.com;

if ($scheme = http) { return 301 https://$host$request_uri; } } ```

While the if condition checks for HTTP, this pattern is fragile and often breaks with more complex configurations.

Fix: Use separate server blocks:

```nginx server { listen 80; server_name example.com; return 301 https://$host$request_uri; }

server { listen 443 ssl; server_name example.com; # SSL and proxy configuration here } ```

Cause 2: Load Balancer Stripping HTTPS

If Nginx sits behind a load balancer (AWS ELB, Cloudflare, etc.), the load balancer terminates TLS and forwards plain HTTP to Nginx. Nginx sees $scheme = http and redirects to HTTPS, which the load balancer again terminates as HTTP.

Fix: Check the X-Forwarded-Proto header:

```nginx server { listen 80; server_name example.com;

if ($http_x_forwarded_proto = "https") { # Request already came via HTTPS through the load balancer location / { proxy_pass http://backend; } }

if ($scheme = "http" AND $http_x_forwarded_proto != "https") { return 301 https://$host$request_uri; } } ```

Or more cleanly, use a map:

```nginx map $http_x_forwarded_proto $redirect_to_https { default 0; http 1; "" 1; }

server { listen 80; server_name example.com;

if ($redirect_to_https) { return 301 https://$host$request_uri; }

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

Cause 3: Application-Level Redirect

The backend application itself may be redirecting HTTPS to HTTP. For example, Django with incorrect SECURE_PROXY_SSL_HEADER:

```python # WRONG: SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'http')

# CORRECT: SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') ```

Debugging the Redirect Chain

Use curl to follow the redirect chain:

bash
curl -vIL http://example.com/page 2>&1 | grep -E "< HTTP|< Location"

This shows every HTTP status code and Location header in the chain. If you see alternating 301 redirects between http and https, you have confirmed the loop.

You can also check what Nginx sees:

```nginx server { listen 80; server_name example.com;

location /debug { return 200 "scheme=$scheme x_forwarded_proto=$http_x_forwarded_proto host=$host\n"; add_header Content-Type text/plain; } } ```

bash
curl http://example.com/debug
# Shows what Nginx sees for the incoming request

This reveals whether the request arrives as HTTP or HTTPS from Nginx's perspective, which is the key to diagnosing redirect loops.