Python Django: How to Resolve "DisallowedHost at / Invalid HTTP_HOST Header" in Python Django
When deploying a Django application, or even running one locally with a non-standard configuration, you may encounter the error DisallowedHost at / Invalid HTTP_HOST header. This is Django's security mechanism preventing your application from responding to requests with unrecognized hostnames.
In this guide, we'll explain why this error occurs, show you exactly how to fix it, and cover best practices to keep your application secure while resolving the issue.
What Does This Error Look Like?
When this error is triggered, Django displays a page like this (in debug mode):
DisallowedHost at /
Invalid HTTP_HOST header: 'your-domain.com'. You may need to add 'your-domain.com' to ALLOWED_HOSTS.
In production (with DEBUG = False), visitors will see a generic 400 Bad Request error instead.
Why Does This Error Occur?
Django includes a security feature called ALLOWED_HOSTS that validates the Host header of every incoming HTTP request. This prevents HTTP Host header attacks, where an attacker sends a request with a forged hostname to exploit your application.
The error occurs when the hostname in the incoming request doesn't match any entry in the ALLOWED_HOSTS list in your settings.py file. Common triggers include:
- Deploying to a server without updating
ALLOWED_HOSTSfrom the default empty list. - Using a reverse proxy (like Nginx or Apache) that forwards requests with a different hostname.
- Accessing via IP address (e.g.,
192.168.1.100) when only domain names are listed. - Running locally on a port other than the default, or accessing via
0.0.0.0. - Setting
DEBUG = False: Django enforcesALLOWED_HOSTSstrictly when debug mode is off.
When DEBUG = True and ALLOWED_HOSTS is empty, Django automatically allows localhost, 127.0.0.1, and [::1]. Once you set DEBUG = False, you must configure ALLOWED_HOSTS explicitly.
How to Fix It
Step 1: Open Your settings.py File
The ALLOWED_HOSTS setting is located in your project's main settings file, typically at myproject/settings.py.
Step 2: Add Your Hostnames
Add every domain name, subdomain, and IP address that your application should respond to:
# myproject/settings.py
ALLOWED_HOSTS = [
'www.example.com', # Your primary domain
'example.com', # Domain without www
'192.168.1.100', # Server IP address (if accessed directly)
'localhost', # Local development
'127.0.0.1', # Loopback address
]
Step 3: Save and Restart Your Server
After modifying ALLOWED_HOSTS, restart your Django server for the changes to take effect:
# Development server
python manage.py runserver
# Gunicorn
sudo systemctl restart gunicorn
# Or if running directly
gunicorn myproject.wsgi:application --reload
Configuration Options Explained
Specific Domain Names
List each domain and subdomain explicitly. This is the most secure approach:
ALLOWED_HOSTS = [
'example.com',
'www.example.com',
'api.example.com',
]
Wildcard Subdomains
To allow all subdomains of a domain, prefix the domain with a dot:
ALLOWED_HOSTS = [
'.example.com', # Matches example.com, www.example.com, api.example.com, etc.
]
This is equivalent to listing every possible subdomain individually.
IP Addresses
If your server is accessed directly by IP (common during development or on internal networks), include the IP:
ALLOWED_HOSTS = [
'10.0.0.5',
'192.168.1.100',
]
Allow All Hosts (Wildcard *)
Using '*' allows requests from any hostname:
ALLOWED_HOSTS = ['*']
Never use '*' in production. This disables Host header validation entirely, making your application vulnerable to HTTP Host header attacks. Only use this for quick local testing or in environments where another layer (like a reverse proxy) handles host validation.
Environment-Specific Configuration
In real projects, your ALLOWED_HOSTS should differ between development and production. Here are two common patterns:
Using Environment Variables
import os
ALLOWED_HOSTS = os.environ.get('DJANGO_ALLOWED_HOSTS', 'localhost,127.0.0.1').split(',')
Then set the environment variable on your server:
# In your .env file or server configuration
export DJANGO_ALLOWED_HOSTS="example.com,www.example.com,192.168.1.100"
Using Separate Settings Files
# settings/base.py
ALLOWED_HOSTS = []
# settings/development.py
from .base import *
ALLOWED_HOSTS = ['localhost', '127.0.0.1']
# settings/production.py
from .base import *
ALLOWED_HOSTS = ['example.com', 'www.example.com']
Common Scenarios and Fixes
Behind a Reverse Proxy (Nginx, Apache)
When using a reverse proxy like Nginx in front of Gunicorn or uWSGI, the proxy forwards requests to Django. You need to ensure the Host header is passed correctly and the hostname is in ALLOWED_HOSTS.
Nginx configuration:
server {
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Django settings:
ALLOWED_HOSTS = ['example.com', 'www.example.com']
Docker Containers
In Docker environments, you may access Django via the container's IP or a Docker network hostname:
ALLOWED_HOSTS = [
'localhost',
'127.0.0.1',
'0.0.0.0', # Common in Docker
'web', # Docker service name
'example.com', # Production domain
]
Elastic Beanstalk, Heroku, or Cloud Platforms
Cloud platforms assign dynamic hostnames. You'll typically need to include both the platform's hostname and your custom domain:
# Heroku example
ALLOWED_HOSTS = [
'your-app-name.herokuapp.com',
'example.com',
'www.example.com',
]
Debugging Tips
If you've added the hostname but the error persists, try these steps:
1. Check the exact hostname in the error message:
The error tells you precisely which host header was received. Copy it exactly as shown:
Invalid HTTP_HOST header: 'example.com:8080'
In this case, the port is included. You may need to add the hostname with the port, or configure your proxy to strip it.
2. Print ALLOWED_HOSTS to verify your settings loaded correctly:
# Temporarily add to your view or settings
print(f"ALLOWED_HOSTS = {ALLOWED_HOSTS}")
3. Check for typos and extra whitespace:
# ❌ Common mistake: extra space
ALLOWED_HOSTS = ['example.com ', ' www.example.com']
# ✅ Correct: no extra whitespace
ALLOWED_HOSTS = ['example.com', 'www.example.com']
4. Ensure you're editing the right settings file:
If your project uses multiple settings files (e.g., settings/production.py), make sure you're editing the one that's actually active:
echo $DJANGO_SETTINGS_MODULE
Quick Reference
| Scenario | ALLOWED_HOSTS Value |
|---|---|
| Local development | ['localhost', '127.0.0.1'] |
| Single domain | ['example.com', 'www.example.com'] |
| All subdomains | ['.example.com'] |
| IP access | ['192.168.1.100'] |
| Docker | ['0.0.0.0', 'localhost', 'web'] |
| Behind reverse proxy | Domain names that Nginx/Apache forwards |
| Testing only (insecure) | ['*'] |
Conclusion
The DisallowedHost at / Invalid HTTP_HOST header error is Django's built-in protection against HTTP Host header attacks.
The fix is straightforward: add the correct domain names, subdomains, and IP addresses to the ALLOWED_HOSTS list in your settings.py file, then restart your server.
For production deployments, always list specific hostnames rather than using the wildcard '*', and consider using environment variables to manage different configurations across development, staging, and production environments.