All posts

Fix Load Balancer Error in Django

Troubleshoot Django errors behind load balancers including ALLOWED_HOSTS, CSRF, SECURE_PROXY_SSL_HEADER, and health check setup.

Django Behind a Load Balancer

Django is opinionated about security, which means load balancers need careful configuration. The classic symptoms are 400 Bad Request, CSRF validation failures, and redirect loops.

ALLOWED_HOSTS

Django rejects requests where the Host header doesn't match ALLOWED_HOSTS:

# settings.py
ALLOWED_HOSTS = [
    'myapp.example.com',
    'internal-alb-1234.us-east-1.elb.amazonaws.com',
    'localhost',  # for health checks hitting the pod IP
]

SSL Termination and Redirect Loops

The load balancer handles SSL, forwarding HTTP to Django. But Django's SECURE_SSL_REDIRECT sees HTTP and redirects — creating an infinite loop:

# Trust the X-Forwarded-Proto header from the LB
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
USE_X_FORWARDED_HOST = True
USE_X_FORWARDED_PORT = True

CSRF Behind a Proxy

Django 4.0+ requires CSRF_TRUSTED_ORIGINS:

CSRF_TRUSTED_ORIGINS = [
    'https://myapp.example.com',
]

Health Check Endpoint

# urls.py
from django.http import HttpResponse

def health_check(request):
    return HttpResponse("ok")

urlpatterns = [
    path('healthz/', health_check),
    # ... other urls
]

Exempt it from middleware that might interfere:

class HealthCheckMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        if request.path == '/healthz/':
            return HttpResponse('ok')
        return self.get_response(request)

Bugsly tracks those 400 and 403 errors in Django with full request metadata, making it straightforward to distinguish between misconfigured proxies and genuine attacks.

Try Bugsly Free

Track up to 100 issues per month on the free plan, with unlimited events and no credit card required.

Get Started Free