HTTP Header 中的黑科技
source link: https://blog.dteam.top/posts/2021-02/http-header-technology.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
简单认识 HTTP 协议
HTTP 协议是一种基于 TCP 的纯文本协议,一个基本的 HTTP 协议交互过程如下:
使用 nc 命令演示以及 mock 最基本的 http server 交互响应过程:
nc -kl 8080
curl -v localhost:8080
终端 1 结果类似如下:
GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.75.0
Accept: */*
此时,我们在终端 1 手工输入一个模拟 http server 的响应:
HTTP/1.1 200 OK
Hello
此时,在终端 2 应该看到类似于下面的响应:
< HTTP/1.1 200 OK
<
Hello
HTTP 的协议的请求内容格式如下:
GET / HTTP/1.1 # <======== 第一行为 method location protocol
Host: localhost:8080 ######################
User-Agent: curl/7.75.0 ## 一些可选的 headers
Accept: */* ######################
# 一个空行
{body} # 可选的 request body
HTTP 协议的响应内容格式如下:
HTTP/1.0 200 OK # 第一行固定 protocol code description
Server: SimpleHTTP/0.6 Python/3.9.1 ################
Date: Sun, 14 Feb 2021 09:24:59 GMT ## 一些可选的 headers
Content-type: text/html; charset=utf-8 ##
Content-Length: 496 ################
# 一个空行
{body} # 响应正文内容
通常描述 REST 接口的文档中,直接省略请求过程以及非关键的 Header 部分,贴出关键部分的请求命令和 header,以及 body 部分,以一个登录 API 为例,可能看到的接口文档描述如下:
客户端请求:
POST /login
Content-Type: application/json
{"usernamae":"u1","password":"p1"}
服务端响应:
200 OK
Content-Type: application/json
{"token":"a1b2c3..."}
常见 HTTP Code 总结
1xx
: 信息响应100 Continue
,这个临时响应表明,迄今为止的所有内容都是可行的,客户端应该继续请求,如果已经完成,则忽略它。101 Switching Protocol
,该代码是响应客户端的 Upgrade 标头发送的,并且指示服务器也正在切换的协议。
2xx
:成功响应200 OK
201 Created
204 No Content
206 Partial Content
,部分成功,断点续传必备
3xx
:重定向301 Moved Permanently
,注意所有的请求都将被定向为 GET302 Found
,与 301 最直观的区别是 302 请求不会被搜索引擎收录304 Not Modified
,见下文缓存部分307 Temporary Redirect
,与 302 唯一的区别在于 method 保持原样308 Permanent Redirect
,与 301 唯一的区别在于 method 保持原样
4xx
: 客户端错误400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
405 Method Not Allowed
5xx
: 服务端失败500 Internal Server Error
501 Not Implemented
502 Bad Gateway
,常见于反向代理503 Service Unavailable
,常见于云加速服务504 Gateway Timeout
,常见于反向代理
参考资料: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status
HTTP 协议的版本的主要演进
HTTP/1.0 增加以下内容:
- 增加协议版本号,如 GET HTTP/1.0, POST HTTP/1.1
- 引入 header 内容,无论请求成功与否都可以添加 header,增加灵活性
- 增加 Content Type 支持,以便 http 协议传输更多更丰富的内容
HTTP/1.1 增加以下内容:
- 连接可以复用,增加
keepalive
支持 - 支持 pipeline,允许在第一个应答被完全发送之前就发送第二个请求,以降低通信延迟。
- 支持分块编码技术(chunked)
- 引入额外缓存机制(基于相对时间缓存的
cache-control
头) - 引入内容协商头(
Accept-*
头) - 引入
Host
头,以便支持虚拟主机
HTTP/2 主要改进:
- 不再基于纯文本,而是使用二进制(但第一次协商为了向下兼容 HTTP/1.1 依旧使用纯文本)
- 是一个复用协议。并行请求和响应在同一个链接完成
- 压缩 headers,节省传输成本
HTTP/3(QUIC)主要改进(目前仍是草案状态):
- 基于 UDP 协议,废弃 TCP 协议
参考资料:
HTTP header 中的黑科技实例
基于域名的虚拟主机(Host
头)
通过 client 添加 Host 头(通常不需要用户干预,命令部分和 Host 头部分通常客户端会自动处理)以及服务端响应 Host 头,可以实现同一个服务器上提供多个网站的的场景。例:
GET / HTTP/1.1
Host: localhost:8080
内容协商的方式为客户端请求带上 Accept 类的头(尽管 User-Agent
不是标准的内容协商内容,但是实际开发中很多人还是会使用 User-Agent
作为协商依据),服务端会根据这个头响应客户端期望的内容以及对应的 header(header 也可能没有,而是直接返回对应期望的内容或者重定向到目标页面)。
常见的 Accept 对与示例:
客户端 Request服务端 Response(可能没有)accept: application/json, text/htmlcontent-type: application/jsonaccept-language: zh-CN,zh;q=0.9,en;q=0.8,en-US;q=0.7Content-Language: zh-CNaccept-encoding: br, gzip, defletecontent-encoding: gzipVary: Accept-EncodingVary
会影响下级服务端或用户浏览器的缓存策略。vary
头表示服务端基于哪个请求头做了内容协商(可能没有),再简单一点就是因为什么头的内容不同而响应内容不同。Vary
头可以防止缓存错乱,但是滥用会导致缓存命中率下降,因此通常不推荐 Vary: *
。实际使用常见的 Vary: Accept-Encoding
,User-Agent
,Origin
跨源资源共享 CORS(仅浏览器)
原理和过程部分强烈建议阅读 MDN 文档: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
客户端请求头:
OPTIONS /resource/foo # fetch 请求通常使用 OPTIONS 命令
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org
服务端期望的响应头:
HTTP/1.1 200 OK
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400
下载文件(仅浏览器)
打开链接下载文件需要满足以下二选一:
content-type
非浏览器能直接支持的,或者为默认的application/octet-stream
- 响应头中包含
Content-Disposition: attachment
,无论content-type
是什么,都将变为文件下载,如果Content-Disposition
的值包含filename=for.bar
,则默认下载文件名为指定filename
的值。例:
Content-Disposition: attachment;filename=download.pdf
文件下载带进度提醒
响应头中包含 Content-Length
,则客户端可以根据这个文件长度提醒下载进度
多线程下载/断点续传
服务端必须支持 Accept-Ranges
响应,大部分情况下这个值是 bytes
:
Accept-Ranges: bytes
客户端请求带上 Range:(unit=first byte pos)-[last byte pos]
,如:
Range: bytes=0-499
服务端应该返回:
206 Partial Content
Content-Range: bytes 0-1023/146515
大数据量传输/流式传输(chunked)
对于大容量数据、动态数据、流式数据这种不可提前预知容量(content-length
)的内容,应当采用 chunked 方式。它可以方便处理动态内容,以及动态维持客户端链接。客户端通常会对 chunked 进行流式处理。一个典型的 chunked 响应如下:
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
7\r\n
Mozilla\r\n
9\r\n
Developer\r\n
7\r\n
Network\r\n
0\r\n
\r\n
具体的 chunked 部分格式如下:
[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]
缓存(通过 header 控制)
HTTP 的缓存可以针对浏览器,也可以针对中间的代理层(如 CDN 等)。Vary
头会影响缓存效果(下不赘述)。
HTTP 协议的缓存结构可以参考下图(来自: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ ):
更灵活的本地缓存策略可以考虑上 service worker: https://developers.google.cn/web/ilt/pwa/caching-files-with-service-worker
哪些响应可以被缓存:
- 通常只有
GET
可以被浏览器缓存,而OPTIONS
和HEAD
可以被 CDN 缓存(可能需要配置) - 响应码为 200, 206, 301, 404
常见缓存头:
Expires
(Since HTTP/1.0)
Expires: <http-date>
:Expires: Thu, 01 Dec 1994 16:00:00 GMT
- 本地时间到达指定时间后缓存会过期,会重新发起 HTTP 请求
- 本地时间如果不同步会影响缓存效果
Last-Modified
/ If-Modified-Since
(Since HTTP/1.0)
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
- 使用服务端时间,因此本地时间不同步时不影响缓存效果
- 资源未过期服务端应该返回
304 Not Modified
Etag
/ If-Non-Match
(Since HTTP/1.1)
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
If-Non-Match: "bfc13a64729c4290ef5b2c2730249c88ca92d82d"
- 和本地时间无关,本地时间不影响缓存效果
- 资源未过期服务端应该返回
304 Not Modified
- 使用 CDN 及分布式存储的场景通常不建议使用 Etag 头
Cache-Control
(Since HTTP/1.1)
cache-control: max-age=3600,public
max-age
字段表示自获取资源之后,在本地缓存多久(单位:秒)public
/private
代表这个是公共还是私有缓存,公共缓存是可以被所有中间层缓存的,私有缓存只能在本地浏览器中缓存。包含max-age
参数或包含 Expires 头的时候,默认为public
,否则默认private
cache-control
头可以同时包含在请求头与响应头中
如果以上头在响应中都包含,并且客户端均支持,那么优先级如下: Cache-Control
> Expires
> Etag
> Last-Modified
Cache-Control
(Since HTTP/1.1)
no-store
:不使用任何缓存,每次重新发起请求,下载最新资源no-cache
:每次重新验证,如果服务端未更新返回 304 Not Modified
Pragma: no-cache
(Since HTTP/1.0)
- 和
cache-control: no-cache
效果相同 - 不能代替
cache-control
,只是向后兼容 HTTP/1.0 的缓存层和客户端
缓存 FAQ
浏览器直接地址栏输入和 F5 以及 CTRL+F5 有什么区别?
- 地址栏直接输入相比普通请求无任何区别,如果缓存还未过期,不会发起请求
- 使用 F5/CTRL+R 刷新页面会在请求头加上 cache-control: max-age=0,强行发起缓存验证,会保留 If-Modified-Since/If-Non-Match 请求头,如果服务端的资源未更新,返回 304 Not Modified。而页面其他相关联的请求(如 js, css, 图片资源等)不做任何特殊处理。
- 使用 CTRL+F5/CTRL+SHIFT+R 刷新页面会在请求头带上 cache-control: no-cache 和 pragma: no-cache,同时会去掉 If-Modified-Since/If-Non-Match 请求头,完全不使用本地缓存,强行从服务端重新获取资源,成功应该返回 200 OK。页面其他相关请求做同样缓存过期处理。这个效果几乎就等同于开发者工具中 Network-Disable Cache 勾选上的效果
如何判断浏览器/CDN 的缓存资源是否过期?
- 如果资源由缓存服务器或 CDN 响应,通常还同时会返回一个
Age: <seconds>
头表示资源在服务器上缓存了多久(单位:秒),可以根据这个头判断最近资源是否有更新 - 通过
Last-Modified
头判断
- 如果资源由缓存服务器或 CDN 响应,通常还同时会返回一个
如何判断响应后端是真实的服务端还是 CDN?
- 通过
Server
头判断。绝大多数自建服务器的Server
可能为Apache
,Nginx
,Tomcat
之类的,而 CDN 通常不直接使用这些 Server - 绝大多数 CDN 通常会带上一个
Via
头,可以通过这个头的内容判断是否来自于 CDN,以及使用哪个 CDN
- 通过
如何判断是否命中 CDN 的缓存?
- CDN 厂商通常都会添加一些自定义的头:
x-****
,这些信息用来帮助用户调试 CDN。多数 CDN 厂商会把是否命中缓存放在x-cache
这个头。如阿里云 CDN:x-cache: HIT TCP_MEM_HIT dirn:11:115947616
,Cloudfront:x-cache: Hit from cloudfront
- CDN 厂商通常都会添加一些自定义的头:
参考资料: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching_FAQ
强制 HTTPS
强制 HTTPS 通常有两种方案:重定向和 HSTS
。
HSTS
(HTTP Strict Transport Security
)是一个特殊的 http header: Strict-Transport-Security: max-age=<expire-time>
,如:
Strict-Transport-Security: max-age=31536000;includeSubDomains
用于告诉浏览器必须使用 HTTPS 访问指定资源。二者比对如下:
重定向(浏览器通常有上限次数限定)HSTS适用性所有客户端(必须开启 follow redirect)仅现代化的浏览器强制性强制。不跳转到Location
无法获取资源非强制。旧的 URL 依旧可用额外请求多一次服务端请求内部重定向,无额外请求非标准端口支持不支持参考资料: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/HTTP_Strict_Transport_Security
基于 HTTP/1.1 之上的协议
HTTP/1.1 协议可以通过升级方式使用基于 HTTP/1.1 的高级协议,如 Websocket
和 WebDAV
等等。升级方式为添加以下两个头:
- Connection: Upgrade
- Upgrade: protocols
升级成功服务端应该返回 101 Switching Protocols
,并且之后的交互则使用高级协议规范进行交互。如果服务端不支持该升级协议,则应该返回 200 OK
,之后由客户端继续按照 HTTP 协议降级处理。
简易图片防盗链(仅浏览器)
图片防盗链一种比较简单的防护策略是通过 Referer
头进行防护。假想你的网页代码中引入图片的部分如下:
<img src="pic.jpg" />
那么浏览器请求这个图片资源的时候,通常还会附带上当前的地址栏到 Referer
头,请求示例如下:
GET /pic.jpg HTTP/1.1
Host: localhost:8080
referer: http://localhost:8080/
服务端可以通过设置 Referer
头白名单的方式一定程度上实现防盗链。
Recommend
-
8
作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架...
-
3
简单认识 HTTP 协议 HTTP 协议是一种基于 TCP 的纯文本协议,一个基本的 HTTP 协议交互过程如下: 使用 nc 命令演示以及 mock 最基本的 http server 交互响应过程: 终端 1: nc -kl 8080 终端 2:...
-
2
[Feature request] Set HTTP header to opt out of FLoC in GitHub PagesFor GitHub Pages, we ought to be able to opt out of computation in Google’s Federated Learning of Cohorts (FLoC) 1.8k. ...
-
5
如何更改url package访问HTTP时的user-agent header 有些网站会根据 http request 中的 user-agent header 的值返回不同的response,例如 http://wttr.in 会根据就会根据 user-agent 是否为 curl 来决定是返回带图片的...
-
4
這幾天在處理公司的第三方 API 介接,其中有一個部分是將 token 放在 HTTP header 裡面當作彼此驗證的方式,雖然這不是什麼特別的方式,但卻在我使用 Ruby 處理時掉進了時空裂縫。底線的魔法主要規格是在 HTTP header 裡面放置 api_key:...
-
5
Discover more content... Enter some keywords in the search box above, we will do our best to offer you relevant results.
-
2
Introduction Access HTTP Request Header in Golang code running on your browser. This can be done by
-
22
The Accept-Language field in HTTP Request header is important for
-
12
[Golang] Parse Accept-Language in HTTP Request Header February 22, 2015 This post i...
-
7
[JavaScript] Parse Accept-Language in HTTP Request Header January 22, 2016 ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK