HTTP Header:基础与排障
验证:未记录
- 适用版本
- 未记录
- 验证范围
- 尚未登记验证记录。
HTTP(Hypertext Transfer Protocol,超文本传输协议)的 Header 是请求和响应的元数据。先确定谁写、谁读、在哪一跳发生变化,再判断配置是否正确。 字段名不区分大小写,字段值是否区分取决于具体字段。
1. 常用字段与职责#
| 方向 | Header | 作用/容易踩的坑 |
|---|---|---|
| 请求 | Host |
HTTP/1.1 的目标站点;HTTP/2、HTTP/3 通常用 :authority;用于虚拟主机和路由,不能直接信任它来拼重置密码链接 |
| 请求 | Authorization |
身份凭证;必须验证,日志脱敏,避免带凭证跨域重定向 |
| 请求/响应 | Content-Type |
当前消息体格式,例如 application/json;与文件扩展名不是一回事 |
| 请求 | Accept、Accept-Encoding |
希望收到的媒体类型、压缩编码;不是声明请求体格式 |
| 请求 | Origin |
发起请求的源(协议、主机、端口);非浏览器可伪造,不能用于身份认证 |
| 请求/响应 | Cookie/Set-Cookie |
浏览器发送已有 Cookie/服务端设置 Cookie;每个 Cookie 独立一个 Set-Cookie,不要用逗号合并 |
| 请求 | X-Forwarded-For、X-Forwarded-Proto |
代理传递客户端地址、原始协议;仅信任受控代理写入的部分 |
| 请求/响应 | traceparent、X-Request-ID |
跨服务追踪/请求关联;前者是标准追踪上下文,后者是常见约定,都不是授权依据 |
| 响应 | Cache-Control、ETag、Vary |
缓存策略、表示版本、缓存按哪些请求头区分 |
| 请求 | If-None-Match |
带上此前的 ETag;GET 内容未变通常返回 304,客户端复用缓存正文 |
| 响应 | Location、Retry-After |
重定向目标、建议何时重试;仍需限制重试次数并避免重复写入 |
Cookie 常用属性:Secure 限 HTTPS,HttpOnly 限制脚本读取,SameSite 控制跨站携带;SameSite=None 需要 Secure。这些不能替代服务端会话验证或防跨站请求伪造措施。
2. 三类最常见问题#
缓存:不要把用户 A 的数据交给用户 B#
| 内容 | 策略示例 | 含义 |
|---|---|---|
| 含凭证/敏感业务数据 | Cache-Control: no-store |
不存储本次响应;不会自动清除历史缓存 |
| 可保存、每次必须校验 | Cache-Control: no-cache + ETag |
可以缓存,但复用前必须向服务端验证 |
| 用户私有、短时可缓存 | Cache-Control: private, max-age=60 |
允许浏览器缓存,禁止共享缓存存储 |
| 文件名含内容哈希的公开静态资源 | Cache-Control: public, max-age=31536000, immutable |
适合不可变资源;更新时更换文件名 |
Vary: Accept-Encoding 区分压缩版本;响应根据 Origin 动态变化时使用 Vary: Origin,并核对 CDN(Content Delivery Network,内容分发网络)的实际缓存键。ALB 本身不是内容缓存。参见 HTTP 缓存标准。
跨域:CORS 不等于鉴权#
完整的 OPTIONS → 业务请求 → 浏览器放行过程,见 浏览器跨域请求时序。
CORS(Cross-Origin Resource Sharing,跨源资源共享)决定浏览器脚本能否读取跨源响应。简单请求可能已执行后才被浏览器限制读取,因此不能靠 CORS 阻止写操作。
- 非简单请求通常先发
OPTIONS预检,携带Origin、Access-Control-Request-Method和所需请求头。 - 服务端返回匹配的
Access-Control-Allow-Origin/Methods/Headers;实际业务响应也需要正确的跨域头。 - 携带 Cookie 等凭证时,允许源必须是具体源,不能是
*;还需Access-Control-Allow-Credentials: true,浏览器请求也需启用凭证。 - 多源白名单由应用逐个匹配,拒绝未知源;不能把多个源用逗号写入 Allow-Origin,也不能无条件回显 Origin。
- 预检一般不带业务凭证;允许合法预检通过,同时保持实际接口鉴权。读取自定义响应头需
Access-Control-Expose-Headers。
协议细节见 Fetch CORS 标准。
安全响应头:由浏览器执行#
| Header | 作用 | 上线要求 |
|---|---|---|
Strict-Transport-Security |
HSTS(HTTP Strict Transport Security,HTTP 严格传输安全),让浏览器后续强制 HTTPS | 从短 max-age 开始;确认所有子域后再考虑 includeSubDomains,不要直接 preload |
Content-Security-Policy |
CSP(Content Security Policy,内容安全策略),限制脚本、资源、嵌入等 | 先用应用的 Content-Security-Policy-Report-Only 观察,再强制;动态 nonce 由应用生成 |
X-Content-Type-Options: nosniff |
阻止部分内容类型猜测 | 先确保脚本/样式的 Content-Type 正确 |
X-Frame-Options: DENY |
禁止页面被嵌入 frame | 有合法嵌入需求时评估 CSP frame-ancestors |
HSTS 不替代首次访问的 HTTP→HTTPS 重定向,也不保证代理到后端加密。浏览器已保存的策略不会因为服务端删除这个头而立即消失。
3. Hands-on:验收真实预发布服务#
前提:已有你负责的 HTTPS 预发布 API(Application Programming Interface,应用程序接口),一个无副作用的 GET 路径、一个已配置 ETag 的公开静态文件,以及一个配置了 CORS 的接口。以下地址和路径必须替换;这是一套真实服务验收步骤,不会替你创建服务。
第一步:观察响应,而不是只看配置#
export BASE='https://api.staging.example.com'
export READ_PATH='/health/ready'
export CORS_PATH='/api/catalog'
export ORIGIN='https://portal.staging.example.com'
umask 077
HEADER_WORK=$(mktemp -d)
# GET 观察真实处理路径;HEAD 可能由不同代码处理。暂不跟随重定向。
curl -sS --max-time 15 -D "$HEADER_WORK/get.headers" -o /dev/null "$BASE$READ_PATH"
cat "$HEADER_WORK/get.headers"记录状态码、Content-Type、安全头、缓存头和关联 ID。用浏览器 Network 同时核对;如果是 301/302,先检查 Location 的协议、域名,避免盲目跟随掩盖循环。
第二步:检查缓存协商#
export ASSET_PATH='/assets/app.CONTENT_HASH.js' # 换成真实、有 ETag 的文件
curl -sS --max-time 15 -D "$HEADER_WORK/asset.headers" -o /dev/null "$BASE$ASSET_PATH"
ETAG=$(awk 'tolower($1)=="etag:" {sub(/^[^:]*:[ \t]*/, ""); sub(/\r$/, ""); print; exit}' "$HEADER_WORK/asset.headers")
test -n "$ETAG" && curl -sS --max-time 15 -D - -o /dev/null -H "If-None-Match: $ETAG" "$BASE$ASSET_PATH"未变化时预期 304;无 ETag 则这项不适用,不是 curl 故障。敏感接口另检查 no-store;不要用真实凭证进行 curl -v 后把输出贴到工单。
第三步:预检、实际响应、拒绝源分别验证#
curl -sS --max-time 15 -D - -o /dev/null -X OPTIONS "$BASE$CORS_PATH" -H "Origin: $ORIGIN" -H 'Access-Control-Request-Method: GET' -H 'Access-Control-Request-Headers: authorization,content-type'
curl -sS --max-time 15 -D - -o /dev/null "$BASE$CORS_PATH" -H "Origin: $ORIGIN"
curl -sS --max-time 15 -D - -o /dev/null "$BASE$CORS_PATH" -H 'Origin: https://untrusted.example'预检预期 2xx 且允许所需方法和头。实际响应若需登录可返回 401,但仍应满足允许源的跨域策略;未知源不能得到匹配自己的 Allow-Origin。curl 不执行 CORS:最终必须在真实前端源的浏览器中验证正常调用成功、未知源脚本无法读取;有会话时再验 Cookie 场景。
第四步:验证代理信任边界#
在预发布发送 -H 'X-Forwarded-For: 198.51.100.77',通过受限应用日志观察最终解析的客户端地址;不要公开全量 Header 回显接口。直连 ALB 且 append 模式下,伪造值在前,ALB 追加其看到的连接来源。应用必须按已配置的可信代理链从右向左解析,不能简单取第一个值。有 CDN 时重新核对完整代理链与绕过入口的可能性。
测试后删除本次临时目录:rm -r -- "$HEADER_WORK"。
4. 排障顺序#
| 现象 | 优先检查 |
|---|---|
| curl 成功、浏览器失败 | OPTIONS 状态、实际响应跨域头、凭证模式、浏览器 Console |
| 页面突然脚本/样式失败 | CSP 拒绝信息、nosniff 与 Content-Type 是否冲突 |
| HTTPS 重定向循环 | 应用是否正确识别可信代理传来的原始协议 |
| 客户端地址全是代理/可被伪造 | X-Forwarded-For 模式、可信代理范围、是否能直连后端 |
| 不同用户读到相同私有内容 | Cache-Control、CDN 缓存键、历史缓存清理 |
| 应用已改头、外部没变 | 逐跳比较应用→代理→ALB→CDN;检查覆盖和缓存 |
AWS 的具体配置与回退见 ALB Header 实战。