事象
独自ドメインや新しいホスト名でDjangoアプリケーションにアクセスしたところ、画面上に「Bad Request (400)」が表示され、サーバー側のログに次のエラーが出力された。
Invalid HTTP_HOST header: 'example.com'. You may need to add 'example.com' to ALLOWED_HOSTS.
Traceback (most recent call last):
...
django.core.exceptions.DisallowedHost: Invalid HTTP_HOST header: 'example.com'. You may need to add 'example.com' to ALLOWED_HOSTS.
環境
- Python 3.x
- Django 3.x / 4.x / 5.x
- Nginx(リバースプロキシ)
- Gunicorn / uWSGI / Docker
原因
主な原因は次の2つである。
1. ALLOWED_HOSTS にアクセス元のホスト名が登録されていない
DjangoはHTTP Hostヘッダーインジェクション攻撃を防ぐため、settings.py の ALLOWED_HOSTS に登録されていないホスト名からのリクエストをすべて拒否する仕様になっている。
本番環境で独自ドメインを割り当てた直後や、ロードバランサー・リバースプロキシを経由させた際に追加を忘れていると発生する。
2. ALLOWED_HOSTS の書き方が誤っている(URL全体を指定している)
ALLOWED_HOSTS にはホスト名(FQDN)またはIPアドレスのみを指定する必要がある。
よくある間違いとして、プロトコル(http:// や https://)や末尾のスラッシュ、ポート番号を含めてしまうケースがある。
# ❌ よくある間違い
ALLOWED_HOSTS = [
"https://example.com/", # スキームや末尾スラッシュは不可
"http://example.com", # スキームは不可
]
これらはドメイン名として正しくパースされず、マッチしないためエラーが解消しない。
対策
1. settings.py の ALLOWED_HOSTS を正しく設定する
スキームやスラッシュを除いたホスト名のみを配列に設定する。
# settings.py
ALLOWED_HOSTS = [
"example.com",
"www.example.com",
"127.0.0.1", # 内部通信やヘルスチェック用
"localhost",
]
任意のサブドメインを許可したい場合は、先頭にピリオドを付けるかワイルドカードを利用する。
# .example.com と書くと example.com および そのすべてのサブドメインにマッチする
ALLOWED_HOSTS = [
".example.com",
]
2. リバースプロキシ(Nginx)の Host ヘッダー転送を確認する
Nginxなどを経由している場合、Nginx側でクライアントがリクエストしたホスト名をDjangoにそのまま渡すよう設定されているか確認する。
# nginx.conf
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $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;
}
3. Djangoアプリケーションを再起動する
settings.py の変更は、Djangoプロセス(Gunicorn/uWSGI等)を再起動しないと反映されない。Nginxのリロードだけでは反映されない点に注意する。
# Gunicornを手動/systemdで動かしている場合
sudo systemctl restart gunicorn
# Docker Compose環境の場合
docker compose restart web
4. 動作確認
ブラウザまたは curl からアクセスし、正常なステータスコード(200など)が返ることを確認する。
curl -I https://example.com/
エラーログに DisallowedHost が出力されなくなっていれば対応完了である。