Apache mod_proxy_balancer默认不保持会话,需配置stickysession绑定JSESSIONID等会话Cookie,并在BalancerMember中显式指定route值,且后端Cookie必须含.route后缀(如JSESSIONID=abc123.node1),否则会话粘性失效。

Apache mod_proxy_balancer 默认不保持会话
直接用 ProxyPass + balancer:// 时,请求会被轮询分发到后端节点,用户登录后刷新页面可能跳到另一个无 session 的服务器,导致登出或数据错乱。这不是配置漏了,而是默认行为——mod_proxy_balancer 本身不解析应用层 Cookie,必须显式启用会话粘性。
用 stickysession 参数绑定 JSESSIONID 或 PHPSESSID
多数 Java/PHP 应用通过 Cookie 中的固定名(如 JSESSIONID、PHPSESSID)传递会话 ID,Apache 可据此做路由。关键是在 ProxyPass 指令中加入 stickysession:
ProxyPass / balancer://mycluster/ stickysession=JSESSIONID|jsessionid
注意点:
-
stickysession值区分大小写,但建议同时提供大写和小写变体(用|分隔),因为某些客户端或反向代理可能修改首字母大小写 - 如果后端是 Spring Boot,默认 Cookie 名是
JSESSIONID;Laravel 是XSRF-TOKEN(但该 Token 不用于会话绑定),真正要用的是laravel_session,需确认应用实际 Set-Cookie 头 - Apache 不校验 Cookie 值有效性,只提取并哈希后匹配后端 worker —— 所以只要 Cookie 存在且非空,就会固定到同一台机器
必须开启 route 参数并在后端 Cookie 中嵌入 route 标识
仅靠 stickysession 不够。Apache 需要知道每个后端节点的唯一标识(即 route),才能把带 JSESSIONID=abc123.xxx 的请求发给标有 route=xxx 的 worker。因此后端应用返回的 Cookie 必须包含 .route 后缀,例如:
Set-Cookie: JSESSIONID=abc123.node1; Path=/
对应 Apache 配置要写成:
BalancerMember http://192.168.1.10:8080 route=node1
常见疏漏:
- 没在
BalancerMember中显式指定route,Apache 会尝试从后端响应头自动提取,但失败率高,尤其当后端没返回含 route 的 Cookie 时 - 后端 Tomcat 的
server.xml里Engine节点未设置jvmRoute="node1",导致它不会在JSESSIONID后自动追加.node1 - Spring Boot 内嵌 Tomcat 默认不设
jvmRoute,需在application.properties加server.tomcat.remote-ip-header=x-forwarded-for并配合外部 Tomcat 配置才生效
避免用 Cookie 指令覆盖原始会话 Cookie
有人会想用 Header edit Set-Cookie ^(.*)$ $1; Path=/; Secure; HttpOnly 统一加属性,但这可能破坏 JSESSIONID=xxx.node1 结构,导致 Apache 无法解析 route。更安全的做法是只操作非会话类 Cookie:
Header edit Set-Cookie "^(JSESSIONID|PHPSESSID|laravel_session)=.*" "$0; Path=/; HttpOnly"
或者干脆不用 Header edit 动态改 Cookie,而是在后端应用层统一设置路径与安全属性 —— Apache 的 stickysession 机制只依赖 Cookie 名和 .route 后缀,其余字段不影响粘性逻辑。
route 匹配是硬依赖,Cookie 名拼写和后缀格式稍有偏差就完全失效,调试时优先用 curl -v 看后端 Set-Cookie 响应头,再核对 BalancerMember 的 route 值是否一字不差。










