Django 部署后页面显示 403 Forbidden,通常不是代码逻辑错误,而是中间件配置、反向代理或静态文件权限没对齐。以下按排查路径整理,每步都附带观察点和回滚方式,方便你对着自己的环境核对。
先确认 403 来自 Django 还是上游
当 Django 应用部署后返回 403 Forbidden,首先应检查浏览器开发者工具中的网络请求,确认响应头是否包含 'X-Frame-Options: DENY' 或 'Content-Type: text/html' 等。如果请求是 POST 且缺少 CSRF 令牌,Django 的 CsrfViewMiddleware 会直接拒绝并返回 403。此时可以查看 Django 日志中是否有 'CSRF token missing or incorrect' 的提示。
这一步是为了区分 403 是谁返回的。如果响应头里没有 Django 的 Server 标识(比如 'Server: WSGIServer/0.2'),那多半是 Nginx 或 Apache 拦截了请求,需要检查上游访问控制。如果响应头里有 Django 痕迹,就往下排查 Django 层面。
检查 CSRF 和表单模板
最常见的原因是模板里缺少 {% csrf_token %} 标签。当使用 Django 表单且未在模板内包含该标签时,POST 请求会被 CSRF 中间件拦截。尤其在使用 JavaScript 发起 AJAX 请求时,需要手动从 cookie 中获取 CSRF 令牌并添加到请求头中。另外,若 Django 版本较低,CSRF 失败的响应可能不是 403 而是 500。
确认方法:在浏览器开发者工具的网络面板里,看 POST 请求的请求头是否有 X-CSRFToken 字段。如果没有,检查模板或者前端代码。对于 Django 表单,直接加上 {% csrf_token %};对于 AJAX,参考 Django 官方文档从 csrftoken cookie 里取值并设置请求头。改完后清空浏览器缓存再试,同时观察 Django 日志里的 CSRF 相关条目。
检查 ALLOWED_HOSTS 和反向代理
最常见的修复方法是在 settings.py 中正确配置 ALLOWED_HOSTS。如果部署环境使用了反向代理(如 Nginx),ALLOWED_HOSTS 应包含实际访问的域名或 IP,例如 ALLOWED_HOSTS = ['yourdomain.com', 'www.yourdomain.com']。若使用万用字符 '*',需注意安全风险,仅限开发环境使用。配置后需重启 Django 服务才能生效。
这里有一个边界情况:如果 Nginx 正确转发了 Host 头,但 ALLOWED_HOSTS 漏了对应的域名,Django 会拒绝请求并返回 403。检查 Nginx 配置里的 proxy_set_header Host $host; 和 proxy_set_header X-Real-IP $remote_addr; 是否开启。另外,如果用了负载均衡或 CDN,要确认 X-Forwarded-For 和 X-Real-IP 头是否被正确传递,并且 Django 的 SECURE_PROXY_SSL_HEADER 也要匹配实际协议。
检查静态文件访问权限
检查静态文件访问是否正常。如果 403 仅出现在访问静态资源(如 CSS、JS)时,可能是 Django 的 STATIC_ROOT 或 STATICFILES_DIRS 配置错误,或者 Nginx 没有正确代理静态文件目录。运行 python manage.py collectstatic 重新收集静态文件,并确保 Nginx 配置中对应路径的权限正确。生产环境下建议由 Nginx 直接处理静态文件,避免经过 Django。
验证思路:在浏览器直接访问一个静态文件 URL,比如 https://yourdomain.com/static/css/style.css,看响应状态码。如果返回 403 且响应头里没有 Django 信息,那就去 Nginx 日志里查 error.log,看是否权限不足或路径不对。Nginx 的静态文件 location 块通常需要指定 alias 或 root,并且目录权限要确保 Nginx 工作进程能读取。
检查安全中间件设置
确认 Django 的安全中间件设置是否过于严格。例如,SECURE_SSL_REDIRECT 设为 True 时,若请求未使用 HTTPS,Django 会重定向,但若配置不当可能导致循环重定向或 403。另外,SECURE_HSTS_SECONDS 等 HSTS 设置也可能在浏览器缓存中导致后续非 HTTPS 请求被拦截。检查 settings.py 中 SECURE_SSL_REDIRECT 和 SECURE_PROXY_SSL_HEADER 的配置是否匹配实际部署环境。
容易忽略的点:如果 Nginx 已经做了 SSL 终止,而 Django 的 SECURE_SSL_REDIRECT 还是 True,并且没有正确配置 SECURE_PROXY_SSL_HEADER,那么来自 Nginx 的 HTTP 请求会被 Django 重定向,导致浏览器陷入循环。同时,SECURE_HSTS_SECONDS 开启后,浏览器会强制后续请求走 HTTPS,如果中间临时想切回 HTTP 测试,可能会被缓存策略阻止。建议先在开发环境调通,再上生产。
改完后看这几个信号
每次修改配置后,重启 Django 服务(systemctl restart gunicorn 或 supervisorctl restart)。然后观察三个地方:浏览器正常访问页面不再 403;Django 日志里没有 CSRF 或 ALLOWED_HOSTS 相关错误;Nginx 日志里静态文件的访问返回 200 而非 403。如果问题依旧,可以把中间件逐个注释测试,但注意安全风险,生产环境不要长期禁用。