Cookie 详解:从产生到安全,一次讲透

面试官问:Cookie 和 localStorage 有什么区别?为什么 Cookie 能 "自动带上",localStorage 不行?SameSite=Strict 和 Lax 到底差在哪?很多人能背出 HttpOnly、Secure 几个属性名,但一被问到 Cookie 是怎么产生的、浏览器凭什么决定带不带、跨域为什么就失效,就答不上来了。

这篇文章从 HTTP 无状态讲起,把 Cookie 的产生、属性、发送规则、安全攻防一次讲透。文末附面试高频题。


1. 从 HTTP 无状态说起:Cookie 为什么存在

HTTP 协议是无状态的:每一次请求都是独立的,服务器不会 "记得" 你。

这带来一个直接问题:早期的电商网站没法实现 "购物车"。用户往购物车加了商品,再点下一步,服务器已经不认识他了。

Cookie 就是为解决这个问题诞生的:服务器在响应里塞一张 "小纸条"(键值对),浏览器保存下来,下次请求时自动把小纸条原样带回。服务器一看纸条,就知道 "哦,还是刚才那个人"。

一句话:Cookie 是 HTTP 从 "无状态" 走向 "有状态" 的补丁。它本身不解决 "你是谁",只是提供了一种 "浏览器帮我记住、自动回传" 的机制。


服务器想给浏览器 "发一张纸条",就在响应里加一个 Set-Cookie 响应头:

ini 复制代码
HTTP/1.1 200 OK
Content-Type: text/html
Set-Cookie: session_id=abc123; Path=/; HttpOnly

浏览器收到后,把 session_id=abc123 连同属性一起存进本地 Cookie 仓库。

服务端代码(Node.js/ Express):

php 复制代码
res.cookie('session_id', 'abc123', {
 path: '/',
 httpOnly: true,
 maxAge: 7 * 24 * 3600 * 1000, // 7 天
 sameSite: 'lax'
});

Python / Flask:

python 复制代码
from flask import make_response
resp = make_response('ok')
resp.set_cookie('session_id', 'abc123', path='/', httponly=True, max_age=604800, samesite='Lax')
return resp

2.2 浏览器回传:Cookie 请求头

之后浏览器再向同一站点发起请求时,会从本地 Cookie 仓库里找出匹配的 Cookie,拼成 Cookie 请求头自动带上:

vbnet 复制代码
GET /cart HTTP/1.1
Host: shop.example.com
Cookie: session_id=abc123

服务器读取请求头里的 Cookie,就能识别出会话。整个过程浏览器自动完成,JS 和用户都不需要干预。

2.3 完整生命周期


一个完整的 Set-Cookie 长这样:

ini 复制代码
Set-Cookie: user=zhang; Domain=example.com; Path=/api; Max-Age=3600; Secure; HttpOnly; SameSite=Lax; Priority=High
属性 作用 示例 常见坑
Name=Value Cookie 本身的内容 user=zhang 值要 encodeURIComponent,不能有分号逗号空格
Domain 发给哪些域名 Domain=example.com 写错域名 Cookie 直接失效;不写则只发给设置它的主机
Path 发给哪些路径 Path=/api 默认取请求路径的目录;/ 表示全站
Expires 过期时间(绝对时间) Expires=Wed, 21 Oct 2026 07:28:00 GMT 会话级 Cookie 不写它,关浏览器就删
Max-Age 存活秒数(相对时间) Max-Age=3600 与 Expires 同时存在时 Max-Age 优先
Secure 仅 HTTPS 发送 Secure 本地 HTTP 环境(除 localhost)会失效
HttpOnly JS 无法读取 HttpOnly 防 XSS 偷 Cookie 的关键,但不影响自动发送
SameSite 跨站请求是否携带 SameSite=Strict/Lax/None None 必须搭配 Secure;默认值各浏览器不同(Chrome 默认 Lax)
Priority 淘汰优先级 Priority=High/Low 浏览器 Cookie 超额时先淘汰 Low
Partitioned 第三方 Cookie 分区存储 Partitioned 较新(CHIPS),用于替代被禁的第三方 Cookie

按功能可以把属性分成三组:

几个容易记混的点单独说一下:

Expires vs Max-Age :Expires 是 "绝对到期时间点",Max-Age 是 "还能活多少秒"。两者同时出现时,Max-Age 优先(RFC 6265 规定)。

Domain 的两种模式

  • 不写 Domain → host-only Cookie :只发给设置它的那个主机(比如只发 api.example.com),子域收不到

  • 写 Domain=example.com → 发给 example.com 及其所有子域

注意:Domain 不能设为公共后缀(如 .com、.cn),浏览器会拒绝。

Path 默认值 :如果不写 Path,浏览器默认取 "发起该请求的 URL 的目录部分"。比如请求 a.com/user/profil... 时设置的 Cookie,默认 Path=/user。想全站生效就显式写 Path=/。

  • __Host- / __Secure- 前缀:给 Cookie 名字加前缀可以 "强制" 安全属性:

  • __Secure-name:必须带 Secure 且走 HTTPS

  • __Host-name:必须带 Secure、Path=/、且不能有 Domain(即必须是 host-only)

这是浏览器层面的硬校验,能防止某些 "Cookie 注入 / 前缀混淆" 攻击,适合放登录凭证。


4.1 三种创建方式

方式一:服务端响应头(最常用)

ini 复制代码
Set-Cookie: theme=dark; Path=/; Max-Age=2592000

方式二:JS 的 document.cookie

javascript 复制代码
// 设置(不会覆盖其他 Cookie,同名同路径才覆盖)
document.cookie = 'theme=dark; Path=/; Max-Age=2592000';
// 读取(返回所有可读 Cookie 拼接的字符串)
console.log(document.cookie); // "theme=dark; user=zhang"

注意:document.cookie 读不到 HttpOnly 的 Cookie,这是设计使然。

方式三:Cookie Store API(较新)

csharp 复制代码
await cookieStore.set({ name: 'theme', value: 'dark', path: '/' });
const cookies = await cookieStore.getAll();

兼容性有限(Chrome/Edge 支持较好),生产环境用前先查兼容性。

4.2 修改与删除

  • 修改 :重新 Set 一条同名、同 Domain、同 Path 的 Cookie,值会被覆盖;任一项不同则视为新 Cookie(会出现两条)。
  • 删除:设置 Max-Age=0(或 Expires 设为过去时间),同名同 Domain 同 Path 覆盖即可删除。
ini 复制代码
// 删除
document.cookie = 'theme=; Path=/; Max-Age=0';
ini 复制代码
// 服务端删除
Set-Cookie: theme=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT

最容易踩的坑:删除时 Path / Domain 必须和创建时完全一致,否则删不掉(会悄悄新建一条空值 Cookie)。


5. 浏览器如何决定 "带不带这个 Cookie"

很多人以为 "Cookie 是同源的,跨域就不带",其实不完全对。浏览器判断是否携带某个 Cookie,是按下面这套规则逐条校验的:

5.1 Domain 匹配

  • host-only Cookie:请求主机名必须完全等于设置主机名
  • 带 Domain 的 Cookie:请求主机名必须等于 Domain 值或它的子域

5.2 Path 匹配

请求路径必须以 Path 值为前缀。Path=/api 只匹配 /api/...,不匹配 /apix(按 / 边界匹配)。

5.3 协议与过期

  • Secure Cookie 只在 HTTPS(或 localhost 等安全上下文)下发送
  • 已过期的 Cookie 不发送,同时浏览器会把它清理掉

5.4 同站判定(SameSite 的底层逻辑)

这里要引入一个容易混淆的概念:

同源(Same-Origin)≠ 同站(Same-Site)

同源:协议 + 域名 + 端口三者完全一致

同站:只看 "站"(eTLD+1,即注册域名),协议不同、端口不同

不算跨站

例如 shop.comshop.com:协议不同(跨源),但同站a.comb.com 才是跨站。

SameSite 管的就是 "跨站请求带不带 Cookie":

  • Strict:跨站请求一律不带(包括用户点了站外链接跳转过来)
  • Lax:跨站请求中,只有顶级导航的 GET 请求 带(点链接跳转、地址栏输入会带),图片 /iframe/fetch 等子资源请求不带 ------ Chrome 80+ 的默认值
  • None:跨站请求都带,但必须同时满足 Secure(仅 HTTPS)
  • 第一方 Cookie:由你正在访问的站点(地址栏里的站点)设置的 Cookie
  • 第三方 Cookie :页面上嵌入的其他站点资源(广告 iframe、第三方脚本、CDN)设置的 Cookie,它们的 "站" 和地址栏里的站不同

第三方 Cookie 是广告追踪和跨站登录(SSO)的基础,也是近年浏览器重点打击的对象(见第 7 节)。


一个最常见的误解:把 Cookie 当成认证方案本身

实际上:

Cookie 只是 "运输工具",Session / Token 才是 "凭证内容"。

认证方案可以坐在 Cookie 这辆车里,也可以自己走别的路(比如 Authorization 请求头)。

场景 凭证放哪 说明
Session 时代 sessionId 放 Cookie 服务端查表验证
Token 时代 accessToken 放请求头、refreshToken 放 httpOnly Cookie 双 Token 机制(见《Token 与 RefreshToken 一次讲透》)
无 Cookie 方案 请求头 / 移动端安全存储 App、跨端场景

所以 Cookie 的典型价值是:浏览器自动携带 ,前端不用手动管理 "把凭证塞进每个请求";代价是自动携带也带来了 CSRF 风险(见第 8 节)。

6.1 和浏览器其他存储对比

维度 Cookie localStorage sessionStorage IndexedDB
大小 单条约 4KB,每域几十条 约 5MB 约 5MB 大(GB 级)
自动随请求发送
过期机制 Expires/Max-Age 手动清除 关标签页清除 手动清除
JS 可读 受 HttpOnly 限制
作用域 Domain/Path 控制 同源 同源(按标签页) 同源
典型用途 会话凭证、偏好 前端缓存、主题 一次性页面状态 复杂离线数据

选型建议 :需要 "服务器每次都能自动收到" 的数据(会话凭证、A/B 实验 ID)用 Cookie;纯前端本地数据用 localStorage;敏感凭证不要放 localStorage(XSS 可读,详见《Token 与 RefreshToken 一次讲透》)。


你在浏览 news.com,页面里嵌了一个广告 iframe(ad.com)。这个 iframe 里的脚本请求 ad.com 的接口,ad.com 在响应里种下 Cookie:

于是 ad.com 能在所有嵌了它广告的网站上识别同一个用户,实现跨站追踪。

7.2 为什么浏览器要禁它

第三方 Cookie 是跨站追踪的基础设施,严重冲击隐私。近几年的浏览器政策:

  • Safari:ITP(智能防追踪)默认拦截第三方 Cookie
  • Firefox:默认拦截第三方 Cookie
  • Chrome:逐步推进禁用第三方 Cookie(2024 年起分阶段灰度),并推出替代方案

7.3 开发者该怎么办

  • 需要跨站携带的 Cookie:设置 SameSite=None; Secure(但会被浏览器逐步拦截)
  • 新的替代方案:CHIPS(Partitioned Cookie) ------ 给 Cookie 加 Partitioned 属性,让它在 "每个顶级站点" 下独立存储,既能跨站工作又不被全局追踪
  • 长期方向:把第三方 Cookie 的追踪能力迁移到第一方数据(自己的域名、服务端会话、邮箱登录等)
ini 复制代码
// CHIPS 示例:第三方 Cookie 分区存储
Set-Cookie: ad_click=1; SameSite=None; Secure; Partitioned

8. 安全:Cookie 常见攻击与防护

8.1 攻击面总览

8.2 逐个攻防

威胁 原理 防护
XSS 窃取 Cookie 恶意脚本执行 document.cookie 读走凭证 凭证 Cookie 加 HttpOnly;配合 CSP、输入输出转义
CSRF(跨站请求伪造) 浏览器会自动携带 Cookie,恶意站点诱导用户发请求 SameSite=Lax/Strict;校验 Origin/Referer;CSRF Token
中间人窃听 明文 HTTP 下 Cookie 被抓包 Secure 属性 + 部署 HSTS 强制 HTTPS
Cookie 投毒 / 篡改 Cookie 里直接存用户名、角色等明文,攻击者改内容 Cookie 只存不透明标识符(如 sessionId),服务端查表验证;重要数据签名
会话固定攻击 攻击者先种一个 sessionId,诱导用户用它的 ID 登录 登录成功后重新生成 sessionId(轮换)
子域 Cookie 注入 任意子域可给父域写 Cookie(若 Domain 设了父域) 敏感 Cookie 用 __Host- 前缀(强制 host-only + Secure + Path=/)

几个要点展开:

HttpOnly 不是万能的 :它只挡 JS 读取,不挡自动发送,也挡不住 XSS 通过其他方式(比如表单提交、图片加载)把请求发出去 ------ 所以 XSS 本身还是要治(CSP、转义、第三方脚本管控)。

CSRF 的本质:攻击者不发你的 Cookie,但浏览器会自动带。防御核心是让 "跨站请求" 和 "合法请求" 长得不一样:SameSite 让浏览器跨站不带,Origin/Referer 校验让服务端能区分,CSRF Token 让伪造请求没有合法凭据。

HSTS:Strict-Transport-Security 响应头告诉浏览器 "这个域名以后只能走 HTTPS",防止中间人先把 HTTPS 降级成 HTTP 再抓 Cookie。


9.1 数量与大小

RFC 6265 要求浏览器至少支持:

  • 每个 Cookie ≤ 4096 字节(约 4KB)
  • 每个域 ≥ 50 个 Cookie
  • 总数 ≥ 3000 个

主流浏览器实际按此基线执行(Chrome 每域约 180 个、Firefox 约 150 个,随版本变化),超出后按 "最不活跃的先淘汰"(Priority 属性可干预)。

9.2 性能影响

每个请求都会自动带上匹配的 Cookie。Cookie 体积越大、条数越多,每个请求的头部就越臃肿:

  • 首屏静态资源(图片、JS、CSS)也会带 Cookie → 浪费带宽
  • 解决:静态资源放独立 CDN 域名 / 子域 (比如 static.example.com 不设置 Domain 到 example.com),避免携带会话 Cookie

别把大对象放 Cookie:用户偏好、购物车这种低频大对象,优先放 localStorage / 服务端;Cookie 里只放 "每次请求都必须让服务器知道" 的最小凭证。


10. 常见的坑

  1. localhost 下 Secure Cookie 失效 :本地 HTTP 调试时带 Secure 的 Cookie 不会发送(Chrome 对 localhost 视为安全上下文,可用 HTTPS 或去掉 Secure 调试)。
  1. Domain 写错导致 "登录态丢失" :在 api.example.com 设置了不带 Domain 的 Cookie,前端页面在 www.example.com 收不到。
  1. 删除 Cookie 条件不一致:删除时 Path/Domain 与创建时不一致 → 删不掉。
  1. SameSite 默认值差异:老项目假设默认是 None,Chrome 80 后默认 Lax,跨站 iframe 里的登录态突然失效。
  1. SameSite=None 忘了加 Secure:浏览器直接拒绝该 Cookie。
  1. Cookie 值没编码:值里带分号、逗号、空格会导致解析错乱,需要 encodeURIComponent。
  1. 静态资源背 Cookie:CDN 资源没和 API 域名分离,每个静态请求都背一堆 Cookie,拖慢首屏。
  1. HttpOnly Cookie 无法用 JS 删除:需要服务端在响应里 Set-Cookie 过期(和上一篇 "登出只清前端" 是同一个坑)。
  1. 把敏感数据明文放 Cookie:用户能直接改,服务端必须验签或查表。
  1. 移动端 App 没有 Cookie 机制:原生请求不会自动带 Cookie,需要自己管理凭证(Keychain/Keystore)。

11. 面试高频 Q&A

Q1:Cookie 和 localStorage 有什么区别?

最核心的差异是 "会不会自动随请求发送":Cookie 会(这也是会话凭证选它的原因),localStorage 不会。其次是容量(Cookie 约 4KB / 条,localStorage 约 5MB)、过期机制(Cookie 有 Expires/Max-Age,localStorage 只能手动清)和作用域(Cookie 可以按 Domain/Path 精细控制,localStorage 严格同源)。

Q2:HttpOnly 和 Secure 有什么区别?

HttpOnly 管的是 "JS 能不能读 "(防 XSS 窃取),Secure 管的是 "什么协议下才发送"(防明文窃听,仅 HTTPS)。两者是互补关系,敏感凭证 Cookie 一般都要加。

Q3:SameSite=Strict 和 Lax 的区别?为什么默认不是 Strict?

Strict 跨站一律不带,Lax 允许 "顶级导航的 GET"(点站外链接跳转)带。不用 Strict 做默认是因为体验:用户从邮件 / 外部链接点进你的站,Strict 下 Cookie 不发送,登录态和偏好直接 "消失",体验像没登录一样;Lax 在保证基本防 CSRF 的同时保住这个场景。

Q4:为什么 Cookie 能自动带上,而 token 要手动加?

Cookie 是浏览器原生机制:匹配 Domain/Path/ 过期规则后,浏览器自动把 Cookie 拼进请求头。token 放在请求头需要前端代码主动注入(比如 axios 拦截器)。Cookie 省事但有 CSRF 风险,所以现在更推荐 "token 放请求头 + refreshToken 放 httpOnly Cookie" 的组合。

Q5:第三方 Cookie 是什么?为什么被禁用?

页面里嵌入的其他站点资源(广告 iframe、第三方脚本)设置的 Cookie 就是第三方 Cookie。它能被广告商用来跨站追踪用户,侵犯隐私,所以 Safari/Firefox/Chrome 逐步默认拦截。替代方案包括 CHIPS(Partitioned)和第一方数据策略。

Q6:Cookie 能跨域吗?

"跨域带 Cookie" 和 "跨域设 Cookie" 要分开看:SameSite=None; Secure 可以让 Cookie 在跨站请求中携带(第三方 Cookie);设置 Cookie 本身由 Domain 控制(不能设到别的域)。而 withCredentials/credentials 是前端跨域请求时是否带上 Cookie 的开关(需要服务端配合 CORS 允许)。

Q7:登录状态存 Cookie 安全吗?

安全的关键不在 "存哪",而在 "存什么 + 怎么保护":只存不透明标识符(sessionId,服务端查表),配合 HttpOnly + Secure + SameSite 三件套,登录后轮换 ID,就是主流且安全的方案;直接存明文用户信息或可被 JS 读取的长期凭证才是危险的。


12. 总结

一句话:

Cookie 是服务器下发给浏览器、由浏览器自动回传的 "身份凭证小纸条",是 HTTP 无状态协议走向 "有状态" 的关键补丁。

  • 工作原理:Set-Cookie 下发 → 浏览器存储 → 匹配 Domain/Path/ 过期 /SameSite 规则 → 自动携带 → 服务端识别
  • 关键属性:Domain/Path 管 "发给谁",Expires/Max-Age 管 "活多久",HttpOnly/Secure/SameSite 管 "安不安全"
  • 安全三件套:HttpOnly + Secure + SameSite,再加登录后轮换 sessionId、服务端验签
  • Cookie 是 "运输工具",Session / Token 是 "凭证内容",不要混淆
  • 限制与性能:单条约 4KB、每域几十条,静态资源要和 API 域名分离,避免无谓的 Cookie 开销

Cookie 是前端面试和日常开发都绕不开的基础设施。希望这篇能帮你把它的原理、属性、发送规则和安全边界一次理清。有问题评论区见。

相关推荐
卡布鲁3 小时前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄4 小时前
JavaScript 隐式全局变量解析
javascript
汉堡大王95275 小时前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大5 小时前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师5 小时前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学5 小时前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端
甜到心里的蛋糕6 小时前
加解密技术详解:纯前端如何实现 AES / SM4 / RSA / 国密 SM2
前端·密码学
拖孩6 小时前
代码我能全交给 AI,流量主这 500 个访客它一个都替不了我
前端·后端·微信小程序
2601_963870216 小时前
基于SSM的特产代购系统
java·前端