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

跨站场景下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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 13:40:33