Back to Blog

DisallowedHost at /: How to Fix ALLOWED_HOSTS in Django

admin
August 7, 2026 3 min read
38 views
You deployed, opened the site, and Django returned a 400 with DisallowedHost. Here is what the error actually means, why it exists, and the three ways people get it wrong.

DisallowedHost at /: How to Fix ALLOWED_HOSTS in Django

You deploy, you open the site, and instead of your homepage you get a bare 400 page. In the logs:

Invalid HTTP_HOST header: 'example.com'. You may need to add 'example.com' to ALLOWED_HOSTS.

It is one of the first errors most people meet in production, and the fix takes ten seconds. The
reason it exists is worth two minutes of your time, because the obvious workaround is the wrong one.

What the error means

Django reads the Host header of every request and compares it against ALLOWED_HOSTS. If the
header is not in the list, the request is rejected before your view is ever called.

The header is supplied by the client, and a client can put anything in it. Without this check, an
attacker can send a request to your server with a Host header pointing at their own domain. Any
absolute URL your application generates — a password reset link, an email confirmation — is then
built with their hostname. The user receives a genuine email from you containing a link to a site
controlled by someone else.

ALLOWED_HOSTS is the defence against that. It is not bureaucracy; it is the reason your password
reset emails cannot be redirected.

The fix

ALLOWED_HOSTS = ["example.com", "www.example.com"]

List every hostname the site legitimately answers on. If you serve both the apex and the www
variant, both belong in the list. Restart the application afterwards — this is a settings value read
at startup.

Three ways people get it wrong

Using a wildcard. ALLOWED_HOSTS = ["*"] makes the error disappear and disables the protection
entirely. It shows up constantly in tutorials and Docker examples, and it is only defensible when
something in front of your application already validates the hostname and you know that for a fact.

Adding the IP address instead of the name. This works while you are testing directly against the
server and stops working the moment traffic arrives through your domain. Add the hostname.

Forgetting the health check. Load balancers and container orchestrators often request the
application by internal IP with no Host header your list recognises. The site works fine for
users while the platform reports it as unhealthy. Either give the checker a hostname it can send, or
add the internal address explicitly.

When you are behind a proxy

If a reverse proxy sits in front of Django, the hostname Django sees depends on what the proxy
forwards. The proxy must pass the original header through:

proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;

Without the first line, Django sees the proxy's own idea of the host and rejects everything. And if
you enable USE_X_FORWARDED_HOST, be certain that only your proxy can reach the application —
otherwise you have handed the header back to the client and undone the protection.

Getting the value from the environment

Hardcoding hostnames means editing code to deploy to a new environment. Read them from
configuration instead:

ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", default=[])
ALLOWED_HOSTS=example.com,www.example.com

A useful side effect: an empty list in production fails loudly and immediately, rather than quietly
accepting everything.

Checking it

curl -s -o /dev/null -w "%{http_code}\n" -H "Host: example.com" http://127.0.0.1:8000/   # 200
curl -s -o /dev/null -w "%{http_code}\n" -H "Host: evil.example" http://127.0.0.1:8000/  # 400

The second command returning 400 is the whole point. If it returns 200, your list is too permissive.


Deploying properly involves a handful of these settings, each protecting against something specific.
Our tutorial Migrating a Live Django Application to a New
VPS
walks through the full set in the order
you need them.

Comments (0)

Please login to leave a comment.

No comments yet. Be the first to comment!