Many Django developers on AWS Elastic Beanstalk encounter this frustrating scenario:
DisallowedHost at //www/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
Invalid HTTP_HOST header: 'xx.xxx.xx.xx'. You may need to add 'xx.xxx.xxx.xx' to ALLOWED_HOSTS.
These occur when bots scan your server's IP address directly rather than using your domain name, triggering Django's security checks.
In an AWS Elastic Beanstalk environment with Nginx:
- Requests first hit the load balancer
- Nginx acts as a reverse proxy
- Your Django application runs on localhost
The default Nginx configuration allows direct IP access, which we need to modify.
The most effective approach is to configure Nginx with:
- A default server block that drops non-domain requests
- A named server block that handles your legitimate domain traffic
Implementation Steps
Create a new configuration file in your .platform/nginx/conf.d/
directory (for Elastic Beanstalk):
# .platform/nginx/conf.d/domain_filter.conf
server {
listen 80 default_server;
server_name _;
return 444;
}
server {
listen 80;
server_name example.com *.example.com;
# Rest of your existing configuration
include conf.d/elasticbeanstalk/*.conf;
}
For dynamic subdomains, use these patterns:
server_name ~^(www\.)?example\.com$ ~^(.+)\.example\.com$;
This regex pattern will match:
- example.com
- www.example.com
- any-subdomain.example.com
1. Health Checks: Ensure AWS health checks still work:
location /health {
access_log off;
return 200;
}
2. SSL Termination: If using HTTPS, update both server blocks:
server {
listen 443 ssl default_server;
server_name _;
ssl_certificate /path/to/cert;
ssl_certificate_key /path/to/key;
return 444;
}
Here's a full example combining all elements:
# .platform/nginx/conf.d/secure_domain.conf
server {
listen 80 default_server;
listen 443 ssl default_server;
server_name _;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
return 444;
}
server {
listen 80;
server_name ~^(www\.)?example\.com$ ~^(.+)\.example\.com$;
# Redirect HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name ~^(www\.)?example\.com$ ~^(.+)\.example\.com$;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Your existing proxy configuration
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /health {
access_log off;
return 200;
}
}
After deployment:
- Verify domain access works normally
- Test that direct IP access returns no response (connection closed)
- Check health check endpoints still function
- Monitor error logs for any legitimate traffic being blocked
When running Django applications on AWS Elastic Beanstalk, you'll often encounter bots scanning for vulnerabilities by making direct requests to your server's IP address. This generates numerous errors like:
DisallowedHost at //www/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php
Invalid HTTP_HOST header: 'xx.xxx.xx.xx'. You may need to add 'xx.xxx.xxx.xx' to ALLOWED_HOSTS.
AWS Elastic Beanstalk provides a default Nginx configuration that needs modification to properly handle domain-based filtering. The key files are:
/etc/nginx/nginx.conf
.platform/nginx/conf.d/elasticbeanstalk/00_application.conf
We'll implement a solution that:
- Blocks all requests not using your domain
- Works with wildcard subdomains
- Maintains ELB health check functionality
# Add this to your .platform/nginx/conf.d/elasticbeanstalk/00_application.conf
server {
listen 80 default_server;
server_name _;
return 444;
}
server {
listen 80;
server_name mydomain.com *.mydomain.com;
# Rest of your existing configuration
location / {
# Your existing location block
}
location = /health-check.html {
# Your existing health check configuration
}
}
The solution works by:
- Creating a default server block that drops connections (444)
- Creating a specific server block for your domains that processes legitimate traffic
Key points to note:
- The default server block uses
return 444
which immediately closes the connection - The domain-specific block matches both your root domain and all subdomains
- Health checks continue to work as they match the specific location block
After deploying, test with:
curl -I http://yourdomain.com
curl -I http://[your-server-ip]
The first should return normal headers, while the second should fail to connect.
For enhanced security, consider adding:
# Block requests with invalid Host headers
if ($host !~* ^(mydomain.com|www.mydomain.com|.*\.mydomain.com)$ ) {
return 444;
}