A recent Django security
release
added a new configuration option,
ALLOWED_HOSTS.
It’s optional in 1.3 and 1.4, but required in 1.5 setups. The relevant
detail is that when enabled in production Django will throw 500 errors
if the Host header doesn’t match one of the values in ALLOWED_HOSTS.
The good news is that ELB
preserves the Host header on requests it forwards, as it would have to.
The bad news is that it doesn’t set a useful Host header during it’s
health and latency checks which causes them to fail and mark the host as
down. In fact, the two user different values.
The health check uses the instance’s internal IP address which can be
determined and added to the ALLOWED_HOSTS list. The latency check on
the other hand uses another IP address that doesn’t belong to any
instances that I own. My guess would be that it’s the IP address of the
ELB node itself, but I have not had a chance to test/verify that.
So by finding and adding the internal IP address to ALLOWED_HOSTS you
can make the health check happy and at least get your instances serving
requests, but the latency checks will still be 500’ing and there’s no
clean way to fix that.
The health check problem coupled with the PITA of reliably getting the
internal IP address of a host lead me to look for a better solution.
What I ended up on involved munging the Host header if it looks like an
IP address. To do this I added the following my
nginx site config.
set $my_host $host;
# if the host header is an ip address change it to www.mysite.com
# this works around requests coming from ELB with either the instance's
# internal ip address in the case of health checks or an unknown internal
# ip address in the case of latency checks. translating them to a known
# good host header makes django's ALLOWED_HOSTS happy
if ($host ~ "\d+\.\d+\.\d+\.\d+") {
set $my_host "www.mysite.com";
}
location @django {
proxy_set_header Host $my_host;
...
The above initially sets $my_host to the $host header from ELB. It then
checks to see if it looks like a 4-segment IP address. If it does set
$my_host to the default “www.mysite.com.” Finally $my_host, whether
defaulted or overridden, is passed along to Django.