HTTP

HTTP概述

HTTP 是一种用作获取诸如 HTML 文档这类资源的协议(TTP 是一种应用层的协议,通过 TCP 或 TLS(一种加密过的 TCP 连接)来发送)。它是 Web 上进行任何数据交换的基础,也是一种客户端—服务器(client-server)协议

HTTP请求包的结构

请求行

URL

(统一资源定位符)是因特网中的唯一资源的地址。它是浏览器用于检索已发布资源(例如 HTML 页面、CSS 文档、图像等)的关键机制之一

URL基本内容
协议(scheme)

HTTPS 、 HTTP、FTP

权威(authority)

权威包括域名(例如 www.example.com)和端口(80),由冒号分隔

注:邮件客户端(mailto:foobar)不使用权威(其包含方案)

路径(Path)

/path/to/myfile.html/index.html是 Web 服务器上资源的路径

参数(Parameters)

?key1=value1&key2=value2 是提供给 Web 服务器的额外参数(参数是用 & 符号分隔的键/值对列表)

锚点(Anchor)

#SomewhereInTheDocument 是资源本身的另一部分的锚点

锚点表示资源中的一种“书签”,给浏览器显示位于该“加书签”位置的内容的方向

# 后面的部分(也称为片段标识符)不会随请求被发送到服务器

一些URL

数据URL:以 data: 方案为前缀的 URL,允许内容创建者在文档中嵌入小文件

注:URL 编码使用 “%” 其后跟随两位的十六进制数来替换非 ASCII 字符。

URL 不能包含空格。URL 编码通常使用 + 来替换空格。

请求方式

请求方式:

GET和POST

get 请求

GET 请求主要用于从服务器获取数据,常用于获取静态资源(网页、图片、文件等)

get请求的参数传递方式

在 HTTP 请求中,GET 请求可以通过 URL 参数或者请求体的方式传递参数。

URL 参数:GET 请求可以通过 URL 中指定的 key-value 形式传递参数。

1
http://example.com/search?q=test&page=2

?后 key= value

post请求

POST 请求主要用于向服务器提交数据,通常用于创建、更新或删除资源、上传文件、发送 JSON 数据

post请求的参数传递方式:URL 参数和请求体。

1.URL 参数:POST 请求可以通过 URL 中指定的 key-value 形式传递参数。例如,在访问如下 URL 时:

1
http://example.com/search?q=test&page=2

2.请求体(仅适用于 POST、PUT、PATCH 请求):当 POST、PUT 或 PATCH 请求的 URL 中没有指定参数时,参数会被放置在请求体中。请求体中的数据可以是表单数据、JSON 数据等。例如,使用 POST 请求将 JSON 数据发送到如下 URL 时:

1
http://example.com/users

请求体中的 JSON 数据如下:

1
2
3
4
{
"name": "John Doe",
"email": "john.doe@example.com"
}
get和post区别

get 和 post 请求的使用场景
get
  • 获取数据:GET 请求适合用于从服务器获取数据,例如获取网页内容、查询数据库、获取资源文件等。由于 GET 请求的参数会显示在 URL 中,因此它适用于不需要保密的参数传递。
  • 缓存友好:GET 请求的响应可以被缓存,这对于一些静态资源的获取非常有益,可以提高性能和减少服务器负载。
  • 链接导航:GET 请求可以用于在浏览器中进行链接导航,通过在 URL 中传递参数来指定要导航到的页面或资源。
post
  • 提交数据:POST 请求适合用于向服务器提交数据,例如提交表单、上传文件等。由于 POST 请求的参数不会显示在 URL 中,因此它适用于传递敏感数据或大量参数

  • 创建资源:POST 请求可以用于在服务器上创建新的资源,例如创建新的用户、发布新的文章等。

  • 执行非幂等操作:POST 请求是非幂等的,意味着多次发送相同的 POST 请求可能会产生不同的结果。这适用于一些需要执行不可重复操作的场景,例如订单支付、密码修改等。

    加键值对时get在url中?a=1 post在请求体中a=11

请求头及其内容

常见的请求头及作用

Host,Referer,User-Agent,Cookie,Content-Type,X-Forwarded-For

注:POST 请求(包含 HTML 表单数据)需要主体

主体大致可分为两类:

*单一资源(Single-resource)主体,由一个单文件组成。该类型的主体由两个标头定义:Content-Type和Content-Length

多资源(Multiple-resource)主体,由多部分主体组成,每一部分包含不同的信息位。通常是和 HTML 表单连系在一起。

  1. Host:指定请求的服务器的域名和端口号。

  2. User-Agent:包含发出请求的用户代理(浏览器)的信息,可用来浏览器伪造。

    一个典型的 User-Agent 字符串可能看起来像这样:

    1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36

    这个字符串包含了以下信息:

    • 浏览器:Mozilla/5.0
    • 操作系统:Windows NT 10.0
    • 平台:Win64; x64
    • 浏览器核心:AppleWebKit/537.36
    • 浏览器名称:Chrome
    • 浏览器版本:58.0.3029.110
    • 渲染引擎:Safari/537.36
  3. Accept:指定客户端能够处理的媒体类型。

  4. Accept-Language:指定客户端偏好的语言。

  5. Accept-Encoding:指定客户端能够处理的压缩算法。

  6. Authorization:用于身份验证的凭证。

    [!IMPORTANT]

    HTTP 基本认证

    通过在请求头 (Authorization 头) 中发送用户名和密码来进行身份验证。用户名和密码(用户名:密码)会经过 Base64 编码,但 不会加密

    客户端提供用户名和密码,并在 Authorization 头中发送:

    1
    Authorization: Basic <Base64编码的用户名:密码>
  7. Cookie:Cookie 是一种服务器发送到用户浏览器并保存在本地的数据。它用于跟踪和识别用户会话,存储用户的偏好设置。

    基本概念

    会话Cookie:只在浏览器会话期间有效,当用户关闭浏览器时,这些Cookie就会被删除。浏览器定义了“当前会话”结束的时间,一些浏览器重启时会使用会话恢复。这可能导致会话 cookie 无限延长。

    持久Cookie:在过期时间(Expires)指定的日期或有效期(Max-Age)指定的一段时间后被删除。

    示例

    假设服务器想要设置一个Cookie,其内容如下:

    1
    Set-Cookie: sessionId=abc123; Expires=Wed, 09 Jun 2024 10:18:14 GMT; Domain=example.com; Path=/; Secure; HttpOnly; SameSite=Lax

    在这个例子中:

    sessionId=abc123 是Cookie的名称-值对(存储在Cookie中的数据)

    Expires=Wed, 09 Jun 2024 10:18:14 GMT 定义了Cookie的过期时间。ax-Age属性指定一个相对于创建时的时间长度(以秒为单位)

    Domain=example.com 指定了Cookie的有效域名(哪些域名下的页面请求可以包含这个Cookie)如果未指定,默认为创建Cookie的服务器的域名

    Path=/ 指定了Cookie的有效路径。

    Secure 属性表示Cookie仅通过HTTPS发送。

    HttpOnly 属性表示Cookie不能被客户端脚本(如JavaScript)访问。

    [!CAUTION]

    XSS 攻击是一种常见的攻击方式,攻击者通过向 Web 页面注入恶意的 JavaScript 代码来窃取用户数据。使用 HttpOnly 标记的 Cookie 可以防止恶意 JavaScript 代码通过 document.cookie 或其他方式获取敏感数据(如会话 Cookie)

    但它并不能防止 Cookie 被服务器端访问,攻击者如果能够获得服务器端的访问权限(如 SQL 注入、服务器配置错误等)或进行CSRF(跨站请求伪造)攻击,仍然有可能窃取 Cookie

    因此,它需要与其他安全措施(如 SecureSameSite)结合使用,以提供全面的安全防护

    SameSite=Lax 属性表示在跨站请求时,Cookie不会随请求发送,除非请求是同站的。

  8. Content-Length:请求体的长度。

  9. Content-Type:指定资源的媒体类型( MIME 类型)(媒体类型(也通常称为多用途互联网邮件扩展MIME 类型)是一种标准,用来表示文档、文件或一组数据的性质和格式),告诉接收方如何处理传输的数据

    [!WARNING]

    浏览器通常使用 MIME 类型而不是文件扩展名来决定如何处理 URL,因此 Web 服务器在 Content-Type 响应标头中添加正确的 MIME 类型非常重要。如果配置不正确,浏览器可能会曲解文件内容,网站将无法正常工作,并且下载的文件也可能被错误处理

    以下是一些常见的 Content-Type 值及其对应的数据格式:

    1. 文本类型

      • text/html:HTML 文档。

        [!CAUTION]

        如果需要 XML 的严格解析规则、<![CDATA[...]]>(在 XML 中,所有的字符数据默认都会被解析,并且需要处理特殊字符(比如 & 会变成 &< 会变成 <)。但是,如果你使用 CDATA,则该部分中的所有内容都会被当作普通文本处理,直到遇到 ]]>,这时 CDATA 部分结束)或 HTML/SVG/MathML 命名空间以外的元素,请使用 application/xml 或 application/xhtml+xml

      • text/plain:纯文本,文本文件的默认值。即使它其实意味着未知的文本文件,但浏览器认为是可以直接展示的。

        [!CAUTION]

        text/plain 并不意味“任何种类的文本数据”。如果浏览器期待的是某种特定的文本数据,很可能不会将其视为匹配。具体来说,如果从声明 CSS 文件的 <link> 元素中下载了一个 text/plain 文件,那么如果提供的是 text/plain类型,就不会将其识别为有效的 CSS 文件。CSS MIME 类型需要使用 text/css

      • text/css:层叠样式表(CSS),在网页中要被解析为 CSS 的任何 CSS 文件必须指定 MIME 为 text/css

      • text/javascript:JavaScript 代码(已废弃,应使用 application/javascript)。

    2. 图像类型

      • image/gif:图形交换格式(GIF)
      • image/jpeg:联合图像专家小组图片(JPEG)
      • image/png:便携式网络图形(PNG)
      • image/svg+xml:可缩放矢量图形(SVG)
      • image/webp:Web 图像格式(WEBP)
      • image/apng:动画便携式网络图形(APNG)
      • image/avif:AV1 图像文件格式(AVIF)
    3. 音频和视频类型

      • audio/mpeg:MPEG 音频。
      • video/mp4:MP4 视频。
      • audio/ogg:Ogg 音频。
      • video/ogg:Ogg 视频。
      • video/webm: WebM格式的视频。
    4. 应用程序类型

      • application/json:JSON 数据。
      • application/xml:XML 数据。
      • application/pdf:PDF 文档。
      • application/zip:ZIP 压缩文件。
      • application/javascript:JavaScript 代码(现代浏览器推荐使用)。
    5. 多部分类型

      • multipart/form-data:用于表单数据的传输,允许在单个请求中发送多个文件和字段。这通常用于文件上传和多值数据传输,可用于HTML表单从浏览器发送信息给服务器
      • multipart/byteranges:用于定义多个字节范围,通常用于视频流或大文件下载
    6. 其他类型

      • application/octet-stream:这是二进制文件的默认值。由于这意味着未知的二进制文件,浏览器一般不会自动执行或询问执行。浏览器将这些文件视为 Content-Disposition 标头被设置为 attachment 一样,并弹出“另存为”对话框
      • application/x-www-form-urlencoded:用于表单数据的传输,通常用于 GET 和 POST 请求。
    7. 字符编码

      • 许多 Content-Type 值可以包含字符编码信息,如 text/html; charset=utf-8,这指定了使用 UTF-8 编码的 HTML 文档。

    [!IMPORTANT]

    在 HTML 表单中,enctype 是一个属性,用于指定在提交表单时数据的编码类型。它决定了表单数据在发送到服务器时的格式在 HTML 表单中,enctype 是一个属性,用于指定在提交表单时数据的编码类型。它决定了表单数据在发送到服务器时的格式

    application/x-www-form-urlencoded(默认):适用于提交常规文本数据,简单的表单提交。

    multipart/form-data:适用于上传文件时的表单提交,当表单包含文件上传时,一定要使用 multipart/form-data,否则文件将无法正确上传

    text/plain:以纯文本形式提交数据,每对 name=value 由换行符分隔。

  10. Referer:指示请求来源的页面URL(用户是从哪个页面点击链接到达当前请求的页面的),可用来服务器伪造 https://www.xxxx.com,这个头部字段通常用于分析流量来源、防止CSRF(跨站请求伪造)攻击以及跟踪用户行为

    示例

    假设你访问了一个网站 http://example.com/page1,然后从该页面点击链接跳转到 http://example.com/page2,浏览器在请求 page2 时,通常会将以下头部添加到请求中:

    1
    Referer: http://example.com/page1

    假设用户从页面 https://www.example.com/page1.html 点击一个链接,跳转到 https://www.example.com/page2.html,那么在请求 page2.html 时,HTTP请求的 Referer 头部可能如下:

    1
    2
    3
    GET /page2.html HTTP/1.1
    Host: www.example.com
    Referer: https://www.example.com/page1.html

11.X-Forwarded-For:用于在HTTP请求经过代理服务器或负载均衡器时保留原始客户端的IP地址信息,可用于修改ip 如127.0.0.1

X-Forwarded-For头部字段的格式如下:

1
X-Forwarded-For: <client-IP>, <proxy1-IP>, <proxy2-IP>, ...

其中,列表中的第一个IP地址(<client-IP>)是客户端的真实IP地址,随后的IP地址表示请求依次经过的代理服务器的IP地址。

HTTP响应包的结构

响应行

HTTP/1.1 404 Not Found

  1. 协议版本,通常为 HTTP/1.1
  2. 状态码(status code),表明请求是成功或失败。常见的状态码是 200(OK。请求成功)、404 (Not Found。服务器无法找到请求的资源)或301(Moved Permanently。请求资源的 URI 已被改变)
  3. 状态文本(status text)。一个简短的,纯粹的信息,通过状态码的文本描述,帮助人们理解该 HTTP 消息

响应头

通用标头(General header),例如 Via,适用于整个消息。

响应标头(Response header),例如 VaryAccept-Ranges,提供有关服务器的其他信息,这些信息不适合状态行。

表示标头(Representation header),例如 Content-Type 描述了消息数据的原始格式和应用的任意编码(仅在消息有主体时才存在)

  1. Content-Type:响应体的媒体类型。
  2. Content-Length:响应体的长度。
  3. Cache-Control:控制响应的缓存行为。
  4. Expires:指定资源到期的时间。
  5. Set-Cookie:设置客户端的Cookie。
  6. Location:用于重定向的URL。
  7. Server:包含服务器软件的信息。
  8. WWW-Authenticate:用于HTTP认证的挑战。
  9. Last-Modified:资源最后修改的时间。
  10. ETag:资源的特定版本的标识符。

HTTP缓存

HTTP 缓存会存储与请求关联的响应,并将存储的响应复用于后续请求

私有缓存

绑定到特定客户端的缓存——通常是浏览器缓存

如果响应包含个性化内容并且你只想将响应存储在私有缓存中,则必须指定 private 指令

1
Cache-Control: private

[!CAUTION]

如果响应具有 Authorization 标头,则不能将其存储在私有缓存(或共享缓存,除非 Cache-Control 指定的是 public)中

[!NOTE]

public 值具有使响应可存储的效果,只有在设置了 Authorization 标头时需要存储响应时才应使用 public 指令。否则不需要,因为只要给出了 max-age,响应就会存储在共享缓存中。

共享缓存

位于客户端和服务器之间,可以存储能在用户之间共享的响应

代理缓存

除了访问控制的功能外,一些代理还实现了缓存以减少网络流量

托管缓存

由服务开发人员明确部署,以降低源服务器负载并有效地交付内容,包括反向代理、CDN 和 service worker 与缓存 API 的组合

[!CAUTION]

HTTP 缓存规范本质上没有定义显式删除缓存的方法——但是使用托管缓存,可以通过仪表板操作、API 调用、重新启动等实时删除已经存储的响应

启发式缓存

HTTP 旨在尽可能多地缓存,因此即使没有给出 Cache-Control,如果满足某些条件,响应也会被存储和重用

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified(最后修改时间): Tue, 22 Feb 2021 22:22:22 GMT

<!doctype html>

采取以上响应。此回复最后一次更新是在 1 年前

基于 age 的缓存策略

存储的 HTTP 响应有两种状态:freshstalefresh 状态通常表示响应仍然有效,可以重复使用,而 stale 状态表示缓存的响应已经过期

确定响应何时是 fresh 的和何时是 stale 的标准是 age(在 HTTP/1.0 中,有效期是通过 Expires 标头来指定的,在 HTTP/1.1 中,Cache-Control 采用了 max-age——用于指定经过的时间)

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=604800

<!doctype html>

当响应存储在共享缓存中时,有必要通知客户端响应的 age。,如果共享缓存将响应存储了一天,则共享缓存将向后续客户端请求发送以下响应

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Cache-Control: max-age=604800
Age: 86400

<!doctype html>

Vary响应

区分响应的方式本质上是基于它们的 URL

Vary 标头的值中添加“Accept-Language”,根据语言单独缓存响应

1
Vary: Accept-Language

这会导致缓存基于响应 URL 和 Accept-Language请求标头的组合进行键控

验证响应

验证是通过使用包含 If-Modified-SinceIf-None-Match 请求标头的条件请求完成的

If-Modified-Since

下面的请求显示客户端发送带有 If-Modified-Since 请求标头的请求,以询问服务器自指定时间以来是否有任何的改变

1
2
3
4
GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-Modified-Since: Tue, 22 Feb 2022 22:00:00 GMT

如果内容自指定时间以来没有更改,服务器将响应 304 Not Modified

ETag/If-None-Match

如果 ETag 标头使用了 hash 值,index.html 资源的 hash 值是 deadbeef,响应如下

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
ETag: "deadbeef"
Cache-Control: max-age=3600

<!doctype html>

如果该响应是陈旧的,则客户端获取缓存响应的 ETag 响应标头的值,并将其放入 If-None-Match 请求标头中,以询问服务器资源是否已被修改

1
2
3
4
GET /index.html HTTP/1.1
Host: example.com
Accept: text/html
If-None-Match: "deadbeef"

如果服务器为请求的资源确定的 ETag 标头的值与请求中的 If-None-Match 值相同,则服务器将返回 304 Not Modified,如果服务器确定请求的资源现在应该具有不同的 ETag 值,则服务器将其改为 200 OK 和资源的最新版本进行响应

[!TIP]

可将ETag 和 Last-Modified 结合使用,ETag 提供高精度,而 Last-Modified 提供简单的时间戳,配合使用可以更高效地实现缓存控制

强制重新验证

如果你不希望重复使用响应,而是希望始终从服务器获取最新内容,则可以使用 no-cache 指令强制验证

**no-cache**:允许缓存,不允许直接使用缓存的数据,每次使用缓存前都要向服务器验证数据的有效性

通过在响应中添加 Cache-Control: no-cache 以及 Last-ModifiedETag。如果请求的资源已更新,客户端将收到 200 OK 响应,否则,如果请求的资源尚未更新,则会收到 304 Not Modified 响应

1
2
3
4
5
6
7
8
9
10
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1024
Date: Tue, 22 Feb 2022 22:22:22 GMT
Last-Modified: Tue, 22 Feb 2022 22:00:00 GMT
ETag: deadbeef
Cache-Control: no-cache

<!doctype html>

不使用缓存

no-cache 指令不会阻止响应的存储,而是阻止在没有重新验证的情况下重用响应

使用 private 指令将使个性化响仅与特定客户端一起存储,而不会泄露给缓存的任何其他用户,但在这种情况下,即使设置了 no-store,也必须设置 private

重新加载和强制重新加载

重新加载

1
2
3
4
5
GET / HTTP/1.1
Host: example.com
Cache-Control: max-age=0
If-None-Match: "deadbeef"
If-Modified-Since: Tue, 22 Feb 2022 20:20:20 GMT

请求中的 max-age=0 指令指定“重用 age 为 0 或更少的响应”——因此,中间存储的响应不会被重用

请求通过 If-None-MatchIf-Modified-Since 进行验证

强制重新加载

1
2
3
4
GET / HTTP/1.1
Host: example.com
Pragma: no-cache
Cache-Control: no-cache

由于这不是带有 no-cache 的条件请求,因此你可以确定你会从源服务器获得 200 OK

[!CAUTION]

“no-cache”是正常重新加载的正确模式
fetch(“/“, { cache: “no-cache” });

“reload”是“强制重新加载”的正确模式
fetch(“/“, { cache: “reload” });

避免重新验证

为了防止用户重新加载时,服务器在内容是不可变时,也会发送重新验证请求,immutable 指令可用于明确指示不需要重新验证

1
Cache-Control: max-age=31536000, immutable

HTTP重定向

HTTP 重定向是服务器告诉客户端(通常是浏览器)将请求重定向到另一个 URL 的一种机制。这通常发生在资源已被移动、网站结构发生更改或服务器需要将请求路由到不同位置的情况下

HTTP 重定向通过返回特定的状态码和 Location 响应头来实现,浏览器收到这些信息后,会自动发起对新的 URL 的请求

永久重定向

表示原 URL 不应再被使用,而选用新的 URL 替换它

搜索引擎机器人、RSS 阅读器以及其他爬虫将更新资源原始的 URL

状态码 状态文本 处理方法 典型应用场景
301 Moved Permanently GET 方法不会发生变更。其他方法有可能会变更为 GET 方法。 网站重构。
308 Permanent Redirect 方法和消息主体都不发生变化。 使用用于非 GET 链接/操作重组网站。

临时重定向

有时候请求的资源无法从其标准地址访问,但是却可以从另外的地方访问。在这种情况下,可以使用临时重定向。在创建、更新或者删除资源的时候,临时重定向也可以用于显示临时性的进度页面

搜索引擎和其他爬虫不会记录新的、临时的 URL。

状态码 状态文本 处理方法 典型应用场景
302 Found GET 方法不会发生变更。其他方法有可能会变更为 GET 方法。[2] 由于不可预见的原因该页面暂不可用。
303 See Other GET 方法不会发生变更,其他方法会变更GET 方法(消息主体丢失)。 用于 PUTPOST 请求完成之后重定向,来防止由于页面刷新导致的操作的重复触发。
307 Temporary Redirect 方法和消息主体都不发生变化。 由于不可预见的原因该页面暂不可用。当站点支持非 GET 方法的链接或操作的时候,该状态码优于 302 状态码。

特殊重定向

状态码 状态文本 典型应用场景
300 Multiple Choice 不常用:所有的选项在消息主体的 HTML 页面中列出。鼓励在 Link 标头中加入机器可读的 rel=alternate
304 Not Modified 发送用于重新验证的条件请求。表示缓存的响应仍然是新的并且可以使用。

指向重定向的其他方式

HTML重定向机制

用于将用户的请求从一个页面自动转到另一个页面

可以尝试在页面的<head>中添加一个<meta>元素,并将其 http-equiv 属性的值设置为 refresh,可以告诉浏览器在指定的时间间隔后自动加载另一个页面。

该方法仅适用于 HTML 页面,然而并不能应用于图片或者其他类型的内容

1
2
3
<head>
<meta http-equiv="Refresh" content="0(秒数); URL=http://example.com/(目标网址)" />
</head>

借助DOM的 JavaScript 重定向机制

通常使用 window.locationwindow.location.replace() 方法

window.location 通过改变浏览器的地址栏实现重定向,相当于用户点击了浏览器的地址栏并输入新的 URL

1
window.location = 'https://www.example.com';

window.location.replace() 会将当前页面从浏览器历史记录中移除,并跳转到新页面。它不会在浏览器的历史记录中留下当前页面,因此用户无法通过后退按钮回到当前页面。相当于无痕模式

1
window.location.replace('https://www.example.com');

应用场景

域名别称

扩大站点的用户覆盖面,迁移到新的域名,强制使用HTTPS

保持链接有效

当你重构 Web 站点的时候,资源的 URL 会发生改变。即便是你更新站点内部的链接来匹配新的 URL,也无法控制被外部资源使用的 URL。你并不想因此而使旧链接失效,因为它们会为你带来宝贵的用户并且帮助优化你的 SEO(搜索引擎优化,是一种通过优化网站和网页内容来提高其在搜索引擎结果页面(SERP)中的排名,从而增加网站的可见性和访问量的技术和策略),所以需要建立从旧链接到新链接的重定向映射

对于不安全请求、耗时请求的临时响应

配置重定向

通常,HTTP 重定向是由服务器配置文件(如 Apache 的 .htaccess 或 Nginx 的配置文件)实现的。

Apache 中使用 .htaccess 文件设置 301 重定向:

1
Redirect 301 /oldpage http://example.com/newpage

Nginx 中设置 301 重定向:

1
2
3
4
5
server {
location /oldpage {
return 301 http://example.com/newpage;
}
}

HTTP条件请求

可以用来验证缓存的有效性,省去不必要的控制手段,以及验证文件的完整性,例如在断点续传的场景下或者在上传或者修改服务器端的文件的时候避免更新丢失问题

[!NOTE]

对于安全方法来说,例如 GET,通常用来获取文件,条件请求可以被用来限定仅在满足条件的情况下返回文件。这样可以节省带宽。

对于非安全方法来说,例如 PUT 方法,通常用来上传文件,条件请求可以被用来限定仅在满足文件的初始版本与服务器上的版本相同的条件下才会将其上传。

验证器

在请求中会传递一个描述资源版本的值,这些值称为“验证器”(1.文件的最后修改时间,即 last-modified(最后修改)时间;2.一个意义模糊的字符串,指代一个独一无二的版本,称为“实体标签”,或者 etag

Last-ModifiedETag 首部均可应用于两种验证类型

强验证类型

应用于需要逐个字节相对应的情况,例如需要进行断点续传的时候

通常这是由 ETag 首部来完成的,该首部可以提供使用 MD5 算法获取的资源(或其衍生品)的散列值

弱验证类型

应用于用户代理只需要确认资源内容相同即可。即便是有细微差别也可以接受,比如显示的广告不同,或者是页脚的时间不同

条件首部

If-Match

如果远端资源的实体标签与在 ETag 这个首部中列出的值相同的话,表示条件匹配成功。默认地,除非实体标签带有 ‘W/‘ 前缀,否者它将会执行强验证。

If-None-Match

如果远端资源的实体标签与在 ETag 这个首部中列出的值都不相同的话,表示条件匹配成功。默认地,除非实体标签带有 ‘W/‘ 前缀,否者它将会执行强验证。

If-Modified-Since

如果远端资源的 Last-Modified 首部标识的日期比在该首部中列出的值要更晚,表示条件匹配成功。

If-Unmodified-Since

如果远端资源的 HTTPHeader(“Last-Modified”)}} 首部标识的日期比在该首部中列出的值要更早或相同,表示条件匹配成功。

If-Range

If-MatchIf-Unmodified-Since 相似,但是只能含有一个实体标签或者日期值。如果匹配失败,则条件请求宣告失败,此时将不会返回 206 Partial Content 响应码,而是返回 200 OK 响应码,以及完整的资源。

HTTP范围请求

HTTP 的 Range 请求使客户端能够要求服务器仅向其回传 HTTP 消息的一部分。

范围请求对于支持随机访问的媒体播放器、明确只需大型文件某部分的数据处理工具,以及允许用户暂停及恢复下载的下载管理器等客户端尤其有用

检测服务器端是否支持范围请求

如果 HTTP 响应中存在 Accept-Ranges 标头:

1.其值不是“none”,那么该服务器支持范围请求。你可以通过使用像 cURL 这样的工具发出一个 HEAD 请求来进行手动检查

1
curl -I http://i.imgur.com/z4d4kWk.jpg
1
2
3
4
HTTP/1.1 200 OK

Accept-Ranges: bytes
Content-Length: 146515

2.如果网站省略了 Accept-Ranges 标头,那么它们很可能不支持部分请求。有些网站会包含这个标头,但明确将其值设为“none”,以表明它们不支持这一特性

1
2
3
HTTP/1.1 200 OK

Accept-Ranges: none

在这种情况下,下载管理器可能会禁用暂停按钮

从服务器端请求特定的范围

在 HTTP 请求中包含 Range 标头,可以指定希望服务器返回文档的哪一部分或哪些部分

单一范围

curl的“-H”选项会向请求中添加一个标头,即 Range 标头

1
curl http://i.imgur.com/z4d4kWk.jpg -i -H "Range: bytes=0-1023"

用于请求1024字节

发出请求如下

1
2
3
GET /z4d4kWk.jpg HTTP/1.1
Host: i.imgur.com
Range: bytes=0-1023

服务器将会返回 206 Partial Content 状态

1
2
3
4
5
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/146515
Content-Length: 1024

(二进制内容)

多重范围

Range 标头还允许在文档中支持一次性获取多重范围。这些范围使用逗号分隔

1
curl http://www.example.com -i -H "Range: bytes=0-50, 100-150"

服务器以 206 Partial Content 状态码以及 Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5 标头响应,表明随后的数据将采用多部分字节范围格式。

每个部分都携带自己的 Content-TypeContent-Range 字段,而必须的 boundary 参数定义了用于分割每个消息体部分的边界字符串

条件式范围请求

If-Range HTTP 请求标头使范围请求具有条件性:如果条件兑现,则会发起范围请求,服务器将以包含适当内容主体的 206 Partial Content 响应进行回复。如果条件不兑现,会被发送完整的资源回来,并带有 200 OK 状态。该标头可与 Last-Modified 验证器或 ETag 一起使用,但不能同时使用

1
If-Range: Wed, 21 Oct 2015 07:28:00 GMT

范围请求的响应

206 Partial Content 范围请求成功

416 Requested Range Not Satisfiable超出范围的范围请求

200 OK不支持范围请求,并传输完整的响应体

HTTP内容协商

一种 HTTP 机制,它允许客户端和服务器在处理请求时根据客户端的需求和服务器支持的内容来协商返回的资源形式。这种机制通常用在需要提供不同格式、语言、编码或版本的资源时

服务端驱动型

服务器根据客户端在请求头中提供的信息来决定返回的资源版本

Accept标头

列举了用户代理希望接收的媒体资源的 MIME 类型(如 text/html, application/json

Accept-CH标头

列出了服务器可以用来选择合适响应的配置数据

含义
Device-Memory 标明客户端设备的内存大小。该值是个估计值,设备的实际内存值会向 2 的次方取整,且除以 1024。比如 512MB 的内存对应的值为 0.5
Viewport-Width 标明用 CSS 像素数值表示的布局视口(layout viewport)宽度。
Width 标明用物理像素值表示的资源宽度(换句话说就是一张图片的固有大小)。

Accept-Encoding标头

说明了(接收端)可以接受的内容编码形式(所支持的压缩算法)(如 gzip, deflate

Accept-Language标头

提示用户期望获得的自然语言的优先顺序(如 en-US, zh-CN

User-Agent标头

提供客户端的设备信息

Vary响应标头

用于告诉客户端和代理服务器(如缓存服务器),哪些请求头会影响返回内容的变化

大多数情况下,vary用于在使用内容协商时创建缓存键

1
2
Vary: *
Vary: <header-name>, <header-name>, ...

*表示响应因无法确定的原因而改变,禁止缓存此响应

<header-name>指影响响应内容的请求头名称,例如 AcceptAccept-Language

如果服务器根据 Accept 请求头返回不同的资源(如 JSON 或 HTML),则应该在响应中添加:

1
Vary: Accept

如果服务器根据 Accept-Language 返回不同的语言内容:

1
Vary: Accept-Language

如果服务器根据 Accept-Encoding 提供不同的压缩格式(如 gzip 或 br):

1
Vary: Accept-Encoding

如果服务器根据 User-Agent 提供不同的内容(如桌面和移动设备优化):

1
Vary: User-Agent

代理驱动型(响应式协商)

客户端和服务器通过多次交互来确定最佳资源版本,面临不明确的请求时,服务器会返回一个页面,其中包含了可供选择的资源的链接

HTTP安全

用户信息安全

如何关闭表单自动填充

浏览器能够提供自动补全(在用户开始输入的时候给用户提供可能的内容)和自动填充(在加载的时候预先填充某些字段)功能,但可能涉及用户的隐私,因此浏览器允许用户禁用这些功能

禁止自动补全

autocomplete 的属性设置为 “off”

为整个表单设置:

1
<form method="post" action="/form" autocomplete="off">[…]</form>

为表单中某个输入元素单独设置:

1
2
3
4
5
6
7
<form method="post" action="/form">
[…]
<div>
<label for="cc">信用卡:</label>
<input type="text" id="cc" name="cc" autocomplete="off" />
</div>
</form>

一些浏览器(如 Chrome 和 Edge)即使设置了 autocomplete="off",仍会尝试为用户名和密码字段提供自动填充功能,那么你需要更改输入元素的 name 属性(浏览器通常会根据字段的 name 属性判断其用途,可以为这些字段设置随机或无意义的 name 值)

autocomplete 属性和登录字段

如果你定义了一个用户管理页面,其中用户可以为其他人指定新的密码,因此你想阻止密码字段的自动填充,你可以使用 autocomplete="new-password"

内容安全

正确配置服务器 MIME 类型

  • text/html对于一般网页
  • text/plain对于一般文本
  • text/css对于级联样式表
  • text/javascript对于脚本
  • application/octet-stream意味着“下载这个文件”
  • application/x-java-applet对于 Java 小程序
  • application/pdf对于 PDF 文档

严格传输安全 (HTTP Strict Transport Security, HSTS)

通过强制使用 HTTPS 而非 HTTP 来防止协议降级攻击和会话劫持

1
Strict-Transport-Security: max-age=<expire-time>[; includeSubDomains][; preload]

max-age: 必须指定,表示 HSTS 策略的有效期(单位:秒)。例如,max-age=31536000 表示策略有效期为 1 年。

includeSubDomains: 可选,表示 HSTS 策略适用于主域及其所有子域。

如果设置了该参数,则 sub.example.com 也会强制使用 HTTPS。

preload: 可选,指示该域名申请加入浏览器的 HSTS 预加载列表。需要通过 HSTS Preload 提交,当使用 , 指令必须至少是 (一年),并且必须存在指令

跨源资源共享(CORS)

一种基于 HTTP 标头的机制,用于允许服务器声明哪些来源(域名)可以访问其资源。它解决了浏览器的 同源策略 限制问题,从而安全地实现跨域资源的共享

简单请求

请求方法是 GETPOSTHEAD

不包含自定义的请求标头(如 Authorization)。

Content-Type 标头所指定的媒体类型的值仅限于下列三者之一:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

浏览器会直接发送请求,并根据响应头判断是否允许访问

1
2
3
GET /api/data HTTP/1.1 
Host: api.example.com
Origin: https://example.com
1
2
HTTP/1.1 200 OK 
Access-Control-Allow-Origin: https://example.com

如果 Access-Control-Allow-Origin 标头的值与请求的 Origin 匹配,则浏览器允许访问响应数据

预检请求

对于非简单请求(如使用 PUTDELETE 方法,或自定义标头),浏览器会在正式请求之前发送一个 预检请求OPTIONS 请求),以确保服务器允许此操作

1
2
3
4
5
OPTIONS /api/data HTTP/1.1
Host: api.example.com
Origin: https://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Authorization
1
2
3
4
5
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: Authorization
Access-Control-Max-Age: 3600

Access-Control-Allow-Origin: 指定允许的来源。

Access-Control-Allow-Methods: 列出允许的 HTTP 方法。

Access-Control-Allow-Headers: 列出允许的请求标头。

Access-Control-Max-Age: 指定预检结果的缓存时间(秒)

带凭据的请求

如果请求需要发送凭据(如 Cookies 或 HTTP 认证),需要满足以下条件:

  • 客户端在请求中设置 withCredentialstrue
  • 服务器返回的响应头包含 Access-Control-Allow-Credentials: true
1
2
3
4
GET /api/data HTTP/1.1
Host: api.example.com
Origin: https://example.com
Cookie: sessionId=abc123
1
2
3
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

如果 Access-Control-Allow-CredentialstrueAccess-Control-Allow-Origin 必须为具体的来源,不能使用通配符 *

HTTP响应标头字段

1.Access-Control-Allow-Origin

1
Access-Control-Allow-Origin: <origin> | *

Access-Control-Allow-Origin 参数指定了单一的源,告诉浏览器允许该源访问资源。或者,对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符“*”,表示允许来自任意源的请求

2.Access-Control-Expose-Headers

跨源访问时,XMLHttpRequest 对象的 getResponseHeader() 方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头

将指定标头放入允许列表中,供浏览器的 JavaScript 代码(如 getResponseHeader())获取

1
Access-Control-Expose-Headers: <header-name>[, <header-name>]*

3.Access-Control-Max-Age

指定了 preflight 请求的结果能够被缓存多久

1
Access-Control-Max-Age: <delta-seconds>

4.Access-Control-Allow-Credentials

指定了当浏览器的 credentials 设置为 true 时是否允许浏览器读取 response 的内容

1
Access-Control-Allow-Credentials: true

5.Access-Control-Allow-Methods

指定了访问资源时允许使用的请求方法,用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法

1
Access-Control-Allow-Methods: <method>[, <method>]*

6.Access-Control-Allow-Headers

用于预检请求的响应。其指明了实际请求中允许携带的标头字段

1
Access-Control-Allow-Headers: <header-name>[, <header-name>]*
HTTP请求标头字段

1.Origin

表明预检请求或实际跨源请求的源站

1
Origin: <origin>

origin 参数的值为源站 URL。它不包含任何路径信息,只是服务器名称

2.Access-Control-Request-Method

用于预检请求。其作用是,将实际请求所使用的 HTTP 方法告诉服务器

1
Access-Control-Request-Method: <method>

3.Access-Control-Request-Headers

用于预检请求,将实际请求所携带的标头字段(通过 setRequestHeader() 等设置的)告诉服务器。这个浏览器端标头将由互补的服务器端标头 Access-Control-Allow-Headers 回答

1
Access-Control-Request-Headers: <field-name>[, <field-name>]*

内容安全策略(CSP)

是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本(XSS)和数据注入攻击等

为使 CSP 可用,你需要配置你的网络服务器返回 Content-Security-Policy HTTP 标头,除此之外,<meta>元素也可以被用来配置该策略

1
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted.cdn.com">
CSP 示例配置
基本配置

只允许从同源加载资源,禁止内联脚本:

1
Content-Security-Policy: default-src 'self'; script-src 'self';
允许可信第三方资源

允许从指定的 CDN 加载 JavaScript 和样式:

1
Content-Security-Policy: script-src 'self' https://cdn.example.com; style-src 'self' https://cdn.example.com;
启用报告机制

当策略被违反时,将报告发送到指定的 URL:

1
Content-Security-Policy: default-src 'self'; report-uri /csp-report-endpoint;
防止 iframe 嵌套

禁止页面被嵌套在 <iframe> 中,防止点击劫持攻击:

1
Content-Security-Policy: frame-ancestors 'none';

屏幕截图 2025-01-22 092240

X 框架选项

‘’’

HTTP标头