🚀 Executive Summary

TL;DR: Django’s ‘DisallowedHost’ error is a critical security feature preventing HTTP Host header attacks, strictly enforced when `DEBUG=False`. Resolving it involves correctly configuring the `ALLOWED_HOSTS` setting in `settings.py` with explicit domains and IP addresses, and ensuring reverse proxies like Nginx properly forward the original `Host` header to the Django application.

🎯 Key Takeaways

  • The `DisallowedHost` error is a security feature, not a bug, designed to protect against HTTP Host header attacks by validating incoming hostnames.
  • The `ALLOWED_HOSTS` setting in `settings.py` is a whitelist that explicitly defines which domains and IP addresses Django should respond to when `DEBUG=False`.
  • When using a reverse proxy (e.g., Nginx), it’s crucial to configure `proxy_set_header Host $host;` to ensure the original `Host` header from the client’s request is passed to the Django application.

Can people simply help out: https://polsia.com/?ref=7BPULESN

A breakdown of Django’s common ‘DisallowedHost’ error, explaining why it happens and providing three practical solutions, from a quick development fix to a production-ready configuration.

So, Your First Django Deploy is Screaming “DisallowedHost” at You? Been There.

I remember it like it was yesterday. 3 AM, a pot of coffee that had long since gone cold, and a “simple” production deployment for a new internal tool that was anything but. Every time I hit the IP address in my browser, all I got was a stark, unfriendly white page from Django: DisallowedHost at /. The junior dev on my team was panicking, the project manager was sending “any update?” messages, and I was staring at a security feature that was, at that moment, feeling like a personal attack. This error isn’t a bug; it’s a rite of passage. Let’s get you through it.

The “Why”: It’s a Feature, Not a Bug

Before you go commenting out lines of code in a frenzy, let’s take a breath. Django is trying to protect you. This error is a defense against “HTTP Host header attacks.” In simple terms, a bad actor could trick your server into serving your site under a malicious domain, potentially leading to password-reset poisoning or other nasty vulnerabilities. The ALLOWED_HOSTS setting in your settings.py is a simple whitelist. You’re telling Django, “Hey, these are the only domains I expect to be serving traffic for. If a request comes in for any other host, shut it down.” When you’re running locally with DEBUG = True, Django is lenient. The moment you flip that to False for production, the whitelist is strictly enforced.

Three Ways to Fix It: From Quick Hack to Production-Grade

Alright, enough theory. You’re here because your app is down. Let’s look at the options, starting with the one you probably want to use right now and ending with the one your future self will thank you for implementing.

Solution 1: The ‘Get It Working NOW’ Fix (For Dev/Testing ONLY!)

You just want to see your site load. You’re on a private network, or you’re just testing the deployment pipeline and you’re about to tear your hair out. I get it. This is the big red button.

In your settings.py file, find the ALLOWED_HOSTS list and make it look like this:


# settings.py

# ... other settings ...

# WARNING: This is insecure and should NOT be used in a real production environment.
ALLOWED_HOSTS = ['*']

# ... other settings ...

A Word From The Trenches: I cannot stress this enough – using a wildcard (*) completely disables the Host header validation security feature. It’s fine for a quick “is the server even running?” check on an isolated VM, but if you push this to a public-facing server, you are leaving a door wide open. Don’t be that person.

Solution 2: The Production-Ready, “Right Way” Fix

This is the correct, permanent solution. You need to explicitly tell Django which domains it should respond to. In the case of the Reddit post, the domain is polsia.com. You should also include the server’s IP address and any subdomains, like ‘www’.

Your settings.py should look like this:


# settings.py

# ... other settings ...

# The secure and correct way to configure for production
ALLOWED_HOSTS = ['polsia.com', 'www.polsia.com', 'YOUR_SERVER_IP_ADDRESS']

# ... other settings ...

Replace YOUR_SERVER_IP_ADDRESS with the actual public IP of your server (e.g., '45.79.123.45'). Restart your Gunicorn or uWSGI process, and you should be good to go.

Pro Tip: In a real-world CI/CD pipeline, you don’t want to hardcode these values. We pull them from environment variables. Your code might look more like this: ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', '').split(','). This lets you configure different hosts for your staging and production environments without ever changing the code.

Solution 3: The ‘My Reverse Proxy is the Problem’ Fix

Sometimes, the issue isn’t even Django. You’ve set ALLOWED_HOSTS correctly, but you’re still getting the error. This often happens when you’re running Django behind a reverse proxy like Nginx or Apache. The proxy server might not be passing the correct Host header to your application server (Gunicorn/uWSGI).

Your Django app only sees the headers it’s given. If Nginx is passing a local hostname like `localhost` or `127.0.0.1` as the Host, Django will reject it. You need to ensure your proxy configuration properly forwards the original host.

For Nginx, make sure you have this line in your location block:


# /etc/nginx/sites-available/polsia.com

server {
    # ... other server config ...

    location / {
        # ... other location config ...
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}

The key line is proxy_set_header Host $host;. This tells Nginx to pass along the original host header from the user’s browser request. After adding this and verifying your ALLOWED_HOSTS in Solution 2 is correct, a quick sudo systemctl restart nginx should solve the problem.

So there you have it. It’s a simple fix, but the reasoning behind it is crucial for building secure and robust applications. Now go get that deployment finished.

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ Why does Django show a ‘DisallowedHost’ error in production?

Django shows a ‘DisallowedHost’ error in production (when `DEBUG=False`) because the incoming request’s `Host` header does not match any entry in the `ALLOWED_HOSTS` list in your `settings.py`, which is a security measure against HTTP Host header attacks.

âť“ How does `ALLOWED_HOSTS` configuration compare to using a wildcard?

Configuring `ALLOWED_HOSTS` with explicit domains and IP addresses is the secure, production-ready method, enabling Django’s Host header validation. Using a wildcard (`ALLOWED_HOSTS = [‘*’]`) completely disables this security feature, making your application vulnerable to HTTP Host header attacks and should only be used for isolated development or testing.

âť“ What is a common implementation pitfall when fixing the `DisallowedHost` error?

A common pitfall is failing to configure your reverse proxy (e.g., Nginx) to correctly forward the original `Host` header to Django. Even if `ALLOWED_HOSTS` is correct, if the proxy passes a local hostname like `localhost` or `127.0.0.1`, Django will still reject the request. The solution is to add `proxy_set_header Host $host;` in the proxy’s configuration.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading