饼干踩过的坑:这些年我在Cookie上栽过的真实跟头
第一次被Cookie坑,是几年前做一个前后端分离的项目。本地调得好好的,一上线登录就失效,刷新一下又退出。查了一下午,最后发现是SameSite搞的鬼。那会儿Chrome 80刚把SameSite的默认值从None改成Lax。说白了就是,你从a.com发起的请求要去b.com拿资源,浏览器默认不带b.com的Cookie了。
以前写代码的人根本不管这个属性,因为不写就是None,现在不写变成Lax,跨站的POST和iframe里的请求全挂。我那个项目登录后有第三方支付回跳,跳回来带了session,但浏览器不发这个Cookie,后端认不出人,直接当未登录处理。抓包一看请求头,Cookie字段是空的,当时一脸懵。

解决办法是后端Set-Cookie时显式写SameSite=None;Secure。注意Secure必须带上,不写的话Chrome会直接忽略这个SameSite=None。这俩是绑一起的。第二个坑是Domain。很多人以为www.xxx.com和.xxx.com是一回事,其实差一个前导点,效果完全不同。

写www.xxx.com,只有这个子域名能用;写.xxx.com,所有子域名共享。我有次把Cookie设在api.xxx.com上,前端在www.xxx.com读,怎么都读不到,翻文档才发现是这里。Path也容易踩。Cookie默认的Path是设置它的那个路径。

你在/api/login上Set-Cookie,不写Path,这Cookie就只在/api/下有效,/user/profile读不到。想让全站生效,直接写Path=/。第三个是HttpOnly。这是安全属性,加上之后JS就没办法通过document.cookie读。
听着挺好,能防XSS。但你要是不提前跟前端说,人家会来找你,说读不到token。这种问题排查很费时间,浏览器不报错,就是默默读不出来。我的经验是,身份相关的敏感Cookie老实加HttpOnly,前端要用的数据走接口返回,别硬塞进Cookie让JS去读。
第四个坑是跨域。前端用fetch请求接口,默认不带Cookie。得在配置里加credentials: 'include'。后端也要配合,Access-Control-Allow-Credentials设成true。而且允许跨域的Origin不能写星号,得写具体域名。
这几个条件缺一个,Cookie就传不过去。我见过有人前端加了credentials,忘了后端,在那儿调了两小时。还有大小限制。单个Cookie上限4KB。有人图省事,把整个JWT塞进Cookie,里面还塞用户信息、权限、一堆claims,编出来超过4KB,浏览器直接把这个Set-Cookie丢掉,什么提示都没有。
你去看响应头,它写着呢,但很多人不看。排查这类问题,我一般直接开Chrome DevTools的Application面板,左边Cookies点开,看哪些Cookie实际存下来了,Domain、Path、Expires都对不对。比瞎猜快多了。
编码也是个隐形的坑。Cookie值里带分号、逗号、空格,会被截断。你存一个带空格的字符串,取出来只剩前半段。稳妥做法是encodeURIComponent编一下再存,读的时候decodeURIComponent解回来。最后说说过期时间。不写Expires或者Max-Age的,就是会话Cookie,关掉浏览器就没了。
做“记住我”功能得显式设置过期时间。但注意格式是GMT字符串,不是时间戳,写错了浏览器不认。Cookie这东西看着简单,一行Set-Cookie的事,上手才发现到处是细节。我的建议是,出问题别硬猜,打开开发者工具看实际存了什么、发了什么,八成的坑看一眼就清楚了。