为何同域名不同目录无法获取$_SESSION数据?含Memcache及生产场景
1. 为何不同目录下无法获取使用Memcache存储的$_SESSION数据?
其实核心原因不是目录本身,而是会话配置不一致或者Memcache存储的会话标识关联出了问题。当用Memcache存session时,PHP会把会话数据以PHPSESSID的值为键存在Memcache里,但如果不同目录下的PHP会话配置(比如session.name、session.save_path或Memcache连接参数)不一样,就会导致要么找不到对应的键,要么连接到了不同的Memcache实例/分组,自然读不到之前存的会话数据。
另外,虽然你看到Cookie的PHPSESSID一致,但如果PHP在启动会话时,因为配置差异生成了不同的内部会话标识(比如有些配置会给会话键加前缀),那即使Cookie里的ID一样,Memcache里的实际键也不一样,就拿不到数据。
2. 生产服务器上哪些设置会导致同域名不同目录下无法读取会话数据?
结合你给出的示例(Cookie的PATH是/,所以目录本身不是Cookie的问题),以下是最可能的几个配置问题:
session.save_path配置不一致
如果你在主目录和subdir下的PHP配置(比如.htaccess、php.ini或者代码里的ini_set())设置了不同的session.save_path,比如主目录连接的是tcp://127.0.0.1:11211,而子目录连接的是tcp://127.0.0.1:11212,或者指定了不同的Memcache分区,那两边的会话数据存在不同的地方,自然读不到。
验证方法:在两个文件里都执行var_dump(ini_get('session.save_path'));对比结果。session.name不一致session.name是会话Cookie的名称,默认是PHPSESSID,但如果其中一个目录的代码里用ini_set('session.name', 'MYSESSID');改了这个值,那即使Cookie里的PHPSESSID存在,子目录的PHP会去寻找MYSESSID对应的会话数据,自然为空。
验证方法:在两个文件里输出var_dump(ini_get('session.name'));。Memcache会话键前缀配置不同
有些时候会给Memcache存储的会话键加前缀,通过session.save_path里的prefix参数设置,比如tcp://127.0.0.1:11211?prefix=main_和tcp://127.0.0.1:11211?prefix=sub_,这样即使PHPSESSID相同,Memcache里的实际键是main_9e5297fa1577b4ea2096490ef3400cad和sub_9e5297fa1577b4ea2096490ef3400cad,自然找不到对应数据。会话启动时机或输出问题
如果子目录的代码里在session_start()之前有意外输出(比如空格、HTML标签),可能导致会话启动失败,PHP会生成新的空会话,但浏览器里的Cookie还是旧的,这时候读的就是空数据。你可以开启error_reporting(E_ALL)看有没有“headers already sent”的警告来排查。
快速排查建议
- 在两个文件的
session_start()之后,立即输出var_dump(session_id());,确认两边的会话ID完全一致。 - 输出两边的关键会话配置:
var_dump(ini_get('session.save_path'), ini_get('session.name'), ini_get('session.save_handler'));,确保所有值都相同。 - 直接用Memcache客户端连接服务器,查看是否存在以当前
PHPSESSID为键(或带前缀)的会话数据。比如用telnet 127.0.0.1 11211,然后执行get 9e5297fa1577b4ea2096490ef3400cad(有前缀就加上前缀),看是否能拿到数据。
内容的提问来源于stack exchange,提问作者beingalex

