frankenphp默认不持久化php会话存储,因其worker模式下files处理器写入的临时目录(如/tmp)在重启后清空且worker间不共享路径,导致登录后$_session为空、session_id()频繁变更;symfony项目必须显式配置session.handler_id(推荐redis),仅改php.ini的session.save_path无效。

FrankenPHP 默认不持久化 PHP 会话存储
FrankenPHP 启动时默认使用 files 会话处理器,但它的临时目录(如 /tmp)在容器或进程重启后会被清空,且多个 worker 实例之间不共享该路径。结果就是:用户登录后刷新页面,$_SESSION 变为空数组,session_id() 每次都变,典型表现是“刚登录就掉线”。
Symfony 项目必须显式配置 session.handler_id
Symfony 默认依赖 PHP 原生会话机制,但 FrankenPHP 的 worker 模式下,PHP 进程常驻、请求复用内存,而原生 files 处理器仍按传统方式写入本地磁盘——这会导致不同 worker 写入不同文件,或因权限/路径隔离根本写不进。你不能只改 php.ini 里的 session.save_path,必须让 Symfony 主动接管会话管理。
- 在
config/packages/framework.yaml中强制指定 handler:
framework:
session:
handler_id: 'session.handler.native_file'
# 或更推荐:用 Redis(需安装 ext-redis)
# handler_id: 'session.handler.redis'
- 若坚持用文件存储,确保路径可写且全局一致:
session.save_path必须指向一个所有 worker 都能访问的绝对路径,例如/var/www/sessions,并在 Dockerfile 中RUN mkdir -p /var/www/sessions && chmod 777 /var/www/sessions - 不要依赖
php.ini的默认值;FrankenPHP 的 PHP 运行时加载顺序可能导致 ini 设置被覆盖
启用 sticky sessions 或禁用 worker 模式来快速验证
如果你暂时无法接入 Redis,又急需验证是否是会话共享问题,可以绕过 worker 模式,退回到“每请求启动一次 PHP”的兼容模式——这时会话行为与传统 FPM 完全一致,files 处理器能正常工作。
- 启动 FrankenPHP 时不加
-worker参数,或在 Caddyfile 中移除php_worker指令 - 若必须用 worker 模式且无 Redis,可在 Caddyfile 中加 sticky session 路由(仅限单机部署):
reverse_proxy localhost:8080 {
lb_policy first
health_timeout 5s
}
# 但注意:FrankenPHP 本身不支持多实例间 session 共享,lb_policy 在单机无意义
真正有效的 sticky 是靠 Caddy 的 hash 策略绑定客户端 IP 到固定 worker,但 FrankenPHP 当前版本(v1.2.x)尚未暴露该能力,所以这条路实际走不通。
Redis 是生产环境唯一可靠选择
FrankenPHP 的 worker 模式本质是多协程复用同一个 PHP 进程上下文,而 Redis 会话处理器基于 ext-redis 的连接池和原子操作,天然支持并发读写。它不依赖文件系统,也不受进程生命周期影响。
- 确保安装了
redis扩展:docker-php-ext-install redis(Docker 构建时) - 在
config/packages/framework.yaml中启用:
framework:
session:
handler_id: 'session.handler.redis'
storage_factory_id: 'session.storage.factory.redis'
- 同时配置
config/packages/redis.yaml(Symfony 6.4+):
redis:
dsn: '%env(REDIS_DSN)%'
环境变量 REDIS_DSN=redis://localhost:6379 即可。注意:不要用 127.0.0.1 —— 在容器中它指向容器自身,应改用宿主别名(如 host.docker.internal)或独立 Redis 服务。
最容易被忽略的是:worker 模式下,session_start() 不再触发 session.save_path 初始化逻辑,所有会话控制完全交由 Symfony 的 handler 实现。没配 handler_id,等于没配会话——不是“坏了”,而是“根本没启用”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











