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

Node.js/Express生产环境会话丢失问题的排查与解决

生产环境Express会话丢失问题修复方案

问题描述

我有一个部署在Hetzner的Debian Linux服务器上的Node.js/Express.js站点,采用Nginx/pm2运行。登录时req.session.user能正常设置为admin,但后续浏览器检查用户时会话丢失,本地开发环境运行正常,生产环境存在该问题。

以下是我的server.ts会话及Cookie配置代码:

import * as model from './model.js';
import express from 'express';
import cors from 'cors';
import * as config from './config.js';
import { INewBook } from './interfaces.js';
import session from 'express-session';
import cookieParser from 'cookie-parser';

declare module 'express-session' {
    export interface SessionData {
        user: { [key: string]: any };
    }
}

const app = express();
app.use(express.json());
app.use(cors({
    origin: config.FRONTEND_URL,
    methods: ['POST', 'GET', 'DELETE', 'PUT', 'OPTIONS', 'HEAD'],
    credentials: true
}));
app.set('trust proxy', 1);
app.use(cookieParser());
app.use(
    session({
        resave: true,
        saveUninitialized: true,
        secret: config.SESSION_SECRET,
        cookie: {
            httpOnly: true,
            sameSite: config.NODE_ENVIRONMENT === 'production' ? 'none' : 'lax',
            secure: config.NODE_ENVIRONMENT === 'production'
        }
    })
);

app.all('/', function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "https://et-mern-crud-site.tanguay.eu");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
});

关键修复点

1. 替换默认内存会话存储

express-session默认使用内存存储会话,生产环境下pm2多进程、服务器重启都会导致会话全部丢失,必须替换为持久化存储方案,比如Redis或MongoDB。

示例(使用Redis存储):
首先安装依赖:

npm install connect-redis redis

修改session配置:

import { createClient } from 'redis';
import RedisStore from 'connect-redis';

// 初始化Redis客户端
const redisClient = createClient({
  url: 'redis://localhost:6379' // 根据你的Redis实例地址调整
});
redisClient.connect().catch(console.error);

// 更新session配置,使用Redis存储
app.use(
    session({
        resave: false, // 建议设为false,减少不必要的存储操作
        saveUninitialized: false, // 只保存已初始化的会话,避免空会话占用存储
        secret: config.SESSION_SECRET,
        store: new RedisStore({ client: redisClient }),
        cookie: {
            httpOnly: true,
            sameSite: config.NODE_ENVIRONMENT === 'production' ? 'none' : 'lax',
            secure: config.NODE_ENVIRONMENT === 'production',
            maxAge: 24 * 60 * 60 * 1000, // 可选:设置会话过期时间,比如1天
            domain: '.tanguay.eu' // 匹配前端根域名,确保子域名下能共享Cookie
        }
    })
);

2. 统一CORS配置,消除冲突

当前代码同时使用cors中间件和手动设置CORS头,容易导致头信息冲突。保留cors中间件即可,删除手动设置的CORS代码:

// 删除这段代码
app.all('/', function (req, res, next) {
    res.header("Access-Control-Allow-Origin", "https://et-mern-crud-site.tanguay.eu");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    next();
});

确保config.FRONTEND_URL的值与前端实际访问URL完全一致(比如https://et-mern-crud-site.tanguay.eu)。

3. 修正Nginx代理配置

Nginx需要正确传递协议、主机等头信息,确保会话Cookie的secure属性生效。在Nginx配置中添加以下内容:

location / {
    proxy_pass http://localhost:3000; # 替换为你的Node服务端口
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection 'upgrade';
    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; # 关键:传递当前请求协议(http/https)
    proxy_cache_bypass $http_upgrade;
}

4. 确认环境变量设置

确保生产环境中NODE_ENVIRONMENT被正确设置为production,可以通过pm2启动命令指定:

pm2 start app.js --env production

或者在服务器环境变量中全局设置:

export NODE_ENVIRONMENT=production

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 22:40:31