← 中文资源

简体中文版本

Django DisallowedHost — HTTP_HOST 头非法

通过配置 ALLOWED_HOSTS 并理解代理转发的 host 头,修复 Django 的 DisallowedHost: Invalid HTTP_HOST header。

阅读英文原文

症状

本篇指南处理“Django DisallowedHost — HTTP_HOST 头非法”。请从症状出发,按顺序执行诊断,在推进前确认每个假设;随后通过修复与验证步骤闭环。

可观察到的症状包括:

  • Django 返回 400 并提示 “DisallowedHost: Invalid HTTP_HOST header: ‘…’”
  • 本地正常,置于负载均衡后却返回 400
  • 报错 host 含内部 IP 或健康检查名称

诊断

先在隔离环境中复现故障,再按顺序执行以下检查,逐步缩小范围。

步骤 1

curl -i https://app.example.com -H 'Host: internal-lb:8000'

400 消息会回显不被允许的确切 host,指导添加到白名单。

步骤 2

python manage.py shell -c "from django.conf import settings; print(settings.ALLOWED_HOSTS)"

确认运行进程接受哪些 host;环境变量覆盖可能与文件不同。

常见诱因(按出现频率排序):

  • 生产环境 ALLOWED_HOSTS 为空,Django 禁止此配置
  • Host 头携带的是内部主机名而非公开域名
  • ALLOWED_HOSTS 缺少域名以及负载均衡健康检查的主机名/IP

根因

Django 会用 ALLOWED_HOSTS 校验传入的 Host 头,以防主机头攻击。任何主机不在列表中的请求都会在到达视图前被以 DisallowedHost 拒绝。

修复

显式设置 ALLOWED_HOSTS(公开域名加健康检查 host/IP),并配置反向代理通过 USE_X_FORWARDED_HOST 与 SECURE_PROXY_SSL_HEADER 转发原始 host,使真实 host 参与校验。

注意:设置 “*” 会关闭主机头防护,使缓存投毒与密码重置攻击成为可能;生产中切勿使用。

# settings.py
ALLOWED_HOSTS = ['app.example.com', '10.0.0.5']  # public domain + LB health-check

# Behind a trusted proxy that sets X-Forwarded-Host
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

验证

通过公开域名请求并确认 200;用伪造 Host 请求并确认仍返回 400,证明白名单仍有效。

修复后补充一条回归测试,复现原始症状,防止问题再次出现。

这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。