
登录态为什么会在子域名之间断掉?先把“没带过去”和“没被认出来”分开
上周有位做家居流量站的客户问了个挺具体的问题。主站是www开头的域名,活动页扔在m子域名下,用户在主站登录完,点活动入口跳到m站,页面右上角立马变成未登录。返回主站又好了。他第一反应是跳转插件把Cookie弄丢了,想让我给个“跳转时强制带上Cookie”的配置。
这个问题真不能这么答。跨子域名跳转的登录态继承,说到底跟跳转这个动作关系不大,问题多半出在Cookie作用域和凭证识别上。得先分清是“压根没带过去”,还是“带过去了但服务端没认出来”。这两条路的排查方向差得远。
结论方向先摆出来:绝大多数跨子域名登录态丢失,根因就落在三处——Cookie的Domain属性没覆盖到目标子域名、Path把凭证限制在了特定路径、或者跳转链路中间经过了不同注册域,凭证根本没法共享。跳转插件自己能做的,是保证跳转前后请求头和参数不被截断,以及在无法共享凭证时提供一套明确的回退路径。它替浏览器“搬运”Cookie这事儿,做不到。
第一层检查:Cookie的Domain与Path是否覆盖了跳转目标
Domain属性的覆盖规则
浏览器对Cookie的Domain匹配是后缀匹配。主站设成Domain=example.com的Cookie,a.example.com、b.example.com都会带着;可要是主站写的是Domain=www.example.com,那这条Cookie就只对www生效,m.example.com压根拿不到。跨子域名登录态断裂最常见的原因就是这个,而且往往不是跳转插件改的,是主站登录模块当初把host写死了。
检查动作很直接:打开浏览器开发者工具的Application面板,看登录凭证那条Cookie的Domain列,显示的是带点的域名还是完整host。要是显示完整host,目标子域名又不在覆盖范围内,那就先别动跳转插件,去登录模块把Domain调到父域级别。限制条件得说清楚:Domain设成父域,意味着这条凭证对所有子域名都可见。站点下面要是有不受信任的子域名,比如外包团队托管的活动页,凭证暴露面就扩大了。配置之前先确认一遍子域名清单。
Path属性特别容易被人忽略。登录凭证的Path要是被设成了/api,m.example.com上的页面请求虽然会带上这条Cookie,但只有路径以/api开头的请求才携带,页面本身的渲染请求不带。服务端拿不到凭证,判未登录。检查方法就是看Cookie的Path列,确认目标子域名下需要识别登录态的页面路径是不是都在Path覆盖范围内。常见做法是把登录凭证的Path设成/,可这同样扩大了作用域,得跟Domain设置放在一起评估。
第二层检查:跳转链路中凭证的传递条件
同注册域下的子域名跳转,Cookie靠Domain属性自然共享就行,跳转插件不需要额外配什么凭证传递。可一旦跳转目标跨到了另一个注册域,比如活动页放在独立域名上,浏览器同源策略下Cookie根本不共享。这时候再聊“登录态继承”就得换思路了:要么在跳转URL上携带一次性凭证(token),由目标站校验后建立本地会话;要么走统一的登录中心做重定向回跳。跳转插件在这里的作用,是保证token参数在跳转链路中不被截断、不被重复编码。
操作上要检查两件事。跳转插件的URL拼接逻辑有没有对参数做二次编码,导致token变形;跳转链路是否经过多级302,每一级是不是都原样透传了query参数。限制条件是token方案必须有过期时间和一次性校验,长期有效的凭证直接挂在URL上,这个不能干。
跳转前后的协议与端口一致性
主站是HTTPS、目标子域名是HTTP,或者端口不同,浏览器的Cookie携带行为会受Secure属性和同源判断影响。带Secure标记的Cookie只在HTTPS请求中发送,跳转目标要是HTTP,凭证直接不出现。检查方法是看登录Cookie的Secure列,再确认跳转目标URL的协议。这类问题在测试环境特别容易漏,因为测试环境常常混用协议。
第三层检查:跳转插件自身的参数与请求头处理
Cookie作用域的问题排除掉之后,再看跳转插件这一层。插件发起跳转时如果用的是前端location跳转,浏览器会正常携带同域Cookie;用的服务端302,凭证携带取决于服务端返回的响应和浏览器的后续请求,逻辑上没差异。但有个坑得提一下:部分插件在拼接跳转URL时会主动清空referrer或者剥离参数,登录态识别要是依赖referrer里的某些标识,就被误伤了。
检查动作分两步:先在插件配置里确认跳转时有没有开启参数透传白名单,把登录相关的标识参数加进去;再用抓包工具看跳转请求的实际URL和请求头,对比跳转前页面里的凭证状态。限制条件是白名单别放开到全量透传,免得把敏感参数带到不该去的目标。
一个容易踩的坑:子域名之间的会话存储不共享
有些站点的登录态不完全靠Cookie,还在localStorage里存了一份用户标识。localStorage是按源隔离的,www.example.com和m.example.com各自独立,跳转过去后localStorage是空的。目标页的渲染逻辑要是依赖localStorage里的标识做首屏判断,就会出现“Cookie明明在、页面却显示未登录”的假象。检查方法是分别在两个子域名下看localStorage的key,确认目标页有没有降级到Cookie读取的逻辑。
实战复盘:一个家居流量站的跨子域名登录态修复过程
背景约束是这样的:某做家居内容与商品导流的团队,主站日均访问量在几万PV量级,活动页放在独立的m子域名下,服务器是两台中等配置的云主机加CDN,跳转插件负责把主站的商品入口按规则分发到活动页。他们的问题是,大促期间活动页的加购转化明显偏低。排查下来发现,大量用户到达活动页后处于未登录状态,加购还得重新登录,路径被拉长了不少。
踩的坑有两个。头一个,主站登录模块当初把Cookie的Domain写成了完整host,m子域名拿不到凭证,这是主因。第二个,他们的跳转插件在拼接活动页URL时对query做了统一编码,把原本携带的一个会话标识参数编码成了另一个形式,目标页解析失败。就算Cookie问题修好了,这条参数通道也是断的。两个问题叠在一起,他们一开始只盯着跳转插件查,绕了两天才回到Cookie作用域上。
调整过程:先把登录Cookie的Domain从完整host调整到父域级别,Path设为/。这一步之后,大部分用户的登录态在跳转后能正常识别了。接着修插件的参数拼接逻辑,把会话标识参数加入透传白名单,关掉该参数的二次编码。最后针对localStorage依赖的问题,让活动页在读取本地标识为空时回退到服务端会话校验。每一步都做了小流量验证,先放百分之五的流量观察活动页的登录识别率和加购点击,确认没有异常再逐步放量。
最终状态:活动页的未登录到达比例从原来的三成多降到了个位数,加购路径恢复顺畅。这个案例里没有哪一步是“跳转插件的高级配置”,全部是作用域和参数链路的对齐。反过来讲,当初要是一上来就去改跳转规则、加什么强制带Cookie的配置,大概率白折腾。
验证方案:怎么确认登录态继承真的生效了
配置改完不能只靠人工点几下。按下面这个顺序做验证,每一步都有明确的通过标准和停止条件。
- 第一步,单浏览器手动验证:清空Cookie后从主站登录,经跳转插件进入目标子域名,看目标页是否识别登录态。通过标准是页面头部显示已登录且能访问需要登录的接口。这一步不过就别往下走,回到Cookie作用域层查。
- 第二步,抓包核对请求头: 在跳转后的首个页面请求里看Cookie头是否包含登录凭证,看凭证的Domain和Path是否符合预期。请求头里没有凭证,说明作用域配置没生效,停止后续验证。
- 第三步,多子域名交叉验证: 站点有多个子域名都接入了跳转的,逐个验证,别只修了主路径。发现某个子域名仍失败,先确认它是否在Domain覆盖范围内。
- 第四步,小流量灰度观察: 按百分之五到百分之十的流量放量,盯目标页的登录识别率和关键转化点击。识别率没有明显提升或出现异常波动,触发回退,恢复原配置并重新排查。
回退边界要提前定好。Cookie作用域调整属于影响面较大的变更,建议低峰期操作,并保留原配置的快照。灰度期间目标页出现登录态错乱,比如显示登录但接口返回未授权,立即回退。这通常意味着凭证作用域扩大后,跟某些子域名的会话逻辑冲突了。
配置检查项汇总与升级处理条件
把上面几层的检查项收拢成一张清单,按发现信号、检查动作、停止或升级条件来组织,方便在项目里直接对照。
- 发现跳转后显示未登录:先看登录Cookie的Domain列是否为完整host。是则调整到父域级别,调整前确认子域名清单;子域名里有不受控的第三方托管页面,升级到站点负责人评估暴露面。
- 发现Cookie在但页面仍判未登录: 看Path是否覆盖目标页面路径,看目标页是否依赖localStorage。Path不覆盖则调整,localStorage依赖则补服务端校验回退逻辑。
- 发现跳转URL上的凭证参数丢失或变形: 检查插件的参数透传白名单和编码逻辑。修完后必须抓包复核,参数仍异常则暂停该跳转分支。
- 发现跨注册域跳转: 别试图共享Cookie,改用一次性token加目标站校验的方案,token必须设过期时间。业务方坚持要“无感继承”,升级说明技术边界,不要硬配。
- 发现协议或端口不一致: 确认Secure属性与目标协议匹配,测试环境同样要验,不能只在生产验。
- 灰度期间登录识别率无改善或出现错乱: 立即回退到配置快照,重新从第一层查起,不要在灰度中反复调参。
这套顺序的核心逻辑是:登录态继承的故障信号,八成以上指向Cookie作用域和凭证传递边界,跳转插件本身的配置问题反而是少数。排查时先把这个判断立住,能少走很多弯路。真正需要跳转插件承担的部分,是保证参数链路完整和提供清晰的回退路径,而不是替浏览器搬运凭证。