跨站场景下Strict属性Cookie被拒求助(Nginx+Next.js+Rocket)
问题场景
- 前端通过
https://client.my-project:3001访问,Nginx代理至本地Next.js服务localhost:3000 - React组件的
handleSubmit函数通过fetch向后端发送POST请求,Nginx作为反向代理 - 后端返回的Cookie配置了
Secure、HttpOnly、SameSite=Strict属性,但浏览器拒绝存储,提示:
cookie “id” has been rejected because it is in a cross-site context and its “samesite” is “lax” or “strict”.
当前配置与代码
Nginx配置
upstream api { server localhost:8000; } upstream client { server localhost:3000; } server { listen 3001 ssl; server_name client.my-project; ssl_certificate /client/client.my-project.pem; ssl_certificate_key /client/client.my-project.pem; location / { proxy_pass http://client; } location /_next/webpack-hmr { proxy_pass http://client/_next/webpack-hmr; proxy_http_version 1.1; proxy_set_header upgrade $http_upgrade; proxy_set_header connection "upgrade"; } } server { if ($http_origin = https://client.my-project:3001) { set $allowed_origin 'https://client.my-project:3001'; } listen 4545 ssl; server_name api.my-project; ssl_certificate /api/api.my-project.pem; ssl_certificate_key /api/api.my-project-key.pem; location / { add_header 'access-control-allow-origin' $allowed_origin always; add_header 'access-control-allow-credentials' 'true' always; add_header 'access-control-allow-headers' 'authorization,accept,origin,dnt,x-customheader,keep-alive,user-agent, x-requested-with,if-modified-since,cache-control,content-type,content-range,range'; add_header 'access-control-allow-methods' 'get,post,options,put,delete,patch'; proxy_pass https://api; } }
前端Fetch代码
const response = await fetch(`${process.env.NEXT_PUBLIC_API}/user`, { mode: 'cors', method: 'POST', body: formData, credentials: 'include' });
后端Cookie生成代码
let cookie = Cookie::build("id", session_id) .secure(true) .http_only(true) .same_site(SameSite::Strict) .finish();
解决方案(无需设置SameSite=None)
核心思路是让前端与API请求处于同一站点(Same Site),浏览器就会接受SameSite=Strict的Cookie。具体实现如下:
1. 修改Nginx配置,将API请求通过前端域名的子路径代理
删除原有的api.my-project服务器块,在前端的服务器块中新增API代理规则:
upstream api { server localhost:8000; } upstream client { server localhost:3000; } server { listen 3001 ssl; server_name client.my-project; ssl_certificate /client/client.my-project.pem; ssl_certificate_key /client/client.my-project.pem; # 代理前端页面请求到Next.js location / { proxy_pass http://client; } # Webpack热重载代理配置 location /_next/webpack-hmr { proxy_pass http://client/_next/webpack-hmr; proxy_http_version 1.1; proxy_set_header upgrade $http_upgrade; proxy_set_header connection "upgrade"; } # 代理API请求到后端服务 location /api { # 转发时去掉路径中的/api前缀 proxy_pass https://api/; # 传递必要的请求头,确保后端正确识别请求 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
2. 调整前端Fetch请求地址
将API请求地址改为前端域名下的/api子路径,无需再使用跨域域名:
const response = await fetch('/api/user', { method: 'POST', body: formData, credentials: 'include' });
(可移除mode: 'cors',因为现在属于同域请求)
3. 保持后端Cookie配置不变
继续使用SameSite=Strict、HttpOnly、Secure属性,此时浏览器会正常存储该Cookie:
let cookie = Cookie::build("id", session_id) .secure(true) .http_only(true) .same_site(SameSite::Strict) .finish();
原理说明
- 调整后,前端页面和API请求均来自
https://client.my-project:3001,属于同一站点,符合SameSite=Strict的适用条件 - 浏览器不再将请求判定为跨域,因此会正常接受并存储带有
SameSite=Strict的Cookie - 避免了使用
SameSite=None带来的安全风险,同时兼容所有支持SameSite属性的浏览器
内容的提问来源于stack exchange,提问作者mvannsec
相关产品推荐
相关产品推荐

