症状
本篇指南处理“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,证明白名单仍有效。
修复后补充一条回归测试,复现原始症状,防止问题再次出现。
这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。