Skip to content
Docs

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.

  1. Your visitors arrive with your domain and your free address as their host. List both in ALLOWED_HOSTS, read from a variable.

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

python
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:

Terminal
ALLOWED_HOSTS=duka.co.tz,www.duka.co.tz,duka-7h3k.tanzahost.co

Then add a small piece of middleware that answers the health check, in a new file shop/health.py (use your own project's folder):

python
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:

python
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:

RequestIts host
A visitor'sThe 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 deployYour 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

  1. The deploy's message: "It answered 400" means Django refused the host.

  2. 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_HOSTS variable. With DEBUG=False, Django doesn't print the refused host by itself.

  3. Adding a domain later? Add it to the variable too, then Redeploy.