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

Express Session无法在iOS/Safari存储Cookie的问题求助

iOS/Safari下express-session无法存储Cookie导致登录失败的解决方案

问题描述

我遭遇该问题数周:在iOS设备上登录应用时,express-session无法在浏览器中存储Cookie,导致无法登录;但在安卓设备及电脑端可正常登录,且安卓设备的Safari浏览器也无法访问应用。前端技术栈为Nextjs(部署在Netlify),后端采用Nodejs+Express(部署在Heroku)。

后端代码(Server.js)

import express from "express"
import rootRoute from "./src/root_Route.js"
import cookieParser from "cookie-parser"
import passport from "passport"
import connectPgSimple from "connect-pg-simple"
import session from "express-session"
import dotenv from "dotenv"
import cors from "cors"
import "./src/LIB/DB-Client.js"
import "./src/PASSPORT_STRATEGY/google-auth-strategy.js"
import "./src/PASSPORT_STRATEGY/facebook-auth-strategy.js"
import { scheduleJob } from "node-schedule"
import pool from "./src/LIB/DB-Client.js"
dotenv.config()
const app = express()


const connection = process.env.PRODUCTION !== "production" ? process.env.DEV_DATABASE_URL : process.env.DATABASE_URL

app.use(
  cors({
    origin: ["http://localhost:3000", "https://nairaonly-frontend.netlify.app"],
    credentials: true,
    methods: "GET, PUT, POST, DELETE",
    optionsSuccessStatus: 200,
  })
)
const PgStore = connectPgSimple(session)
const store = new PgStore({ conString: connection, schemaName: "hidden", createTableIfMissing: true })

app.use(express.json())
app.use(cookieParser())
app.set("trust proxy", 1)

app.use(
  session({
    store: store,
    secret: process.env.SESSION_SECRET,
    saveUninitialized: false,
    resave: true,
    proxy: true,
    cookie: {
      maxAge: 1000 * 60 * 60 * 24,
      httpOnly: true,
      sameSite: process.env.NODE_ENV === "production" ? 'none' : 'lax',
      secure: process.env.NODE_ENV === "production" ? true : false,
    },
  })
)

app.get("/", (req, res) => {
  res.send("API Running...")
})

app.use(passport.initialize())
app.use(passport.session())
rootRoute(app)

const PORT = process.env.PORT || 4000

app.listen(PORT, (req, res) => console.log(`Server running on PORT:${PORT}...`))

解决方案

1. 修正Cookie的SameSite与Secure配置逻辑

iOS Safari对SameSite=None的Cookie强制要求搭配Secure属性,且需确保生产环境下该配置生效。替换NODE_ENV判断为你当前使用的PRODUCTION环境变量:

cookie: {
  maxAge: 1000 * 60 * 60 * 24,
  httpOnly: true,
  // 用PRODUCTION变量判断环境,确保生产环境正确配置
  sameSite: process.env.PRODUCTION === "production" ? 'none' : 'lax',
  secure: process.env.PRODUCTION === "production" ? true : false,
}

2. 确保前端请求携带Credentials

在Next.js的所有跨域请求中(fetch/axios),必须显式声明携带Cookie:

  • Fetch示例:
fetch('https://your-heroku-backend-url.com/api/login', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(loginData),
})
  • Axios示例:
axios.post('https://your-heroku-backend-url.com/api/login', loginData, {
  withCredentials: true,
})

3. 优化代理信任设置

Heroku作为反向代理,需让Express完全信任代理,确保HTTPS协议识别正确:

// 替换原有的app.set("trust proxy", 1)
app.set("trust proxy", true);

4. 调整Session的resave属性

resave: true可能导致不必要的Session更新,结合PgStore的特性,改为:

session({
  // ...其他配置
  resave: false, // 改为false
  // ...
})

5. 检查CORS配置的Origin

确保生产环境的前端域名配置正确,建议用环境变量动态配置避免硬编码:

const allowedOrigins = process.env.PRODUCTION === "production" 
  ? [process.env.FRONTEND_URL] 
  : ["http://localhost:3000"];

app.use(
  cors({
    origin: allowedOrigins,
    credentials: true,
    methods: ["GET", "PUT", "POST", "DELETE"], // 用数组格式更规范
    optionsSuccessStatus: 200,
  })
);

6. 用户端兜底设置

iOS Safari默认开启"阻止跨站跟踪",会拦截跨域Cookie。可引导用户关闭:设置 → Safari浏览器 → 阻止跨站跟踪(关闭)。

验证方法

部署修改后的后端后,在iOS Safari中:

  1. 打开开发者工具(设置→Safari浏览器→高级→网页检查器)
  2. 查看Application→Cookies,确认Session Cookie是否存在
  3. 检查登录请求的Response Headers中的Set-Cookie字段,确认包含SameSite=None; Secure

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 20:15:43