DisallowedHost at / Invalid HTTP_HOST header
Put your domains and free address in ALLOWED_HOSTS from a variable, and give our health check its own path, so Django's host check never fails a deploy.
Updated 17 Oct 20262 min read
The fix on Tanzahost: two things, because two kinds of request reach your Django app.
Your visitors arrive with your domain and your free address as their host. List both in
ALLOWED_HOSTS, read from a variable.Our health check asks your app on its private address, so its host is that private address and port 8080, which can change on every deploy and can't be listed in advance. Answer it before Django's host check, on a path of its own.
In settings.py:
import os
ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",")
CSRF_TRUSTED_ORIGINS = ["https://" + h for h in ALLOWED_HOSTS if h]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")On your project's Variables tab, add ALLOWED_HOSTS for your app, with every address, separated by commas and no spaces. For Asha:
ALLOWED_HOSTS=duka.co.tz,www.duka.co.tz,duka-7h3k.tanzahost.coThen add a small piece of middleware that answers the health check, in a new file shop/health.py (use your own project's folder):
from django.http import HttpResponse
class HealthCheck:
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)Put it first in MIDDLEWARE, above Django's own:
MIDDLEWARE = [
"shop.health.HealthCheck",
"django.middleware.security.SecurityMiddleware",
# … the rest, as before
]Last, open the app. Under Build and run, set Health address to /healthz, and choose Save and build.
Why it happens
Django refuses any request whose Host header isn't in ALLOWED_HOSTS, and with DEBUG=False it answers "Bad Request (400)". That protects you from forged host names. On Tanzahost two kinds of request come in:
| Request | Its host |
|---|---|
| A visitor's | The address they typed: your domain, or your free address on tanzahost.co. Our front door only sends your app visits for its own addresses. |
| Our health check, on every deploy | Your app's private address and port 8080 |
Before a new version gets visitors, we ask its health address, / unless you choose another, until it answers with a status from 200 to 399. Django's 400 fails that check, and the deploy says "The new version didn't answer its health address in 120 seconds. It answered 400 at /." Your visitors keep the version before, so nothing breaks, but nothing new goes live either. The middleware answers before Django reads the host, and only on /healthz: every other path keeps the full check.
How to check
The deploy's message: "It answered 400" means Django refused the host.
Your app's logs list each visit with its status. Visits answered 400 after a deploy went live mean a visitor's address is missing from the
ALLOWED_HOSTSvariable. WithDEBUG=False, Django doesn't print the refused host by itself.Adding a domain later? Add it to the variable too, then Redeploy.