You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django经Nginx反向代理时CSRF验证失败问题咨询

Django CSRF 问题排查记录

环境配置

localhost (Windows 11)
  - Nginx
      监听80和443端口,80端口未自动重定向至443
      均反向代理至http://wsgi-server(wsgi-server=127.0.0.1:8080)
  - waitress_wsgi 作为服务运行在8080端口

Django配置

模板文件(email_test.html)

<!-- email_test.html -->
<!-- ... -->
<form action="{% url 'identity:email_test' %}" method="post">
  {% csrf_token %}
  {{ email_form }}
  {% translate 'Send email' as submit_translated %}
  <!-- 我使用django_bootstrap5 -->
  {% bootstrap_button button_type="submit" content=submit_translated extra_classes='w-100'%}
</form>

配置文件(settings.py)

# settings.py ----------
MIDDLEWARE = {
  # ...
  'django.middleware.csrf.CsrfViewMiddleware',
  # ...
}

表单定义(forms.py)

# forms.py -------------
class EmailTestForm(forms.Form):
  email = forms.EmailField(
    # help_text=password_validation.password_validators_help_text_html(),
    label=_('Email'),
    max_length=128,
  )

视图逻辑(views.py)

# views.py -------------
def email_test(request):
  context = {}
  context.update(template_globals())
  if request.method == "POST":
    email_form = EmailTestForm(request.POST)
    if email_form.is_valid():
      email_obj = EmailMessage(subject='Hello', body='Email body',
                               from_email='noreply@nutrihub.hnet',
                               to=[email_form.cleaned_data.get('email')])
      email_obj.send(fail_silently=False)
  else:
    email_form = EmailTestForm()

  context['email_form'] = email_form
  return render(request, "identity/email_test.html", context)

浏览器访问测试结果

  1. py manage.py runserver(默认端口8000),浏览器访问http://127.0.0.1:8000,settings.CSRF_TRUSTED_ORIGINS为空:运行正常。
  2. 浏览器访问http://localhost/http://127.0.0.1/https://localhost/https://127.0.0.1,对应地址未加入settings.CSRF_TRUSTED_ORIGINS:出现CSRF错误。
  3. 浏览器访问http://localhost/http://127.0.0.1/https://localhost/https://127.0.0.1,对应地址已加入settings.CSRF_TRUSTED_ORIGINS:运行正常。
  4. 浏览器访问https://mymachine.net(该域名在etc/hosts中指向127.0.0.1),未加入settings.CSRF_TRUSTED_ORIGINS:出现CSRF错误。
  5. 浏览器访问https://mymachine.net(该域名在etc/hosts中指向127.0.0.1),已加入settings.CSRF_TRUSTED_ORIGINS:运行正常。
  6. 浏览器访问http://localhost:8080/http://localhost/http://mymachine.net:8080:运行正常。

问题解答

是的,这个现象的核心原因就是通过Nginx转发的请求,在Django眼里和浏览器的请求源不属于同站点。

具体逻辑如下:

  • Django的CSRF验证核心是比对「浏览器发起请求的源」和「Django感知到的请求源」是否一致。
  • 当用Nginx反向代理时,Django默认获取的是Nginx转发过来的请求地址(即WSGI服务器的127.0.0.1:8080),但浏览器的请求源是localhost/127.0.0.1/mymachine.net(对应80/443端口),两者完全不匹配,因此被判定为跨站请求,触发CSRF错误。
  • 将这些外部源加入settings.CSRF_TRUSTED_ORIGINS后,相当于告知Django这些域名/端口的请求是可信的,允许通过CSRF验证,所以场景3、5能正常运行。
  • 而场景6直接访问8080端口时,浏览器的请求源(如localhost:8080)和Django感知到的源一致,属于同站点请求,无需额外配置即可通过验证。

内容的提问来源于stack exchange,提问作者fishfin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 02:20:13