FastAPI 请求头与 Cookie:Web 世界的“身份证”与“通行证”
你每天上网的时候,有没有好奇过一个问题:
为什么你登录淘宝之后,不管点进“购物车”、“订单详情”还是“个人中心”,网站都知道你是谁?
答案并不是你每次点击都把用户名和密码重新发一遍(那样太危险了),而是你的浏览器在每一次请求的背后,悄悄携带了两样东西:请求头(Headers) 和 Cookie。
就像你进小区要刷门禁卡,进公司要刷工牌一样,请求头和 Cookie 就是 Web 世界里辨别你身份的“电子证件”。它们虽然看不见摸不着,但每一次的页面跳转、每一次的 API 调用,都在默默地发挥着作用。
本文将站在普通 Web 用户的视角,深入浅出地讲清楚请求头和 Cookie 到底是什么、有什么区别,以及 FastAPI 如何轻松读取它们,让你的后端服务具备识别用户、控制访问的能力。
一、什么是请求头?(快递包裹的面单)
想象一下,你收到一个快递。
快递盒里装着你买的手机(这是请求体 Body),快递面单上写着你的地址(这是路径参数),面单角落还有备注“轻拿轻放”(这是查询参数)。
但除了这些,快递盒上还有很多你看不到、但对于快递公司至关重要的信息:
寄件人信息(谁发的?)
快递单号(追踪用)
承运方式(空运还是陆运?)
包裹重量(计费依据)
面单版本号(系统识别用)
这些信息,就是 HTTP 请求中的 请求头(Headers)。
请求头是 HTTP 请求的“元数据(Metadata)”,即“描述数据的数据”。它不承载业务的核心内容(那是 Body 的活),而是告诉服务器:“我是谁”、“我从哪里来”、“我能接受什么”、“我的连接状态如何”。
在浏览器的开发者工具中(F12 → Network),你可以看到每一个请求都有密密麻麻的
Headers:
GET /api/profile HTTP/1.1 Host: example.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Accept: application/json Accept-Language: zh-CN,zh;q=0.9 Authorization: Bearer eyJhbGciOiJIUzI1NiIs... Cookie: session_id=abc123; theme=dark这些键值对,就是请求头。
二、什么是 Cookie?(商场的会员卡)
Cookie 本质上只是请求头里的一行特殊字段(Cookie: key=value),但它的地位太重要了,值得单独拎出来讲。
想象你去一家大型商场。
如果你每次买东西都要出示身份证(输入用户名密码),太麻烦。
于是商场给你办了一张会员卡(Cookie)。
卡上写着你的会员编号(用户 ID),商场系统通过这张卡识别你是老顾客。
每次你结账时,收银员扫一下卡(浏览器自动携带 Cookie),就知道你是谁、有没有折扣、积分多少。
但会员卡有有效期(过期时间),过期了需要重新激活(重新登录)。
Cookie 的本质,就是服务器颁发给浏览器的一小段文本信息(通常不超过 4KB),浏览器将其保存在本地,并在之后的每一次请求中自动携带回服务器。它主要用于保持会话状态(Session),让无状态的 HTTP 协议“记住”你是谁。
三、请求头 vs Cookie:一张表看懂区别
四、FastAPI 如何读取请求头?
在 FastAPI 中,读取请求头就像读取普通的函数参数一样简单。
1. 基础用法:使用 Header
from fastapi import FastAPI, Header app = FastAPI() @app.get("/user-agent/") async def get_user_agent(user_agent: str = Header(None)): return {"user_agent": user_agent}请求:GET /user-agent/,浏览器会自动携带 User-Agent 头。后端直接拿到字符串,比如 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"。
注意:Header 默认会把参数名转为小写加下划线的形式(user_agent → User-Agent),非常贴心。
2. 读取自定义请求头
前端或客户端可以发送自定义请求头(通常以 X- 开头):
@app.get("/custom-header/") async def custom_header( x_request_id: str = Header(None, description="自定义请求追踪ID"), x_api_version: str = Header("v1") ): return {"request_id": x_request_id, "api_version": x_api_version}前端请求时带上 X-Request-ID: req-2026-001,后端就能拿到这个值用于日志追踪。
3. 使用 Depends 封装 Header 解析
如果多个接口都需要解析某个请求头(比如 Authorization),可以封装成依赖项复用:
from fastapi import Depends, HTTPException async def get_token(authorization: str = Header(...)): if not authorization.startswith("Bearer "): raise HTTPException(401, "Invalid token format") return authorization.split(" ")[1] @app.get("/secure-data/") async def secure_data(token: str = Depends(get_token)): return {"token": token, "data": "这是敏感数据"}五、FastAPI 如何读取 Cookie?
1. 基础用法:使用 Cookie
FastAPI 提供了 Cookie 函数,用于从请求中提取 Cookie 值:
from fastapi import FastAPI, Cookie app = FastAPI() @app.get("/profile/") async def get_profile(session_id: str = Cookie(None)): if not session_id: return {"error": "未登录"} return {"message": f"欢迎回来,你的 session 是 {session_id}"}浏览器访问 /profile/ 时,会自动携带该域名下的所有 Cookie。Cookie("session_id") 就是从 Cookie: session_id=abc123 中提取 abc123。
2. 设置 Cookie(服务器响应)
除了读 Cookie,服务器还需要给客户端“发”Cookie。这需要使用 Response 对象:
from fastapi import FastAPI, Response app = FastAPI() @app.post("/login/") async def login(response: Response, username: str): # 假设验证成功,给浏览器发一个 Cookie response.set_cookie( key="session_id", value="abc123xyz", max_age=3600, # 1小时后过期 httponly=True, # 禁止 JavaScript 读取(防 XSS) secure=True, # 仅 HTTPS 传输 samesite="lax" # 防 CSRF 攻击 ) return {"message": f"{username} 登录成功"}浏览器收到响应后,会把 session_id=abc123xyz 存到本地。下次再访问同域名下的任何页面,浏览器会自动在请求头里带上 Cookie: session_id=abc123xyz,服务器就知道你是谁了。
六、请求头的五大核心作用(站在用户视角)
1. 身份认证(你是谁?)
最常见的用途。用户登录后,服务器返回一个 Token(JWT),前端在后续请求中通过 Authorization: Bearer <token> 头发送给服务器。这就是现代 Web 中最主流的无状态认证方式。
用户感知:你登录过一次之后,接下来几天打开 App 都不用重新输密码。
2. 内容协商(你想要什么格式?)
浏览器通过 Accept 头告诉服务器它能接受什么格式的数据。
Accept: application/json → 请返回 JSON
Accept: text/html → 请返回 HTML
Accept-Language: zh-CN → 请返回中文内容
这就是为什么同一个 API 地址,浏览器访问返回 HTML 页面,移动端访问返回 JSON 数据。
用户感知:你的浏览器自动请求中文页面,而你在国外时自动显示英文,无需手动切换。
3. 缓存控制(快不快?)
Cache-Control 头告诉服务器和浏览器如何缓存资源,直接影响你的网页加载速度。
用户感知:刷新微博首页时,LOGO 图片几乎是瞬间加载的,因为它被浏览器缓存了。
4. 安全防护(安不安全?)
Referer:告诉服务器你是从哪个页面跳转过来的,用于防盗链。
User-Agent:告诉服务器你用的什么浏览器,用于适配不同的渲染策略。
Origin:用于 CORS(跨域资源共享),控制哪些网站能调用你的 API。
用户感知:你在 A 网站点击链接跳转到 B 网站,B 网站知道你是从 A 来的,可能给你展示不同的欢迎语。
5. 追踪与调试(出问题了找谁?)
企业级应用中,请求头常用于链路追踪。每个请求携带一个唯一的 X-Request-ID,贯穿整个微服务调用链。
用户感知:当你在 App 上报 Bug 时,客服让你提供“请求编号”,就是这个 ID 帮助你快速定位问题。
七、Cookie 的三大核心作用(站在用户视角)
1. 会话管理(保持登录态)
这是 Cookie 最经典的应用。用户登录后,服务器下发一个包含用户 ID 的加密 Cookie,浏览器保存。后续每次请求都带着它,服务器就知道你是谁,无需重复登录。
用户感知:你登录淘宝后,关掉浏览器再打开,发现还是登录状态(只要没点“退出登录”)。
2. 用户偏好追踪
网站通过 Cookie 记住你的个性化设置。
用户感知:你把网站主题改成“深色模式”,下次打开还是深色模式;你在购物车加的商品,隔天还在。
3. 流量分析与个性化推荐
第三方追踪 Cookie(如 Google Analytics)记录你的浏览行为,用于分析流量和推送广告。
用户感知:你搜过“旅行箱”后,接下来几天到处都能看到旅行箱广告——这就是追踪 Cookie 的“功劳”。
八、最佳实践与安全指南
1. 敏感信息不要放在 Cookie 里
Cookie 虽然方便,但不要明文存储密码、信用卡号等极敏感信息。应该存一个无意义的 Session ID,真正的用户数据放在服务端数据库中。
2. 给 Cookie 加三道安全锁
response.set_cookie( key="session_id", value="abc123", httponly=True, # 🔒 防 XSS:JS 读不到,只能浏览器发送 secure=True, # 🔒 防窃听:仅 HTTPS 传输 samesite="lax" # 🔒 防 CSRF:跨站请求不携带 )这三道锁是现代 Web 安全的基本防线。
3. 使用 Token(JWT)替代 Cookie 做认证
在纯 API 服务(前后端分离)中,Cookie 依赖浏览器机制,移动端并不适用。更推荐在请求头中使用 Authorization: Bearer <JWT> 做认证,它是跨平台的。
4. 区分“请求头解析失败”与“未登录”
请求头解析失败返回 422(参数错误),认证失败返回 401(未授权),权限不足返回 403(禁止)。语义要清晰,不要混用。
@app.get("/admin") async def admin_panel(authorization: str = Header(None)): if not authorization: raise HTTPException(401, "请先登录") if authorization != "Bearer secret_admin_token": raise HTTPException(403, "权限不足,需要管理员权限") return {"message": "欢迎管理员"}5. 不要依赖 Referer 做唯一安全验证
Referer 头可以被浏览器策略或用户设置禁用,也可能被伪造。只把它作为辅助参考,不要作为唯一的安全防线。
九、结语
对于普通 Web 用户而言,请求头和 Cookie 就是你和互联网服务之间那层看不见的“默契”:
每次打开网页,浏览器都在背后默默递上你的“电子身份证”(请求头)。
每次点击“保持登录”,浏览器都在本地存下你的“会员卡”(Cookie)。
它们像空气一样无处不在,却默默承载着认证、追踪、安全、缓存、内容协商等无数关键功能,让你每一次点击都能得到个性化的、安全的、快速的响应。
对于 FastAPI 开发者而言,Header 和 Cookie 是框架提供的两把轻量级钥匙。你无需手动解析原始请求字符串,只需在路由函数参数中声明你要读取的字段,框架就会自动提取、自动注入,让你用最少的代码,构建出最安全的身份识别系统。