Next.js请求Django服务器遭遇CSRF 403错误(已传Token仍报错)
问题描述
本地同时运行Django服务器与Next.js客户端,从Next.js向Django发送POST请求时,已传递CSRF Token但仍收到403 Forbidden (CSRF cookie not set.)错误。Postman/Insomnia调用同一API可正常工作,Next.js应用中请求失败。已排查CORS并修改Django的settings.py配置,问题依旧。
获取CSRF Token的API步骤正常,以下是Next.js请求代码:
const uploadFile = async () => { // Select file from input element or any other method const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; const form = new FormData(); form.append("image", file); const options = { method: 'POST', headers: { cookie: 'csrftoken=2V7dKpZXLN24pLDdDkub9GxM2ljzI0nI', 'Content-Type': 'multipart/form-data; boundary=---011000010111000001101001', 'X-CSRFToken': '2V7dKpZXLN24pLDdDkub9GxM2ljzI0nI' } }; options.body = form; fetch('http://127.0.0.1:8000/getinfo', options) .then(response => response.json()) .then(response => console.log(response)) .catch(err => console.error(err)); };
Django项目settings.py配置:
# SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [ 'mymedic.pythonanywhere.com', '127.0.0.1', ] # Application definition INSTALLED_APPS = [ 'backend.apps.BackendConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'corsheaders' ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'corsheaders.middleware.CorsMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] CSRF_COOKIE_HTTPONLY=False SESSION_COOKIE_SECURE=False CSRF_COOKIE_SECURE=False CORS_ALLOW_CREDENTIALS = True # https://stackoverflow.com/questions/53215045/redirect-is-not-allowed-for-a-preflight-request CORS_ALLOW_HEADERS = ( 'accept', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', 'user', 'apikey', 'Referer', 'credentials', 'image,' 'username', ) CORS_ALLOWED_ORIGINS = [ 'http://localhost:3000', # Add the origin of your Next.js application ] CORS_ALLOW_ALL_ORIGINS = False # Disable allowing all origins CSRF_TRUSTED_ORIGINS = [ 'http://localhost:3000', 'https://*.mydomain.com', 'https://*.127.0.0.1' ]
问题分析
- 手动设置Cookie无效:浏览器会自动管理Cookie,跨域场景下手动硬编码的
cookie头可能被浏览器拦截,无法被Django正确识别。 - Content-Type配置错误:使用
FormData时,浏览器会自动生成带正确boundary的multipart/form-data请求头,手动指定的boundary与实际生成的不匹配,会导致请求解析异常,进而影响CSRF验证。 - CSRF_TRUSTED_ORIGINS配置不匹配:Next.js运行在
http://localhost:3000,但配置中的https://*.127.0.0.1是HTTPS协议,与本地HTTP环境不兼容。 - CORS_ALLOW_HEADERS语法错误:
'image,'末尾多了一个逗号,会导致头信息解析失败。
调试方法
- 查看浏览器网络请求:打开开发者工具(F12)→Network标签,检查POST请求的Request Headers:
- Cookie中是否包含
csrftoken字段 X-CSRFToken头的值是否与Cookie中的一致- Request URL和Origin是否匹配
CSRF_TRUSTED_ORIGINS配置
- Cookie中是否包含
- 检查Django日志:查看Django控制台输出的CSRF验证失败详情,日志会明确提示失败原因(如Cookie不存在、Token不匹配等)
- 简化请求测试:先发送不带文件的普通POST请求,验证CSRF机制是否正常,再逐步添加FormData和文件上传逻辑
修改建议
1. 修正Next.js请求代码
去掉手动设置的cookie和Content-Type头,让浏览器自动处理,同时开启凭证携带:
const uploadFile = async () => { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; const form = new FormData(); form.append("image", file); // 从浏览器Cookie中解析CSRF Token const csrfToken = document.cookie.split('; ').find(row => row.startsWith('csrftoken='))?.split('=')[1]; const options = { method: 'POST', headers: { 'X-CSRFToken': csrfToken }, credentials: 'include' // 关键:携带Cookie等凭证 }; options.body = form; fetch('http://127.0.0.1:8000/getinfo', options) .then(response => response.json()) .then(response => console.log(response)) .catch(err => console.error(err)); };
注:如果解析Cookie麻烦,可安装
js-cookie库,用Cookies.get('csrftoken')直接获取Token。
2. 修正Django settings.py配置
- 调整
CSRF_TRUSTED_ORIGINS,添加HTTP协议的本地地址:
CSRF_TRUSTED_ORIGINS = [ 'http://localhost:3000', 'https://*.mydomain.com', 'http://127.0.0.1' # 改为HTTP协议,去掉不必要的通配符 ]
- 修正
CORS_ALLOW_HEADERS中的语法错误:
CORS_ALLOW_HEADERS = ( 'accept', 'accept-encoding', 'authorization', 'content-type', 'dnt', 'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', 'user', 'apikey', 'Referer', 'credentials', 'image', # 去掉末尾的逗号 'username', )
- 保持Middleware顺序:确保
CorsMiddleware在CommonMiddleware之前,当前配置已符合要求,不要随意调整。
3. 验证CSRF Token存储
在浏览器开发者工具→Application→Cookies→http://127.0.0.1:8000下,确认存在csrftoken字段,且值与请求头中的X-CSRFToken一致。
内容的提问来源于stack exchange,提问作者zodiac
相关产品推荐
相关产品推荐

