allowed_hosts必须按实际host头精确匹配:开发可配['localhost','127.0.0.1']或['*'](仅debug=true时有效),生产必须显式列出域名(含www等变体)、禁用通配符、通过环境变量动态配置并严格校验空格与加载顺序。

因为开发环境和生产环境面对的请求来源、安全要求、代理结构完全不同,ALLOWED_HOSTS 不是“配得越宽越好”,而是必须按实际流量路径精确匹配 Host 头。
开发时为什么可以写 ['localhost', '127.0.0.1'] 或 ['*']
本地调试时请求基本来自本机浏览器或 curl,Host 头通常是 localhost、127.0.0.1 或带端口的变体(如 localhost:8000)。DEBUG=True 时 Django 允许 ['*'],但仅限开发——它不校验 Host,纯粹为了方便;一旦 DEBUG=False,['*'] 就失效,直接报 DisallowedHost。
- 用
python manage.py runserver 0.0.0.0:8001启动,浏览器访问http://localhost:8001,Django 收到的 Host 是localhost:8001,不是localhost—— 必须显式写进列表 -
['*']在生产环境完全无效,Django 会忽略它并拒绝所有请求 - 开发时加
[::1]是为了支持 IPv6 本地访问,不是可有可无的“兼容项”
生产环境为什么必须显式列出域名且不能漏掉变体
用户通过 https://www.example.com 访问,Nginx 转发给 Django 的 Host 头默认仍是 www.example.com(除非你显式重写),Django 拿这个原始值去比对 ALLOWED_HOSTS。它不自动补 www、不忽略协议、不处理子域名通配——只做字符串完全匹配。
提供使用 django-unfold (v0.56+) 构建高级 Django 管理界面的专业指导。当用户提及 django-unfold、unfold admin 等内容时使用此技能。
- 写了
'example.com'但没写'www.example.com'→ 用户输带 www 的地址就 400 - 用了 CDN 或云负载均衡(如 Cloudflare、AWS ALB),它们可能透传原始 Host 头,也可能改写成内部 IP → 必须查文档确认实际到达 Django 的 Host 值是什么
-
'.example.com'(开头带点)是 Django 支持的子域名通配写法,但只匹配二级及以下,比如api.example.com、shop.example.com会被接受,而example.com本身不会 —— 所以仍需单独加
反向代理后 Host 头被篡改或丢失怎么办
Nginx/Apache 默认转发时 Host 头可能变成上游地址(如 127.0.0.1:8000)或空,导致即使配置了正确域名也匹配失败。这不是 ALLOWED_HOSTS 配错了,而是请求根本没带上预期 Host。
- 在 Nginx 配置里加
proxy_set_header Host $host;,确保把客户端原始 Host 传下去 - 如果用了
proxy_set_header Host $http_host;,要注意$http_host可能带端口(如example.com:443),而ALLOWED_HOSTS里没写端口就会不匹配 - Django 收到的 Host 值,永远是你 curl -H "Host: xxx" 实测的那个字符串 —— 抓包或加日志打印
request.get_host()最可靠
用环境变量动态配置时最容易踩的坑
硬编码域名会导致上线改代码,但环境变量配错一样 400,而且更难排查。
-
os.environ.get('DJANGO_ALLOWED_HOSTS', 'localhost').split(',')中,如果环境变量值是"example.com, www.example.com"(逗号后有空格),split(',')会生成['example.com', ' www.example.com']—— 第二个元素开头有空格,匹配失败 - 多个 settings 文件(如
prod.py、base.py)里都定义了ALLOWED_HOSTS,结果加载顺序导致覆盖或拼接出错 - 忘记在容器或 systemd service 里注入环境变量,
printenv DJANGO_ALLOWED_HOSTS查不到,回退到默认值'localhost',线上全 400
最常被忽略的一点:上线后 ALLOWED_HOSTS 里还留着 'localhost' 或 '127.0.0.1',而真实流量是域名,Django 根本不看其他配置,直接拒掉——它连视图层都进不去,日志里也不会记录请求,只能靠复现和排除法定位。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










