How to Properly Configure Nginx SSL Reverse Proxy for Gunicorn: Fixing Redirect Loops and 502 Bad Gateway Errors


2 views

The common pitfall when setting up Gunicorn behind Nginx with SSL is dealing with redirect loops and 502 Bad Gateway errors. These typically occur due to misconfigured proxy headers or incorrect SSL termination.

Here's a working configuration that handles SSL termination at Nginx while properly forwarding requests to Gunicorn:

server {
    listen 443 ssl;
    server_name www.yourdomain.com;
    
    ssl_certificate /path/to/your/certificate.crt;
    ssl_certificate_key /path/to/your/private.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    
    location / {
        proxy_pass http://0.0.0.0:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }
}

server {
    listen 80;
    server_name www.yourdomain.com;
    return 301 https://$host$request_uri;
}

When running Gunicorn behind Nginx with SSL, you should:

  1. Run Gunicorn without SSL (Nginx handles SSL termination)
  2. Bind to 0.0.0.0:8000 (not localhost)
  3. Set proper forwarded headers

Sample Gunicorn command:

gunicorn --bind 0.0.0.0:8000 --workers 4 --forwarded-allow-ips="*" your_app:app

The redirect loop issue typically occurs when:

  • Nginx is not properly forwarding the X-Forwarded-Proto header
  • Gunicorn is not configured to trust forwarded headers
  • There's a mismatch between Host headers

The 502 Bad Gateway usually means:

  • Gunicorn isn't running or isn't accessible from Nginx
  • Port or IP binding is incorrect
  • Firewall blocking the connection

For better performance and security, consider these additions to your Nginx config:

proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffers 8 32k;
proxy_buffer_size 64k;
proxy_read_timeout 90;
proxy_send_timeout 90;
keepalive_timeout 90;

When deploying Python web applications, many developers encounter issues with Nginx SSL termination and Gunicorn proxying. The error patterns typically manifest as either:

  • Endless redirect loops when accessing the site
  • 502 Bad Gateway errors when Gunicorn runs with HTTPS
  • Cryptic worker crashes like the AttributeError in your traceback

Your current Nginx configuration has several good elements but needs refinement. Let's examine a corrected version:

server {
    listen 80;
    listen 443 ssl;
    
    server_name www.pyhub.co pyhub.co;
    
    ssl_certificate /etc/ssl/pyhub_crt.crt;
    ssl_certificate_key /etc/ssl/pyhub.key;
    
    # Strong SSL configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384...';
    
    # Security headers
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
    
    location / {
        proxy_pass http://0.0.0.0:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        proxy_buffering off;
        proxy_redirect off;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

The key adjustments that resolve both redirect loops and 502 errors:

  1. Never run Gunicorn with HTTPS: The SSL termination should only happen at Nginx level
  2. Proper header forwarding: X-Forwarded-Proto is essential for Django/Flask apps
  3. Connection handling: The proxy_http_version and Connection settings prevent worker crashes

Run Gunicorn with these recommended parameters:

gunicorn --bind 0.0.0.0:8000 \
         --workers 3 \
         --threads 2 \
         --timeout 120 \
         --keep-alive 5 \
         --access-logfile - \
         --error-logfile - \
         your_app.wsgi:application

If problems persist, check these aspects:

  • Verify SELinux/AppArmor isn't blocking the connection (check /var/log/audit/audit.log)
  • Test the direct Gunicorn connection: curl http://0.0.0.0:8000
  • Check Nginx error logs with tail -f /opt/bitnami/nginx/logs/error.log
  • Ensure no firewall rules block port 8000: sudo ufw allow 8000

For high-traffic sites, consider these optimizations:

upstream gunicorn_app {
    server 0.0.0.0:8000 fail_timeout=0;
    # Add more servers here for load balancing
    keepalive 32;
}

server {
    # ... previous config ...
    
    location / {
        proxy_pass http://gunicorn_app;
        proxy_connect_timeout 75s;
        proxy_send_timeout 300s;
        proxy_read_timeout 300s;
        proxy_buffer_size 16k;
        proxy_buffers 4 32k;
    }
}