Skip to content

Troubleshooting: ALLOWED_HOSTS & 400 Errors

The Problem

Django's ALLOWED_HOSTS validation rejects requests with unrecognized Host headers, returning 400 Bad Request. This manifests as "Bad Request (400)" pages or DisallowedHost errors in logs.

Common Triggers

Scenario Header Fix
Caddy proxies from messages.<domain> Host: messages.<domain> Add messages.<domain> to ALLOWED_HOSTS
Caddy proxies from calendars.<domain> Host: calendars.<domain> Add calendars.<domain> to ALLOWED_HOSTS
CalDAV callbacks from 172.29.0.30:8000 Host: 172.29.0.30:8000 Add 172.29.0.30 to ALLOWED_HOSTS
CalDAV direct access from Docker Host: caldav Add caldav to ALLOWED_HOSTS
Caddy internal proxy test Host: messages-frontend-1:8080 Add messages-frontend-1 or use static IP

Required ALLOWED_HOSTS

Messages

DJANGO_ALLOWED_HOSTS=messages.<domain>,api.messages.<domain>,backend,localhost,127.0.0.1

The host messages.<domain> is critical — without it, ALL proxied requests through Caddy fail.

Calendars

DJANGO_ALLOWED_HOSTS=calendars.<domain>,api.calendars.<domain>,backend,caldav,172.29.0.30,localhost,127.0.0.1

The IP 172.29.0.30 is critical — without it, CalDAV scheduling callbacks fail.

Debugging

Check the backend logs:

docker compose logs backend | grep "DisallowedHost\|Invalid HTTP_HOST"

This shows exactly which Host header was rejected. Add that value to DJANGO_ALLOWED_HOSTS.

SECURE_SSL_REDIRECT

The Production configuration enables SECURE_SSL_REDIRECT=True. When Caddy proxies requests, it must send X-Forwarded-Proto: https so Django knows the original request was HTTPS and doesn't redirect. The Caddyfile includes this header.

Without this header, Django redirects HTTP requests to HTTPS, but localhost:8000 has no HTTPS listener, causing connection failures.