文章目录
- 一、通用头部
-
- [1. **Cache-Control**](#1. Cache-Control)
- [2. **Connection**](#2. Connection)
- [3. **Date**](#3. Date)
- [4. **Pragma**](#4. Pragma)
- [5. **Transfer-Encoding**](#5. Transfer-Encoding)
- [6. **Upgrade**](#6. Upgrade)
- [7. **Via**](#7. Via)
- [8. **Warning**](#8. Warning)
- 二、请求头部
-
- [1. **Host**](#1. Host)
- [2. **User-Agent**](#2. User-Agent)
- [3. **Accept**](#3. Accept)
- [4. **Accept-Language**](#4. Accept-Language)
- [5. **Accept-Charset**](#5. Accept-Charset)
- [6. **Accept-Encoding**](#6. Accept-Encoding)
- [7. **Authorization**](#7. Authorization)
- [8. **Cookie**](#8. Cookie)
- [9. **Referer**(注意拼写错误,应为Referrer)](#9. Referer(注意拼写错误,应为Referrer))
- [10. **Origin**](#10. Origin)
- [11. **If-None-Match**](#11. If-None-Match)
- [12. **If-Modified-Since**](#12. If-Modified-Since)
- [13. **If-Match**](#13. If-Match)
- [14. **Range**](#14. Range)
一、通用头部
1. Cache-Control
作用:控制缓存机制的核心头部,指示请求和响应中的所有缓存机制。
详细说明:
Cache-Control: public, max-age=3600, must-revalidate
指令类型:
可缓存性指令:
public:响应可以被任何缓存(浏览器、代理服务器、CDN)缓存private:响应只能被用户浏览器缓存,不允许共享缓存(如CDN)存储no-cache:必须先向服务器验证(发送条件请求)才能使用缓存no-store:禁止缓存任何响应内容,每次都必须从服务器获取
到期指令:
max-age=<seconds>:资源被认为新鲜的最大秒数(相对于请求时间)s-maxage=<seconds>:仅适用于共享缓存(如代理服务器),覆盖max-agemax-stale[=<seconds>]:客户端愿意接受过期但不超过指定秒数的响应min-fresh=<seconds>:客户端希望获取在指定秒数内保持新鲜的响应
重新验证和重新加载指令:
must-revalidate:一旦缓存过期,必须向源服务器验证proxy-revalidate:与must-revalidate相同,但仅适用于共享缓存immutable:表示响应内容永不变,浏览器可以直接使用缓存而不验证
其他指令:
no-transform:禁止代理服务器转换资源(如压缩图片)only-if-cached:只从缓存获取,不请求网络,如果无缓存则返回504
应用示例:
# 静态资源可以缓存一年
Cache-Control: public, max-age=31536000, immutable
# 需要经常验证的用户内容
Cache-Control: private, no-cache
# 敏感数据不缓存
Cache-Control: no-store
2. Connection
作用:控制当前事务完成后,是否关闭网络连接。
详细说明:
keep-alive:保持连接打开,允许在同一连接上发送多个请求close:本次响应后关闭连接- 自定义值:可以传递自定义的头部,如
Connection: X-My-Custom-Header
工作机制:
- HTTP/1.0 默认关闭连接,需要显式指定
keep-alive - HTTP/1.1 默认保持连接,需要关闭时才指定
close
相关头部:
-
Keep-Alive:指定keep-alive行为的参数Keep-Alive: timeout=5, max=1000 # timeout: 保持空闲连接的时间(秒) # max: 该连接上最多可发送的请求数
应用示例:
http
# 客户端请求保持连接
GET / HTTP/1.1
Connection: keep-alive
# 服务器响应保持连接
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: timeout=5, max=100
3. Date
作用:消息生成的日期和时间,使用固定的GMT格式。
详细说明:
Date: Tue, 15 Aug 2023 10:30:00 GMT
格式要求:
- 必须使用格林威治标准时间(GMT)
- 必须使用RFC 7231定义的固定格式
- 星期和月份必须使用英文缩写
有效格式:
Date: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
示例:
Date: Mon, 14 Aug 2023 14:30:00 GMT
Date: Wed, 21 Oct 2015 07:28:00 GMT
重要用途:
- 计算缓存新鲜度 :与
Cache-Control的max-age结合使用 - 调试和日志:帮助排查时间相关问题
- 条件请求 :作为
If-Modified-Since的参考时间
注意事项:
- 服务器时钟必须准确同步
- 如果响应包含
Age头部,缓存时间应从Date计算 - 代理服务器不应修改
Date头部
4. Pragma
作用:HTTP/1.0的遗留头部,用于向后兼容。
详细说明:
Pragma: no-cache
历史背景:
- HTTP/1.0时代没有
Cache-Control头部 Pragma用于控制缓存行为- HTTP/1.1引入
Cache-Control后逐渐废弃
唯一有效值:
no-cache:与Cache-Control: no-cache效果相同
当前用法:
http
# 为了向后兼容,通常两者一起使用
Cache-Control: no-cache
Pragma: no-cache
特殊情况:
- 某些旧代理服务器可能只识别
Pragma - 某些API要求同时发送两个头部确保兼容性
5. Transfer-Encoding
作用:指定传输消息体时使用的编码方式。
详细说明:
主要值:
-
chunked(分块传输):
- 在不知道内容长度时使用
- 将数据分成一系列分块发送
- 每个分块包含:长度(十六进制) + CRLF + 数据 + CRLF
- 以长度为0的分块结束
示例:
httpHTTP/1.1 200 OK Transfer-Encoding: chunked 4\r\n Wiki\r\n 5\r\n pedia\r\n 0\r\n \r\n -
gzip, deflate, br:
- 表示内容已被压缩
- 通常与
Content-Encoding配合使用 - 注意:
Transfer-Encoding关注传输过程,Content-Encoding关注内容格式
-
compress(已废弃):
- 使用LZW算法压缩
- 现代浏览器已不支持
重要特性:
-
可以指定多个值,按顺序应用:
Transfer-Encoding: gzip, chunked # 先gzip压缩,再分块传输 -
与
Content-Length互斥:- 如果指定了
Transfer-Encoding: chunked,则不能有Content-Length - 两者同时存在时,
Transfer-Encoding优先
- 如果指定了
使用场景:
- 动态内容:服务器生成内容时不知道最终大小
- 流式传输:实时推送数据(如服务器推送事件)
- 大文件上传:客户端上传未知大小的文件
6. Upgrade
作用:请求将连接升级到其他协议。
详细说明:
升级HTTP到WebSocket:
http
# 客户端请求升级
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
# 服务器同意升级
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
升级到HTTP/2:
http
# 客户端请求HTTP/2
GET / HTTP/1.1
Host: example.com
Connection: Upgrade, HTTP2-Settings
Upgrade: h2c
HTTP2-Settings: <base64url encoding of HTTP/2 SETTINGS payload>
# 服务器响应
HTTP/1.1 101 Switching Protocols
Connection: Upgrade
Upgrade: h2c
支持的协议:
websocketh2(HTTP/2 over TLS)h2c(HTTP/2 over cleartext TCP)IRC/6.9RTA/x11
注意事项:
- 101状态码:升级成功返回101 Switching Protocols
- Connection头必须 :必须同时包含
Connection: Upgrade - 服务器选择权:服务器可以拒绝升级,继续使用当前协议
- TLS连接:HTTPS连接的升级有特殊规则
7. Via
作用:追踪消息经过的代理服务器路径。
详细说明:
格式:
Via: 1.1 proxy1, 1.1 proxy2, 1.0 proxy3
每个代理添加自己的信息:
协议版本 主机名 [注释]
示例:
http
# 请求经过多个代理
GET / HTTP/1.1
Host: example.com
Via: 1.1 proxy1.internal.net, 1.0 proxy2.external.com
# 每个代理追加自己的信息
# 最终头部可能变成:
Via: 1.1 client, 1.1 proxy1, 1.0 proxy2, 1.1 proxy3
版本标识:
1.0:HTTP/1.01.1:HTTP/1.12.0:HTTP/23.0:HTTP/3
注释内容:
代理服务器可以添加额外信息:
Via: 1.1 proxy1 (Cache-Miss)
Via: 1.0 proxy2 (Compressed)
重要用途:
- 调试:追踪请求路径,排查网络问题
- 防止循环:代理服务器检查是否已经处理过该请求
- 统计:分析网络拓扑和流量路径
- 安全:识别请求是否经过不可信代理
相关头部:
Forwarded:更现代的替代方案,包含更详细的信息X-Forwarded-For:非标准但广泛使用的客户端IP记录
8. Warning
作用:携带关于消息状态或转换的额外警告信息(已废弃)。
详细说明:
格式:
Warning: 110 proxy "Response is stale"
结构:警告码 警告代理 "警告文本" [日期]
标准警告码:
- 110:Response is stale - 响应已过期
- 111:Revalidation failed - 重新验证失败
- 112:Disconnected operation - 断开连接操作
- 113:Heuristic expiration - 启发式过期
- 199:Miscellaneous warning - 杂项警告
- 214:Transformation applied - 已应用转换
- 299:Miscellaneous persistent warning - 持久性杂项警告
示例:
http
HTTP/1.1 200 OK
Warning: 110 proxy "Response is stale"
Warning: 111 cache "Revalidation failed"
Warning: 214 proxy "Transformation applied"
Date: Tue, 15 Aug 2023 10:30:00 GMT
Cache-Control: max-age=3600
废弃原因:
- 复杂性:实现和维护复杂
- 混淆:容易与HTTP状态码混淆
- 替代方案:现代缓存控制机制更完善
- 有限使用:实际使用场景有限
现代替代方案:
- 使用特定的状态码
- 在响应体中包含错误信息
- 使用自定义头部
二、请求头部
1. Host
作用:指定请求的目标服务器域名和端口号(HTTP/1.1必需)。
详细说明:
基本格式:
Host: www.example.com:443
或
Host: api.example.com
主要用途:
- 虚拟主机支持:多个域名共享同一IP地址时,服务器根据Host区分
- 端口指定:显式指定非标准端口
- HTTP/1.1要求:所有HTTP/1.1请求必须包含Host头部
示例:
http
GET /index.html HTTP/1.1
Host: www.example.com
GET /api/data HTTP/1.1
Host: api.example.com:8080
特殊情况:
-
默认端口:HTTP默认80,HTTPS默认443,通常省略
-
IPv6地址 :需要用方括号括起来
Host: [2001:db8::1]:8080 -
HTTP/1.0:可选,但推荐包含
安全注意事项:
- Host头注入:未验证Host头部可能导致安全漏洞
- 相对路径解析:代理服务器需要正确解析Host
- 重定向:重定向时可能需要保留原始Host
2. User-Agent
作用:标识发起请求的客户端软件信息。
详细说明:
标准格式:
User-Agent: Mozilla/5.0 (平台信息) 引擎信息 浏览器信息 扩展信息
组成部分:
- 产品标识符:主要软件名称和版本
- 注释:括号内的详细系统信息
- 可选扩展:其他相关信息
常见示例:
Chrome on Windows:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Firefox on macOS:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:89.0) Gecko/20100101 Firefox/89.0
Mobile Chrome:
Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36
cURL:
curl/7.77.0
主要用途:
- 统计分析:了解用户设备分布
- 功能适配:根据客户端能力返回不同内容
- 反爬虫:识别自动化请求
- 调试:客户端问题诊断
服务器端处理:
javascript
// 常见的User-Agent检测
const ua = req.headers['user-agent'];
if (ua.includes('Mobile')) {
// 返回移动端页面
} else if (ua.includes('bot')) {
// 处理爬虫
} else if (ua.includes('curl')) {
// 命令行请求
}
注意事项:
- 易伪造:User-Agent可以被轻易修改
- 隐私问题:可能泄露过多用户信息
- 复杂性:字符串格式混乱,解析困难
- 标准化:WHATWG正在制定Client Hints作为替代方案
3. Accept
作用:告诉服务器客户端能够处理的MIME类型及其优先级。
详细说明:
基本语法:
Accept: 类型/子类型; q=权重
- 多个类型用逗号分隔
- q值范围0.000-1.000,默认1.000
- 星号(*)表示通配符
常见示例:
# 优先HTML,其次是XML,最后是任意类型
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
# JSON优先,XML其次
Accept: application/json, application/xml;q=0.9
# 图片格式偏好
Accept: image/webp, image/apng, image/*;q=0.8
详细示例:
http
GET /data HTTP/1.1
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
Accept-Charset: utf-8, iso-8859-1;q=0.5
Accept-Encoding: gzip, deflate
Accept-Language: en-US, en;q=0.9, zh-CN;q=0.8, zh;q=0.7
质量值(q)规则:
Accept: text/html, text/plain;q=0.5, text/x-dvi;q=0.8
等价于:
- text/html → q=1.0
- text/x-dvi → q=0.8
- text/plain → q=0.5
特殊值:
-
媒体范围:
Accept: text/* Accept: image/* Accept: */* -
参数扩展:
Accept: text/html;level=1;q=0.4 Accept: text/html;q=0.7;level=1
服务器响应:
服务器根据Accept头部选择最合适的Content-Type:
http
HTTP/1.1 200 OK
Content-Type: application/json
Vary: Accept
如果无法满足,返回406 Not Acceptable。
Vary头部配合:
Vary: Accept
告诉缓存服务器根据Accept头部缓存不同版本。
4. Accept-Language
作用:声明客户端偏好的自然语言。
详细说明:
语法格式:
Accept-Language: 语言标签[;q=权重]
语言标签格式:
- 基础语言 :
en,zh,ja - 地区变体 :
en-US,zh-CN,zh-TW - 扩展 :
zh-Hans,zh-Hant - 脚本 :
sr-Latn(塞尔维亚语拉丁字母)
示例:
# 英语优先,简体中文其次
Accept-Language: en-US, en;q=0.9, zh-CN;q=0.8
# 多种语言偏好
Accept-Language: zh-CN, zh;q=0.9, en-US;q=0.8, en;q=0.7, ja;q=0.6
# 简单的偏好
Accept-Language: fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5
质量值处理:
Accept-Language: da, en-gb;q=0.8, en;q=0.7
服务器选择顺序:da → en-gb → en
服务器响应:
- 内容协商:返回对应语言版本的资源
- 多语言网站:根据Accept-Language重定向
- 默认语言:没有匹配时返回默认语言
响应示例:
http
HTTP/1.1 200 OK
Content-Language: zh-CN
Content-Type: text/html; charset=utf-8
Vary: Accept-Language
实际应用:
javascript
// 服务器端语言检测
function detectLanguage(acceptLanguage) {
const languages = acceptLanguage.split(',')
.map(lang => {
const [language, q = 'q=1'] = lang.split(';');
const quality = parseFloat(q.split('=')[1]) || 1;
return { language: language.trim(), quality };
})
.sort((a, b) => b.quality - a.quality);
return languages[0]?.language || 'en';
}
注意事项:
- 用户设置:通常来自浏览器语言设置
- 缓存问题 :需要配合
Vary: Accept-Language - 精确匹配 :
zh-CN和zh需要分别处理 - 权重继承 :
zh的权重会传递给zh-CN等子标签
5. Accept-Charset
作用:声明客户端支持的字符编码(已废弃)。
详细说明:
历史背景:
Accept-Charset: utf-8, iso-8859-1;q=0.5
- HTTP/1.1引入,但现代浏览器已基本不再发送
- 被Content-Type中的charset参数替代
曾经的作用:
- 字符集协商:客户端告知支持的字符集
- 编码回退:设置备选字符集
- 国际化:支持多语言字符集
废弃原因:
- UTF-8普及:现代Web普遍使用UTF-8
- Content-Type优先:响应中直接指定charset
- 冗余:Accept头部可以包含charset参数
- 实现复杂:服务器处理逻辑复杂
现代替代方案:
Content-Type: text/html; charset=utf-8
或
Accept: text/html;charset=utf-8
兼容处理:
如果收到Accept-Charset,服务器可以:
- 忽略(现代常见做法)
- 解析并选择合适字符集
- 返回406 Not Acceptable(如果完全不支持)
6. Accept-Encoding
作用:声明客户端支持的内容编码(通常是压缩算法)。
详细说明:
语法:
Accept-Encoding: 编码1, 编码2;q=权重
常用编码:
- gzip:使用LZ77算法,广泛支持
- deflate:使用zlib格式
- br:Brotli算法,压缩率更高
- compress:UNIX compress程序格式(已废弃)
- identity:不压缩
- *:任意编码(不建议)
示例:
# 常见现代浏览器
Accept-Encoding: gzip, deflate, br
# 优先Brotli,其次是gzip
Accept-Encoding: br;q=1.0, gzip;q=0.8, deflate;q=0.6, *;q=0.1
# 不接收压缩
Accept-Encoding: identity
质量值示例:
Accept-Encoding: br;q=1.0, gzip;q=0.8, deflate;q=0.6
选择顺序:br → gzip → deflate
服务器响应:
http
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
特殊情况:
- 无匹配编码:返回406 Not Acceptable或使用identity
- 代理处理:代理不应重新压缩已压缩内容
- 分块传输:压缩后再分块传输
实际配置:
nginx
# Nginx配置示例
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_vary on;
gzip_min_length 1000;
注意事项:
- CPU开销:压缩需要服务器CPU资源
- 小文件:太小的文件压缩可能适得其反
- 动态内容:每次请求都需要重新压缩
- 缓存 :需要配合
Vary: Accept-Encoding
7. Authorization
作用:向服务器提供身份验证凭据。
详细说明:
基本语法:
Authorization: 认证方案 凭据
主要认证方案:
1. Basic认证:
Authorization: Basic base64(username:password)
示例:
Authorization: Basic dXNlcjpwYXNzd29yZA==
# base64解码:user:password
特点:
- 明文传输(base64不是加密)
- 适合HTTPS环境
- 简单但安全性低
2. Bearer认证(令牌):
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
示例:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
用途:
- OAuth 2.0访问令牌
- JWT令牌
- API密钥
3. Digest认证:
Authorization: Digest username="Mufasa",
realm="testrealm@host.com",
nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
uri="/dir/index.html",
response="6629fae49393a05397450978507c4ef1",
opaque="5ccc069c403ebaf9f0171e9517f40e41"
特点:
- 挑战-响应机制
- 密码不直接传输
- 比Basic安全,但复杂
4. AWS签名:
Authorization: AWS4-HMAC-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20130524/us-east-1/s3/aws4_request,
SignedHeaders=host;range;x-amz-date,
Signature=fe5f80f77d5fa3beca038a248ff027d0445342fe2855ddc963176630326f1024
自定义方案:
Authorization: Custom token123456789
服务器响应:
-
验证成功:正常处理请求
-
验证失败 :401 Unauthorized
httpHTTP/1.1 401 Unauthorized WWW-Authenticate: Basic realm="Access to the staging site" -
过期/无效:可能返回403 Forbidden
WWW-Authenticate头部:
服务器要求认证时返回:
WWW-Authenticate: Bearer realm="example",
error="invalid_token",
error_description="The access token expired"
安全最佳实践:
- HTTPS:始终在TLS上使用认证
- 令牌过期:设置合理的令牌有效期
- 刷新机制:使用refresh token更新访问令牌
- 范围限制:令牌应有最小必要权限
- 安全存储:客户端安全存储凭据
8. Cookie
作用:存储客户端状态信息,每次请求自动发送给服务器。
详细说明:
基本格式:
Cookie: name=value[; name2=value2...]
完整示例:
Cookie: sessionId=abc123; userId=user456; theme=dark; lang=en-US
Cookie属性(在Set-Cookie响应中设置,请求中只发送名称值对):
- Expires/Max-Age:过期时间
- Domain:生效域名
- Path:生效路径
- Secure:仅HTTPS传输
- HttpOnly:禁止JavaScript访问
- SameSite:跨站请求控制
SameSite属性详解:
Set-Cookie: sessionId=abc123; SameSite=Lax
- Strict:严格模式,完全禁止跨站发送
- Lax:宽松模式,允许GET导航请求
- None:允许跨站,但必须设置Secure
服务器端设置示例:
http
HTTP/1.1 200 OK
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
Set-Cookie: userPrefs=darkMode; Path=/; Max-Age=86400
Cookie大小限制:
- RFC规范:至少4096字节
- 实际实现:不同浏览器限制不同
- 数量限制:每个域名通常50-150个
现代替代方案:
- HTTP状态令牌:无状态认证
- IndexedDB:客户端大量数据存储
- LocalStorage:简单键值存储
安全最佳实践:
javascript
// 安全的Cookie设置
res.setHeader('Set-Cookie', [
`sessionId=${token}; HttpOnly; Secure; SameSite=Strict; Path=/`,
`csrfToken=${csrf}; Secure; SameSite=Strict; Path=/`
]);
9. Referer(注意拼写错误,应为Referrer)
作用:指示当前请求的来源页面URL。
详细说明:
基本格式:
Referer: <完整或部分URL>
示例:
Referer: https://www.google.com/search?q=http+headers
Referer: https://example.com/page1.html
Referer: // 无协议的相对URL(少见)
发送规则:
- 页面导航:点击链接、表单提交时发送
- 资源加载:图片、脚本、样式等子资源通常发送
- 同源策略:通常发送完整URL
- 跨源策略:可能被Referrer-Policy限制
不发送Referer的情况:
- 直接输入地址:地址栏直接输入
- 书签访问:从书签打开
- 隐私模式:某些隐私浏览模式
- HTTPS→HTTP:安全降级时不发送(默认)
- 页面重定向:某些重定向可能不发送
Referrer-Policy策略:
Referrer-Policy: no-referrer-when-downgrade
策略值:
- no-referrer:从不发送
- no-referrer-when-downgrade:HTTPS→HTTP时不发送(默认)
- origin:只发送源(协议+主机+端口)
- origin-when-cross-origin:跨源时发送源,同源时发送完整
- same-origin:同源时发送完整,跨源时不发送
- strict-origin:只发送源,且HTTPS→HTTP时不发送
- strict-origin-when-cross-origin:跨源时发送源,同源时发送完整,且HTTPS→HTTP时不发送
- unsafe-url:总是发送完整URL(不安全)
安全考虑:
-
信息泄露 :可能泄露敏感查询参数
Referer: https://example.com/reset-password?token=abc123 -
隐私保护:需要适当限制Referer信息
-
防盗链:基于Referer的资源保护
防盗链实现:
nginx
location /images/ {
# 只允许本域和信任域名引用
valid_referers none blocked example.com *.example.com google.com;
if ($invalid_referer) {
return 403;
}
}
10. Origin
作用:指示请求的来源(协议+域名+端口),用于CORS机制。
详细说明:
基本格式:
Origin: <协议>://<主机>[:端口]
示例:
Origin: https://www.example.com
Origin: http://localhost:3000
Origin: null # 文件协议或无来源
Origin: file:// # 本地文件
发送条件:
- 跨域请求:所有跨域请求
- 同源请求:POST、PATCH、DELETE方法
- 排除情况:简单GET、HEAD通常不发送
CORS流程示例:
http
# 简单请求(自动发送Origin)
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://www.example.com
# 服务器响应
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://www.example.com
http
# 预检请求(复杂请求先发送OPTIONS)
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://www.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header
# 服务器预检响应
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header
特殊值处理:
- null来源 :
file://协议- 沙盒化iframe
- 数据URL
- 不透明源:某些隐私上下文
- 源继承:iframe内嵌页面继承父页面源
与Referer的区别:
| 特性 | Origin | Referer |
|---|---|---|
| 内容 | 仅协议+主机+端口 | 完整URL |
| 用途 | CORS安全机制 | 来源追踪 |
| 隐私 | 信息较少 | 信息较多 |
| 控制 | 不能完全禁用 | 可通过策略控制 |
服务器验证:
javascript
// Express.js CORS验证
app.use((req, res, next) => {
const allowedOrigins = ['https://example.com', 'https://admin.example.com'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
}
// 预检请求处理
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Max-Age', '86400');
return res.sendStatus(204);
}
next();
});
11. If-None-Match
作用:条件请求,基于ETag验证资源是否变化。
详细说明:
基本格式:
If-None-Match: "etag-value"
If-None-Match: "etag1", "etag2", ...
If-None-Match: * # 任何ETag
工作原理:
- 客户端首次获取资源,服务器返回ETag
- 客户端缓存资源并记录ETag
- 后续请求时发送If-None-Match
- 服务器比较ETag,决定返回304或200
完整流程:
http
# 第一次请求
GET /api/data HTTP/1.1
HTTP/1.1 200 OK
ETag: "abc123"
Content-Type: application/json
Cache-Control: max-age=3600
{"data": "value"}
# 第二次请求(带ETag验证)
GET /api/data HTTP/1.1
If-None-Match: "abc123"
# 资源未修改
HTTP/1.1 304 Not Modified
ETag: "abc123"
# 资源已修改
HTTP/1.1 200 OK
ETag: "def456"
Content-Type: application/json
{"data": "updated value"}
ETag类型:
-
强验证器 :内容完全匹配
ETag: "abc123" -
弱验证器 :语义相同即可(加W/前缀)
ETag: W/"abc123" # 弱ETag If-None-Match: W/"abc123"
特殊值"*":
- 用于PUT操作检查资源是否存在
- 对于GET,匹配任何现有资源
与If-Modified-Since结合:
http
GET /resource HTTP/1.1
If-None-Match: "abc123"
If-Modified-Since: Tue, 15 Aug 2023 10:30:00 GMT
服务器优先使用ETag验证。
实现示例:
javascript
// Express.js ETag验证
app.get('/api/data', (req, res) => {
const data = getDataFromDatabase();
const etag = generateETag(data);
// 检查If-None-Match
const clientETag = req.headers['if-none-match'];
if (clientETag && clientETag === etag) {
return res.status(304).end();
}
res.setHeader('ETag', etag);
res.json(data);
});
function generateETag(data) {
// 简单实现:使用内容的哈希
const hash = require('crypto')
.createHash('md5')
.update(JSON.stringify(data))
.digest('hex');
return `"${hash}"`;
}
12. If-Modified-Since
作用:基于时间戳的条件请求。
详细说明:
基本格式:
If-Modified-Since: <HTTP日期>
完整示例:
http
GET /article.html HTTP/1.1
If-Modified-Since: Tue, 15 Aug 2023 10:30:00 GMT
工作原理:
- 服务器响应包含Last-Modified
- 客户端缓存并记录时间
- 后续请求发送If-Modified-Since
- 服务器比较时间决定是否返回新内容
服务器响应逻辑:
javascript
function handleConditionalRequest(req, res, filePath) {
const stats = fs.statSync(filePath);
const lastModified = stats.mtime.toUTCString();
const clientModified = req.headers['if-modified-since'];
if (clientModified) {
const clientDate = new Date(clientModified);
const serverDate = new Date(lastModified);
// 注意:服务器时间 <= 客户端时间,表示未修改
if (serverDate <= clientDate) {
res.writeHead(304, {
'Last-Modified': lastModified,
'Cache-Control': 'public, max-age=3600'
});
return res.end();
}
}
// 返回新内容
res.writeHead(200, {
'Last-Modified': lastModified,
'Content-Type': 'text/html'
});
res.end(content);
}
精度问题:
- 秒级精度:HTTP日期只精确到秒
- 1秒内修改:无法检测
- 时钟漂移:依赖服务器时钟准确
常见场景:
- 静态文件:适合修改不频繁的文件
- 动态内容:需要精确控制Last-Modified
- CDN缓存:配合Cache-Control使用
与ETag对比:
| 方面 | If-Modified-Since | If-None-Match |
|---|---|---|
| 依据 | 修改时间 | 内容哈希 |
| 精度 | 秒级 | 字节级 |
| 时钟 | 依赖时钟同步 | 无依赖 |
| 开销 | 较小 | 需要计算哈希 |
| 适用 | 静态文件 | 动态内容 |
最佳实践:
nginx
# Nginx配置
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Last-Modified "";
# 禁用Last-Modified,强制使用ETag
}
13. If-Match
作用:乐观锁机制,确保资源未被修改。
详细说明:
基本格式:
If-Match: "etag-value"
If-Match: "etag1", "etag2", ...
If-Match: * # 任何ETag都匹配
使用场景:
- 资源更新:防止覆盖他人修改
- 删除操作:确保删除的是正确版本
- 并发控制:实现乐观锁
完整示例:
http
# 1. 获取资源
GET /article/123 HTTP/1.1
HTTP/1.1 200 OK
ETag: "version1"
Content-Type: application/json
{"id": 123, "title": "Original", "version": 1}
# 2. 更新资源(携带ETag)
PUT /article/123 HTTP/1.1
If-Match: "version1"
Content-Type: application/json
{"id": 123, "title": "Updated", "version": 2}
# 3a. ETag匹配,更新成功
HTTP/1.1 200 OK
ETag: "version2"
# 3b. ETag不匹配,其他人已修改
HTTP/1.1 412 Precondition Failed
特殊值"*"的意义:
If-Match: *
- 对于更新:资源必须存在
- 对于删除:无条件删除(危险)
- 避免使用:可能导致数据丢失
服务器实现:
javascript
// REST API的乐观锁实现
app.put('/resources/:id', async (req, res) => {
const resourceId = req.params.id;
const clientETag = req.headers['if-match'];
const newData = req.body;
// 获取当前资源
const currentResource = await db.get(resourceId);
const currentETag = generateETag(currentResource);
// 验证ETag
if (!clientETag) {
return res.status(428).json({ error: 'Precondition required' });
}
if (clientETag !== '*' && clientETag !== currentETag) {
return res.status(412).json({
error: 'Precondition failed',
currentETag: currentETag
});
}
// 更新资源
const updatedResource = await db.update(resourceId, newData);
const newETag = generateETag(updatedResource);
res.setHeader('ETag', newETag);
res.json(updatedResource);
});
结合版本号:
json
{
"id": 123,
"title": "Article",
"content": "...",
"version": 5,
"updatedAt": "2023-08-15T10:30:00Z"
}
http
PUT /articles/123 HTTP/1.1
If-Match: "v5"
Content-Type: application/json
{"title": "New Title", "version": 5}
错误处理:
- 412 Precondition Failed:ETag不匹配
- 428 Precondition Required:需要If-Match头部
- 400 Bad Request:ETag格式错误
14. Range
作用:请求资源的特定部分,实现断点续传和流式传输。
详细说明:
基本格式:
Range: bytes=<start>-<end>
Range: bytes=<start>-
Range: bytes=-<suffix-length>
Range: bytes=<range1>, <range2>, ...
示例:
Range: bytes=0-499 # 前500字节
Range: bytes=500-999 # 第二个500字节
Range: bytes=500- # 从500字节到末尾
Range: bytes=-500 # 最后500字节
Range: bytes=0-499,1000-1499 # 多个范围(少见)
服务器响应:
单范围成功:
http
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-499/12345
Content-Length: 500
Content-Type: application/octet-stream
[数据]
多范围成功:
http
HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=boundary_string
--boundary_string
Content-Type: application/octet-stream
Content-Range: bytes 0-499/12345
[第一部分数据]
--boundary_string
Content-Type: application/octet-stream
Content-Range: bytes 1000-1499/12345
[第二部分数据]
--boundary_string--
不支持范围请求:
http
HTTP/1.1 200 OK
Content-Length: 12345
Content-Type: application/octet-stream
[完整数据]
断点续传实现:
javascript
// Express.js 范围请求处理
const fs = require('fs');
const path = require('path');
app.get('/download/:filename', (req, res) => {
const filePath = path.join(__dirname, 'files', req.params.filename);
const stat = fs.statSync(filePath);
const fileSize = stat.size;
// 解析Range头部
const range = req.headers.range;
if (range) {
const parts = range.replace(/bytes=/, "").split("-");
const start = parseInt(parts[0], 10);
const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
const chunkSize = (end - start) + 1;
const file = fs.createReadStream(filePath, { start, end });
res.writeHead(206, {
'Content-Range': `bytes ${start}-${end}/${fileSize}`,
'Accept-Ranges': 'bytes',
'Content-Length': chunkSize,
'Content-Type': 'application/octet-stream'
});
file.pipe(res);
} else {
// 完整文件
res.writeHead(200, {
'Content-Length': fileSize,
'Content-Type': 'application/octet-stream'
});
fs.createReadStream(filePath).pipe(res);
}
});
相关头部:
Accept-Ranges:
http
Accept-Ranges: bytes
Accept-Ranges: none
Content-Range:
Content-Range: bytes <start>-<end>/<total>
Content-Range: bytes */<total> # 无效范围时
If-Range:
If-Range: "etag-value"
If-Range: <HTTP-date>
- 如果条件满足,返回范围内容
- 如果条件不满足,返回完整内容
应用场景:
- 大文件下载:分段下载,断点续传
- 视频播放:随机访问,跳转播放
- 图片懒加载:渐进式加载
- HTTP流:实时数据传输
注意事项:
- 字节对齐:某些编码可能需要对齐
- 缓存问题:部分响应需要特殊缓存处理
- 代理支持:确保代理正确处理206状态码
- 多范围限制:实现复杂,支持有限