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¶
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:
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.