入门
HTTP 请求与响应
HTTP 是请求-响应协议。客户端发送请求(方法、路径、头、可选的请求体),服务器以状态、头和响应体回应。HTTP/1.1 基于文本,HTTP/2 和 HTTP/3 使用二进制分帧。
# HTTP Request
GET /api/users HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: application/json
# HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 45
{"id": 1, "name": "Alice", "email": "[email protected]"}URL 结构
URL 标识资源。scheme 选择协议。host 通过 DNS 解析。省略时使用默认端口。fragment(#) 仅在客户端使用,在发送到服务器之前会被剥离。查询字符串通常用于过滤/分页。
# Generic URL format
scheme://userinfo@host:port/path?query#fragment
# Example
https://user:[email protected]:8443/api/v1/users?page=2#section
# Components
# scheme - https (protocol)
# userinfo - user:pass (rare, deprecated)
# host - example.com (domain or IP)
# port - 8443 (default: 80 for http, 443 for https)
# path - /api/v1/users (resource location)
# query - page=2 (key=value pairs, & separated)
# fragment - section (client-only, never sent to server)HTTP 版本概览
HTTP/0.9 是一个最小的原型。HTTP/1.0 增加了头和内容类型,但每个请求都打开一个新连接。HTTP/1.1 引入了持久连接、必需的 Host 头(启用虚拟主机)和管道化。HTTP/2 增加了二进制多路复用。HTTP/3 运行在 QUIC 之上,消除了传输层的队头阻塞。
# HTTP/0.9 (1991) - single GET, no headers
GET /page.html
# HTTP/1.0 (1996) - headers, status codes, multiple types
GET /page.html HTTP/1.0
# HTTP/1.1 (1997) - persistent connections, Host header, pipelining
GET /page.html HTTP/1.1
Host: example.com
# HTTP/2 (2015) - binary, multiplexing, header compression
# (negotiated via ALPN or h2c upgrade)
# HTTP/3 (2022) - over QUIC (UDP-based)
# (negotiated via Alt-Svc header)连接生命周期
HTTP/1.1 默认保持 TCP 连接打开(keep-alive),避免了重复握手的开销。多个请求可以通过一个连接发送。Connection: close 头标志最后一个请求。HTTP/2 在单个连接上多路复用多个流。Keep-Alive 超时和最大值可在服务器上配置。
# HTTP/1.1 persistent connection (keep-alive)
GET /page1 HTTP/1.1
Host: example.com
Connection: keep-alive
# (server responds, connection stays open)
HTTP/1.1 200 OK
Content-Length: 128
Connection: keep-alive
# Client reuses same TCP connection for next request
GET /page2 HTTP/1.1
Host: example.com
# Closing the connection
GET /final HTTP/1.1
Host: example.com
Connection: close消息格式
HTTP 消息以起始行(请求行或状态行)开始,后 跟头、空行和可选的请求体。行使用 CRLF 行尾。头名称不区分大小写。空行是必需的,标志请求体的开始。请求体可以是任意长度(分块)或固定长度(Content-Length)。
# Request format
<METHOD> <PATH> <VERSION>
<Header-Name>: <value>
...
<optional body>
# Response format
<VERSION> <STATUS> <REASON>
<Header-Name>: <value>
...
<optional body>
# Lines end with CRLF (
). Headers are case-insensitive.
# A blank line (
) separates headers from body.Telnet / 原始 HTTP 调试
可以使用 nc 或 telnet(端口 80)或 openssl s_client(HTTPS)在 TCP 上直接发送原始 HTTP。这对调试非常有价值。在 HTTP/1.1+ 上必须包含 Host 头。两个空行终止请求。curl -v 显示包括头在内的网络格式对话。
# Manual HTTP request via netcat/nc
nc example.com 80
GET / HTTP/1.1
Host: example.com
# (press Enter twice to send blank line)
# Using openssl for HTTPS
openssl s_client -connect example.com:443
GET / HTTP/1.1
Host: example.com
# curl with --http1.1 to force version
curl -v --http1.1 https://example.com
curl -v --http2 https://example.com
curl -v --http3 https://example.comHTTP 方法
GET — 获取资源
GET 检索资源的表示。它必须是安全的(无副作用)且幂等的(可重复)。GET 请求可缓存、可加入书签。虽然规范允许请求体,但不建议使用——代理和 CDN 可能会剥离它。改用查询参数进行过滤。
# Basic GET
GET /api/users/42 HTTP/1.1
Host: api.example.com
Accept: application/json
# GET with query parameters
GET /api/users?role=admin&active=true HTTP/1.1
Host: api.example.com
# GET should be safe & idempotent
# - safe: no server state change
# - idempotent: repeating yields same result
# - cacheable: yes
# - body allowed but discouraged (some clients/servers reject)POST — 创建资源
POST 提交数据以进行处理。它用于创建由服务器分配 URI 的新资源(例如 /api/users → /api/users/43)。POST 既不安全也不幂等——提交两次可能创建两个资源。201 Created 响应应包含指向新资源的 Location 头。
# POST creating a new resource
POST /api/users HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 52
{"name": "Bob", "email": "[email protected]", "age": 30}
# Response typically 201 Created with Location header
HTTP/1.1 201 Created
Location: /api/users/43
Content-Type: application/json
{"id": 43, "name": "Bob"}
# POST is neither safe nor idempotent.
# Repeating may create duplicate resources.PUT — 替换资源
PUT 用请求体替换目标 URI 处的整个资源。它是幂等的——重复相同的 PUT 产生相同的最终状态。PUT 针对特定 URI(例如 /users/43),而 POST 针对集合。如果在 PUT 中省略某个字段,通常会将其删除(完全替换)。
# PUT replaces the entire resource at the given URI
PUT /api/users/43 HTTP/1.1
Host: api.example.com
Content-Type: application/json
Content-Length: 60
{"id": 43, "name": "Robert", "email": "[email protected]", "age": 31}
# Idempotent: sending the same PUT again yields the same state.
# If the resource doesn't exist, PUT may create it (server-dependent).
# Response
HTTP/1.1 200 OK
# or 204 No Content if no body returnedPATCH — 部分更新
PATCH 对资源应用部分更新,与 PUT 完全替换不同。最简单的形式是 JSON Merge Patch(RFC 7396):仅发送要更改的字段。JSON Patch(RFC 6902)使用操作数组(add、remove、replace、move、copy、test)。PATCH 默认不幂等,但 merge-patch 是幂等的。
# PATCH applies partial modifications
PATCH /api/users/43 HTTP/1.1
Host: api.example.com
Content-Type: application/merge-patch+json
Content-Length: 22
{"email": "[email protected]"}
# Only the email field is updated; other fields remain.
# JSON Patch (RFC 6902) - structured operations
PATCH /api/users/43 HTTP/1.1
Content-Type: application/json-patch+json
[
{"op": "replace", "path": "/email", "value": "[email protected]"},
{"op": "remove", "path": "/age"}
]DELETE — 删除资源
DELETE 删除目标资源。它是幂等的——两次删除同一资源应使服务器保持相同状态。常见的成功代码是 204(无请求体)、200(带有确认的请求体)或 202(异步删除)。大多数 API 需要认证。对于缺失的资源是否返回 404 存在争议——204 对幂等更安全。
# Delete a resource
DELETE /api/users/43 HTTP/1.1
Host: api.example.com
Authorization: Bearer <token>
# Successful responses:
HTTP/1.1 204 No Content # most common
HTTP/1.1 200 OK # if body returned
HTTP/1.1 202 Accepted # async deletion queued
# Idempotent: deleting an already-deleted resource
# should still return 204 (or 404 — debatable).HEAD 与 OPTIONS
HEAD 与 GET 相同,但服务器只返回头(无请求体)——适合在不下载请求体的情况下检查 Content-Length、Content-Type 或 Last-Modified。OPTIONS 询问允许哪些方法(Allow 头),并被浏览器用于 CORS 预检。两者都是安全 且幂等的。
# HEAD: like GET but returns headers only (no body)
HEAD /api/users/42 HTTP/1.1
Host: api.example.com
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 128
# (no body)
# OPTIONS: describe communication options
OPTIONS /api/users HTTP/1.1
Host: api.example.com
HTTP/1.1 200 OK
Allow: GET, POST, HEAD, OPTIONS
Access-Control-Allow-Methods: GET, POST状态码
1xx 信息性
1xx 代码是信息性的,表示请求已收到,处理继续进行。100 Continue 让客户端检查是否发送大型请求体(与 Expect: 100-continue 头配合使用)。101 Switching Protocols 启用 WebSocket。103 Early Hints 让服务器在最终响应准备好之前提示浏览器预加载资源。
# 100 Continue - server received headers, client may send body
POST /upload HTTP/1.1
Host: example.com
Expect: 100-continue
Content-Length: 1048576
# Server replies:
HTTP/1.1 100 Continue
# (client now sends the request body)
# 101 Switching Protocols - used for WebSocket upgrade
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
# 103 Early Hints - preload resources before final response
HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style2xx 成功
2xx 代码表示成功。200 OK 是通用成功。201 Created 表示创建了新资源(带有 Location 头)。202 Accepted 表示请求已排队进行异步处理(例如长任务)。204 No Content 表示成功但无请求体——常用于 DELETE/PUT。206 Partial Content 返回资源的一部分(Range 请求)。
# 200 OK - standard success
HTTP/1.1 200 OK
# 201 Created - new resource created (POST/PUT)
HTTP/1.1 201 Created
Location: /api/users/43
# 202 Accepted - request queued for async processing
HTTP/1.1 202 Accepted
# 204 No Content - success, no body (DELETE, PUT)
HTTP/1.1 204 No Content
# 206 Partial Content - Range request result
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/20483xx 重定向
3xx 代码表示重定向。301/308 是永久的(可缓存,SEO 链接权重传递);302/307 是临时的。关键区别:301 和 302 历史上允许 POST→GET 转换(导致数据丢失),而 307 和 308 严格保留方法。304 Not Modified 在缓存有效时的条件请求中返回——不发送请求体。
# 301 Moved Permanently - cacheable, change bookmarks
HTTP/1.1 301 Moved Permanently
Location: https://newsite.com/page
# 302 Found - temporary redirect (don't update bookmarks)
HTTP/1.1 302 Found
Location: /login
# 304 Not Modified - cache is still valid (conditional GET)
HTTP/1.1 304 Not Modified
ETag: "abc123"
# 307 Temporary Redirect - preserves method (POST stays POST)
HTTP/1.1 307 Temporary Redirect
Location: /new-endpoint
# 308 Permanent Redirect - permanent, preserves method
HTTP/1.1 308 Permanent Redirect
Location: https://newsite.com/api4xx 客户端错误
4xx 代码表示客户端出错。400 Bad Request 是格式错误的输入。401 表示无/无效认证(必须包含 WWW-Authenticate)。403 表示已认证但缺少权限。404 是缺失资源。429 表示速率限制(包含 Retry-After)。其他常见代码:405 Method Not Allowed、409 Conflict、422 Unprocessable Entity。
# 400 Bad Request - malformed syntax
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"error": "Invalid JSON in request body"}
# 401 Unauthorized - authentication required
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="api"
# 403 Forbidden - authenticated but not allowed
HTTP/1.1 403 Forbidden
# 404 Not Found - resource doesn't exist
HTTP/1.1 404 Not Found
# 429 Too Many Requests - rate limit hit
HTTP/1.1 429 Too Many Requests
Retry-After: 605xx 服务器错误
5xx 代码表示服务器失败。500 是未处理错误的统称(检查服务器日志)。501 表示服务器未实现该方法。502 表示代理/网关从上游收到错误响应。503 表示临时过载(使用 Retry-After)。504 表示上游超时。客户端可以用退避策略重试 502/503/504。
# 500 Internal Server Error - generic server failure
HTTP/1.1 500 Internal Server Error
# 501 Not Implemented - server doesn't support the method
HTTP/1.1 501 Not Implemented
# 502 Bad Gateway - upstream server returned invalid response
HTTP/1.1 502 Bad Gateway
# 503 Service Unavailable - temporary overload or maintenance
HTTP/1.1 503 Service Unavailable
Retry-After: 300
# 504 Gateway Timeout - upstream didn't respond in time
HTTP/1.1 504 Gateway Timeout状态码模式
一致的状态码映射使 API 可预测。使用 2xx 表示成功,并注意语义精确性(201 表示创建,204 表示无请求体)。使用 4xx 表示客户端错误,并选择最具体的代码(409 表示冲突,422 表示验证失败)。401 与 403 是最常见的混淆:401 = '你是谁?',403 = '我知道你是谁,但你不能做这件事'。
# REST API common mapping
GET /users -> 200
POST /users -> 201 (Created)
GET /users/42 -> 200 | 404
PUT /users/42 -> 200 | 201 (if created) | 404
PATCH /users/42 -> 200 | 404
DELETE /users/42 -> 204 | 404
# Validation errors
400 -> malformed JSON, missing required field
409 -> duplicate resource / version conflict
422 -> semantic validation failure (some APIs)
# Auth flow
401 -> no/invalid token (login required)
403 -> logged in but lacking permission
404 -> hide existence from unauthorized users
# Use 418 I'm a teapot for fun (RFC 2324 joke).请求头
常见请求头
常见请求头传达客户端身份(User-Agent)、所需响应格式(Accept 系列)、连接控制(Connection)、认证(Authorization)、状态(Cookie)和来源(Referer/Origin)。Host 在 HTTP/1.1+ 中是必需的,用于虚拟主机。Content-Type 和 Content-Length 描述任何请求体。
GET /api/data HTTP/1.1
Host: api.example.com # target host (mandatory in HTTP/1.1)
User-Agent: Mozilla/5.0 # client identity
Accept: application/json # desired response format
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Authorization: Bearer <token>
Cookie: session=abc123; theme=dark
Referer: https://example.com/
Origin: https://example.com
Content-Type: application/json
Content-Length: 42Accept 头(内容协商)
Accept 系列驱动内容协商。质量值(q,0-1)表示偏好——值越高越偏好。*/* 是通配符,匹配所有类型。服务器从其支持的类型中选择最佳匹配;如果都不匹配,则返回 406 Not Acceptable(尽管大多数服务器会回退到默认值而不是 406)。Brotli(br)和 zstd 提供比 gzip 更好的压缩。
# Accept: desired media types with quality (q) values
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
# Accept-Language: preferred languages
Accept-Language: en-US,en;q=0.9, zh-CN;q=0.8, fr;q=0.7
# Accept-Encoding: accepted compression algorithms
Accept-Encoding: gzip, deflate, br, zstd;q=0.9
# q=1.0 is default. Higher q = more preferred.
# */* matches any type.
# Server responds with 406 Not Acceptable if it can't satisfy.Authorization 与认证头
Authorization 携带凭据。Basic auth 在每个请求上发送 base64 编码的用户名:密码(始终使用 HTTPS)。Bearer 令牌是不透明字符串(通常是 JWT),用于 OAuth 2.0。自定义认证方案(X-API-Key)在 API 网关中很常见。Basic auth 的 base64 是编码,不是加密——没有 TLS 它不提供任何安全性。
# Basic Authentication (base64 of user:pass)
Authorization: Basic dXNlcjpwYXNz
# dXNlcjpwYXNz = base64("user:pass")
# Bearer Token (OAuth 2.0 / JWT)
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
# API Key in header (custom)
X-API-Key: abc123def456
# Proxy authentication
Proxy-Authorization: Basic dXNlcjpwYXNz
# WARNING: Basic auth sends credentials on every request.
# Always use HTTPS to prevent eavesdropping.User-Agent 与 Referer
User-Agent 标识客户端,但大多数浏览器为了向后兼容(历史嗅探)伪造 Mozilla/5.0。Referer(规范中拼写错误,从未更正)告诉服务器哪个页面链接到此请求——用于分析和防盗链保护。Origin 是 scheme+host+port,由浏览器在 CORS 请求和 POST 请求中发送。
# User-Agent identifies the client software
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
# Common browsers prepend "Mozilla/5.0" for historical compatibility
# (servers used to serve different content to different browsers)
# Referer: page that linked to this request
Referer: https://example.com/search?q=hello
# Origin: scheme + host + port (used by CORS)
Origin: https://example.com
# Note: "Referer" is the official spelling (misspelled in the original spec)条件请求头
条件头启用高效缓存和并发控制。If-None-Match(与 ETag 一起)和 If-Modified-Since(与 Last-Modified 一起)使 GET 成为条件请求——如果缓存有效,服务器返回 304。If-Match 和 If-Unmodified-Since 保护 PUT/PATCH 免受丢失更新(乐观并发)。If-Range 启用原子范围请求。
# Conditional GET - only fetch if changed
If-Modified-Since: Wed, 21 Oct 2023 07:28:00 GMT
If-None-Match: "abc123"
# If-Match / If-Unmodified-Since - for safe PUT/PATCH
If-Match: "abc123"
If-Unmodified-Since: Wed, 21 Oct 2023 07:28:00 GMT
# If-Range - for range requests (all-or-nothing)
If-Range: "abc123"
Range: bytes=0-1023
# Typical flow:
# 1. Server sends ETag/Last-Modified with response
# 2. Client sends If-None-Match/If-Modified-Since on next request
# 3. Server replies 304 if unchanged, 200 if changed自定义头与 X- 头
自定义头历史上使用 X- 前缀,但 RFC 6648 在 2012 年废弃了此约定。新头应去掉 X-(例如 Request-ID 而不是 X-Request-ID)。X-Forwarded-For 和 X-Forwarded-Proto 通过代理携带客户端信息(注意:可被伪造)。标准化替代方案是 Forwarded(RFC 7239)。Traceparent/Tracestate 启用分布式追踪(W3C)。
# X- prefixed headers (deprecated convention, but widely used)
X-Request-ID: 550e8400-e29b-41d4-a716-446655440000
X-Correlation-ID: abc-123
X-Forwarded-For: 203.0.113.1, 70.41.3.18
X-Forwarded-Proto: https
X-Real-IP: 203.0.113.1
X-RateLimit-Remaining: 42
# Modern recommendation: avoid X- prefix (RFC 6648)
# Use a namespace instead:
Request-ID: 550e8400-e29b-41d4-a716-446655440000
Traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
# Forwarded (RFC 7239) - standardized proxy info
Forwarded: for=203.0.113.1; proto=https; host=example.com响应头
常见响应头
常见响应头描述服务器(Server)、响应体(Content-Type、Content-Length)、连接控制(Connection)、缓存(Cache-Control、ETag、Last-Modified)、内容协商(Vary)、状态(Set-Cookie)和 CORS(Access-Control-*)。Date 是必需的,使用 GMT 的 RFC 1123 格式。
HTTP/1.1 200 OK
Server: nginx/1.25.0
Date: Wed, 21 Oct 2023 07:28:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 128
Connection: keep-alive
Cache-Control: public, max-age=3600
ETag: "abc123"
Last-Modified: Mon, 20 Oct 2023 10:00:00 GMT
Vary: Accept-Encoding
Set-Cookie: session=xyz; HttpOnly; Secure
Access-Control-Allow-Origin: https://example.com内容头
Content-* 头描述请求体。Content-Type 是 MIME 类型,至关重要。Content-Length 给出准确的字节计数(除非使用分块编码,否则必需)。Content-Encoding 指示压缩(gzip、br)。Content-Disposition 强制下载并指定文件名。Content-Range 伴随 206 Partial Content 响应。
# Body metadata
Content-Type: application/json; charset=utf-8
Content-Length: 128 # exact byte count
Content-Encoding: gzip # applied encoding
Content-Language: en-US
Content-Location: /api/users/42.json
Content-Disposition: attachment; filename="report.pdf"
Content-Range: bytes 0-1023/2048 # for partial content
# For chunked transfer (no Content-Length known upfront)
Transfer-Encoding: chunked
# MIME type format: type/subtype; parameter=value
# e.g., text/html; charset=UTF-8安全头
安全头加固浏览器以抵御常见攻击。HSTS 强制使用 HTTPS 并防止 SSL 剥离。X-Content-Type-Options: nosniff 防止 MIME 嗅探。X-Frame-Options(或 CSP frame-ancestors)阻止点击劫持。CSP 是最强大的——它控制哪些资源可以加载和执行。Referrer-Policy 限制泄露的 referer 信息。在每个响应上设置这些头。
# Force HTTPS for 6 months (incl. subdomains)
Strict-Transport-Security: max-age=15768000; includeSubDomains; preload
# Prevent MIME-type sniffing
X-Content-Type-Options: nosniff
# Prevent clickjacking
X-Frame-Options: DENY
# Or modern CSP frame-ancestors:
Content-Security-Policy: frame-ancestors 'none'
# Referrer policy
Referrer-Policy: strict-origin-when-cross-origin
# Permissions policy (feature lockdown)
Permissions-Policy: geolocation=(), camera=()
# Full Content-Security-Policy
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.comCORS 响应头
CORS(跨源资源共享)头让服务器选择性地接受跨源请求。任何跨源读取都需要 Access-Control-Allow-Origin;可以是特定源或 *。如果涉及凭据,需要 Allow-Credentials: true 且特定源(不能用通配符)。预检(OPTIONS)响应包括 Allow-Methods/Headers。Expose-Headers 让 JS 读取非默认响应头。
# Allow a specific origin
Access-Control-Allow-Origin: https://example.com
# Or wildcard (but credentials won't work with *)
Access-Control-Allow-Origin: *
# Allow credentials (cookies, Authorization)
Access-Control-Allow-Credentials: true
# Allowed methods (preflight response)
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
# Allowed headers (preflight response)
Access-Control-Allow-Headers: Content-Type, Authorization
# How long browser can cache preflight (seconds)
Access-Control-Max-Age: 86400
# Headers the JS can read from the response
Access-Control-Expose-Headers: X-Request-ID, X-Total-Count缓存头
Cache-Control 是现代(HTTP/1.1)缓存头,带有指令:public/private(谁可以缓存)、max-age(以秒为单位的鲜度)、no-cache(必须重新验证)、no-store(从不缓存)、immutable(URL 从不更改)。ETag 和 Last-Modified 是与条件请求一起使用的验证器。Expires(HTTP/1.0)是后备方案。Age 告诉响应已缓存多长时间。
# Cache-Control directives (HTTP/1.1 - preferred)
Cache-Control: public, max-age=3600
Cache-Control: private, max-age=600
Cache-Control: no-cache # revalidate before use
Cache-Control: no-store # never cache
Cache-Control: must-revalidate
Cache-Control: immutable
# Validators
ETag: "abc123" # opaque version tag
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
# HTTP/1.0 fallbacks (deprecated)
Expires: Wed, 21 Oct 2023 08:28:00 GMT
Pragma: no-cache
# Age: time a response has been in a cache (seconds)
Age: 120认证
基本认证
Basic auth 在每个请求上发送 base64 编码的凭据。它简单且被普遍支持,但如果没有 HTTPS 则不提供任何安全性——base64 很容易被解码。WWW-Authenticate 头挑战客户端(浏览器显示原生登录对话框)。Basic auth 在现代 Web 应用中很少见,但在 API 令牌和内部工具中很常见。
# Basic auth: base64(user:pass) sent on every request
GET /api/data HTTP/1.1
Host: example.com
Authorization: Basic dXNlcjpwYXNz
# dXNlcjpwYXNz = base64("user:pass")
# In curl: curl -u user:pass https://example.com/api/data
# Server challenge (401 with WWW-Authenticate)
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="Restricted Area", charset="UTF-8"
# WARNING: base64 is encoding, NOT encryption.
# Always pair Basic auth with HTTPS.Bearer 令牌(OAuth 2.0)
Bearer 令牌是作为授权证明呈现的不透明字符串。服务器只需验证它们——如果是自包含的(JWT)则是无状态的,或在存储中查找。OAuth 2.0 定义了获取令牌的流程(授权码、客户端凭据、刷新令牌)。始终通过 HTTPS 发送 Bearer 令牌。令牌具有有限的生命周期;使用刷新令牌获取新令牌。
# Bearer token: opaque string proving authorization
GET /api/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer SlAV32hkKG...
# Token obtained from OAuth 2.0 flow:
POST /oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=alice&password=secret&client_id=...
# Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "SlAV32hkKG...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "GmR..."
}JWT 结构
JWT 由点分隔的三部分 base64url 编码组成:头部(算法)、载 荷(声明)和签名。签名证明令牌未被篡改。常见声明:sub(主题)、iat(签发时间)、exp(过期时间)、iss(签发者)、aud(受众)。载荷未加密——切勿在其中放置机密。签名算法:HS256(HMAC)、RS256(RSA)、ES256(ECDSA)。
# JWT = header.payload.signature (base64url-encoded)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
# Decoded:
# Header
{"alg": "HS256", "typ": "JWT"}
# Payload (claims)
{
"sub": "1234567890", # subject (user id)
"name": "Alice",
"iat": 1516239022, # issued at
"exp": 1516242622, # expiration
"iss": "example.com", # issuer
"aud": "api.example.com" # audience
}
# Signature (HMAC-SHA256 of header.payload with secret)
HMAC-SHA256(secret, "eyJhbGc...")JWT 验证
JWT 验证:验证签名(捕获篡改),然后检查声明(exp、iss、aud)。exp 最重要——用 401 拒绝过期令牌。JWT 是无状态的:一旦签发,在过期之前无法撤销。对于撤销,使用短生命周期访问令牌(5-15 分钟)+ 服务器端存储的刷新令牌,或维护黑名单(破坏无状态性)。
# Server-side JWT validation steps:
# 1. Parse three parts (header.payload.signature)
# 2. Verify signature using secret (HS256) or public key (RS256)
# 3. Check claims:
# - exp (must be in future)
# - iat (must be in past, optional)
# - iss (must match expected issuer)
# - aud (must match expected audience)
# 4. Check revocation list (if using short-lived + refresh)
# Common errors:
# 401 - invalid signature, expired, wrong issuer
# 401 - "Bearer" prefix missing
# 403 - valid token but lacking permission (scope)
# Token expired response
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token", error_description="The token has expired"OAuth 2.0 授权码流程
授权码流程是服务器端 Web 应用的标准。第 1 步将用户重定向到认证服务器。第 2 步将短期代码返回到重定向 URI。第 3 步服务器对服务器交换代码(加上 client_secret)获取令牌。PKCE(代码交换证明密钥)用代码挑战/验证器对替换密钥——对于无法安全存储密钥的 SPA 和移动应用至关重要。
# Step 1: Redirect user to authorization endpoint
GET /authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=https://app.com/callback&scope=read&state=random123 HTTP/1.1
Host: auth.example.com
# Step 2: User logs in & grants consent, redirected back with code
HTTP/1.1 302 Found
Location: https://app.com/callback?code=AUTH_CODE&state=random123
# Step 3: Server exchanges code for token (server-to-server)
POST /oauth/token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)
grant_type=authorization_code&code=AUTH_CODE&redirect_uri=https://app.com/callback
# Step 4: Token response
{"access_token": "...", "refresh_token": "...", "expires_in": 3600}
# Use PKCE for SPAs/mobile (no client_secret)
# code_challenge = base64url(SHA256(code_verifier))API 密钥
API 密钥是标识客户端的简单不透明机密。它们在头中发送(首选)或在查询字符串中发送(避免——记录在 URL 和代理日志中)。API 密钥是有状态的(服务器查找它们)且可撤销。与 JWT 不同,它们不携带声明——服务器存储权限。最佳实践:静态哈希密钥、严格限定范围、定期轮换,并提供自助轮换 UI。
# API key in header (most common)
GET /api/data HTTP/1.1
X-API-Key: abc123def456
# API key as Bearer token
Authorization: Bearer abc123def456
# API key in query string (less secure - logged in URLs)
GET /api/data?api_key=abc123def456 HTTP/1.1
# API key in custom header with prefix
Authorization: ApiKey abc123def456
# Best practices:
# - Rotate keys periodically
# - Scope keys to specific permissions
# - Store keys hashed at rest (like passwords)
# - Never commit keys to source control内容协商
Accept 头
Accept 头列出客户端可以处理的媒体类型,质量值(q)表示偏好。通配符(text/*、*/*)扩大匹配范围。服务器选择最佳匹配并以所选 Content-Type 响应。如果没有媒体类型匹配,服务器理想情况下返回 406,但实际上许多 API 会回退到默认格式。
# Single type
Accept: application/json
# Multiple types with preferences (q values, 0-1)
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8
# Wildcard media types
Accept: text/* # any text/* subtype
Accept: */* # anything
Accept: image/png, image/*;q=0.8
# Parameters
Accept: text/html;charset=utf-8
# Server picks best match and returns it in Content-Type.
# If no match, server returns 406 Not Acceptable (or a default).Accept-Language
Accept-Language 告诉服务器用户偏好的语言,质量值用于排名。RFC 5646 定义语言标签(language[-script][-region])。服务器以 Content-Language 响应 。浏览器根据用户的 OS/区域设置进行设置。对于 i18n,优雅回退:如果请求的语言不可用,返回默认值。CDN 有时按 Accept-Language 变化缓存——使用 Vary 头。
# Single language
Accept-Language: en-US
# Multiple languages with preferences
Accept-Language: en-US,en;q=0.9, zh-CN;q=0.8, fr;q=0.7, de;q=0.5
# Wildcard
Accept-Language: *
# Server responds with the selected language:
Content-Language: en-US
# RFC 5646 language tags:
# en - language
# en-US - language + region
# zh-Hans - language + script (Simplified Chinese)
# es-419 - language + region (Latin America)Accept-Encoding
Accept-Encoding 列出客户端可以解码的压缩算法。服务器选择一个并应用它,返回 Content-Encoding。Brotli(br)比 gzip 更好地压缩文本,并在所有现代浏览器中受支持。zstd 正在兴起,速度/比率更好。identity 表示无压缩。始终对文本响应(HTML、CSS、JS、JSON)启用压缩——大幅节省带宽,但跳过已压缩的格式(图像、视频)。
# Accepted compression algorithms
Accept-Encoding: gzip, deflate, br, zstd
# With preferences
Accept-Encoding: br;q=1.0, gzip;q=0.8, identity;q=0.5, *;q=0
# identity = no encoding (raw)
# * = any encoding not listed
# Server response
Content-Encoding: br
# Common algorithms:
# gzip - widespread, RFC 1952
# deflate - zlib, RFC 1950
# br - Brotli, better for text (modern browsers)
# zstd - Zstandard, fast + good ratio (newer)
# identity - no compressionVary 头
Vary 头告诉缓存(浏览器、CDN、代理)哪些请求头影响响应。如果服务器基于 Accept-Encoding 返回不同内容,必须 Vary: Accept-Encoding,以便缓存不会向只接受 br 的客户端提供 gzip 响应。过度 Vary 损害缓存命中率;Vary 不足导致缓存中毒(提供错误内容)。Vary: * 实际上禁用缓存。
# Server response telling caches to vary by Accept-Encoding
HTTP/1.1 200 OK
Content-Type: text/html
Content-Encoding: gzip
Vary: Accept-Encoding
# Vary on multiple headers
Vary: Accept-Encoding, Accept-Language
# Vary on Accept (full content negotiation)
Vary: Accept
# Vary: * means "varies on everything" (effectively no-cache)
Vary: *
# CDN/cache key includes the Vary header values.
# A gzip+en response is cached separately from a br+fr response.质量值(q)
质量值(q,0-1)在 Accept 系列头中表示偏好强度。q=1.0 是默认值(最偏好);q=0 表示'不可接受'。服务器列出它可以生成的内容,将每个与客户端列表匹配,选择 q 值最高的匹配。特异性打破平局:application/json 即使在相同 q 值下也胜过 */*。允许三位小数,但很少需要。
# q values appear in Accept, Accept-Language, Accept-Encoding, Accept-Charset
# Format: ;q=0.000 to ;q=1.000 (default is 1.0)
Accept: text/html;q=1.0, application/json;q=0.9, */*;q=0.1
# Meaning:
# q=1.0 - strongly preferred
# q=0.5 - acceptable
# q=0 - NOT acceptable (refuse)
# Server algorithm (RFC 7231):
# 1. List media types the server can produce
# 2. Match each against the client's Accept
# 3. Pick the match with the highest q value
# 4. Ties broken by specificity (concrete > wildcard)
# Example: client prefers JSON over XML
Accept: application/json, application/xml;q=0.5缓存
Cache-Control 指令
Cache-Control 是带有指令的现代缓存头。public/private 控制谁可以缓存;max-age 设置以秒为单位的鲜度;no-cache 强制重新验证;no-store 禁止缓存;must-revalidate 禁止提供过期内容;s-maxage 为共享缓存(CDN)设置单独的 TTL。immutable 告诉浏览器资源从不更改——重新加载时跳过重新验证。Stale-while-revalidate 和 stale-if-error 启用优雅降级。