由于现在的业务系统通常提供 Web 服务,因此 Web 安全成为了企业/单位网络安全中的重要部分。这篇文档记录了对 HTTP 与 HTTPS 的学习笔记。由于 HTTPS 仅仅只是再数据传输过程中套了一个 TLS 的壳子,因此也放到这篇文档中。关于 TLS 的学习会另写一篇文档。


Term

  1. HTTP (Hyper Text Transfer Protocol over Secure Socket Layer, 超文本传输协议),
  2. HTTPS (Hyper Text Transfer Protocol Secure, 超文本传输安全协议):是以安全为目标的 HTTP 通道,在 HTTP 协议的基础上,在传输层使用 TLS 对通信过程进行加密。简单讲是 HTTP 的安全版。
  3. ‌RFC‌ (Request for Comments, 请求注解):是 IETF 发布的一系列技术文档,定义了互联网的核心协议与标准。
  4. TLS (Transport Layer Security, 传输层安全协议):用于在两个通信应用程序之间提供保密性和数据完整性。该协议由两层组成: TLS 记录协议 (TLS Record) 和 TLS 握手协议 (TLS Handshake),是更新、更安全的 SSL 版本。
  5. SSL (Secure Sockets Layer, 安全套接层协议):
  6. IETF (Internet Engineering Task Force, Internet 工作小组):
  7. URL (uniform resource locator, 统一资源定位系统):
  8. W3C (World Wide Web Consortium, 万维网联盟):

HTTP 协议简介

HTTP (Hyper Text Transfer Protocol, 超文本传输协议) 的发展是 W3C 和 IETF 合作的结果,他们最终发布了一系列的 RFC,RFC 1945 定义了 HTTP/1.0 版本。其中最著名的就是 RFC 2616,它定义了今天普遍使用的一个版本,HTTP 1.1

HTTP 是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型 (C/S)。是一个无状态的协议。用于从 WWW 服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪一部分,以及哪部分内容首先显示(如文本先于图形)等。

**HTTP 和 HTTPS:HTTP 协议和 HTTPS 协议都是一种发布和接收 HTML 页面的方法。两者之间的区别在于 HTTPS 是 HTTP 的安全版,在 HTTP 下加入 SSL/TLS 层。HTTP 和 HTTPS 均是由 TCP 协议封装而来,在进行 HTTP 协议和 HTTPS 协议时,需要进行三次握手和四次挥手。HTTP 使用 80 端口,HTTPS 使用 443 端口。


HTTP 数据报文

客户端与服务器端之间的通信,通过 HTTP 协议,以 HTTP 报文的形式来实现数据的交互。HTTP 报文是 HTTP 通信时发送的数据块,由三部分组成:状态行(请求行 | 响应行)、首部主体。也有些书籍说是由首部和主体两部分组成,状态行包含在首部中,但绝大多数的说法是由三部分组成。HTTP报文可以分为请求报文响应报文;请求报文向服务器传达请求,响应报文将请求的结果返回给客户端。大致上,HTTP 数据报文的结构如下:

+-----------------------+
| Request / Status Line |
+-----------------------+
| Header                |
+-----------------------+
| (Blank Line)          |
+-----------------------+
| Payuload-Body         |
+-----------------------+

HTTP 报文以状态行开始,跟在后面的是 HTTP 首部。首部由多个首部字段构成,每行一个首部字段;HTTP 首部后是一个空行,然后是报文主体。

可以看到,状态行和首部中的每行都是以回车符(\r%0dCR)和换行符(\n%0aLF)结束,这是因为 HTTP 规范中行应该使用 CRLF 结束。另外,首部和主体之间由一空行隔开,或者可以理解为 HTTP 首部的最后一个字段有两个 CRLF。与状态行和首部不同的是,主体是可选的,也就是说报文中不一定要有主体;另外状态行和首部是 ASCII 文本,主体可包含文本或二进制数据(如图片、视频、软件等)。

以上就是 HTTP 报文的大概结构,下面直观地展示一下报文格式:


请求报文结构

要注意的一点就是,即使请求报文的 body 部分是空的,请求头部后的回车换行符也是必须要有的。

GET /test/hi-there.txt HTTP/1.0
Accept: text/*
Accept-Language: em,fr
Host:www.joes-hardware.com

HTTP报文以状态行开始,请求报文中的状态行叫请求行。

请求行 (GET /test/hi-there.txt HTTP/1.0):由请求方法URL协议版本组成,这些字段都由空格分隔。请求行表明了要对哪个资源执行哪个方法,具体有哪些请求方法,文章后面会详细介绍。

  1. GET:请求方法
  2. /test/hi-there.txt:URL,表示要获取的资源位于服务器的 /test/hi-there.txt 路径下
  3. HTTP/1.0:HTTP 协议的版本,这里使用的是 HTTPv1

首部:HTTP 首部由多个首部字段构成,旨在向报文中添加一些通信过程中所需的重要信息。

  1. Accept: text/*
  2. Accept-Language: em,fr
  3. Host:www.joes-hardware.com

这个请求报文不包含请求主体。因此空白行之后没有内容


响应报文结构

HTTP/1.0 200 OK
Content-type: text/plain
Content-length: 19

Hi! i'm a message!

HTTP 响应报文中的状态行叫响应行。响应行由协议版本状态码原因短语(状态码描述)组成。这些字段同样都由空格分隔。响应行表明了服务器对请求的处理结果,由状态码体现。

  1. HTTP/1.0:HTTP 协议的版本

  2. 200:状态码。HTTP 协议将状态码分成了 5 类,在下面的章节中会详细介绍。

  3. OK:原因短语。值得注意的是,原因短语是数字状态码的可读版本,描述数字状态码的含义,便于人理解,只对人有意义,因此以下两种响应行都会被当作成功处理。

    HTTP/1.0 200 NOT OK
    HTTP/1.0 200 OK
    

首部:HTTP 首部由多个首部字段构成,旨在向报文中添加一些通信过程中所需的重要信息。

  1. Content-type: text/plain
  2. Content-length: 19

主体:报文主体包含了HTTP所要传输的内容。这里的主体数据为 Hi! i'm a message!。也是请求体 URL /test/hi-there.txt 所对应的资源。


HTTP 版本号

请求行和响应行中都包含 HTTP 版本号,其格式为:

HTTP/<major>.<minor>

major是主版本号,minor是次版本号,使用版本号的目的是规范双方之间通信的格式。


HTTP 请求方法

状态行部分讲到请求行中包含请求方法字段,请求方法告诉服务器要做什么。下图是HTTP规范中目前已定义的方法,绿色的是比较常用的方法。

首先,方法名称是区分大小写的。当你使用 get 作为请求方法去访问时,响应体状态码为 501(Not Implemented),因为服务器不认识这个请求方法。另外,服务器可以指定某个资源所支持的请求方法。如果贸然混用请求方法,可能导致 405(Method Not Allowed)。

作为最常用的两种请求方法,HTTP 服务器至少应该实现对 GET 和 HEAD 方法的支持,其他方法都是可选的。当然,所有的方法支持的实现都应当匹配下述的方法各自的语义定义。

首先简要的介绍常用的几种请求方法:

GET 用于请求获取实体资源,没有正文,提交的数据在 URL 的查询字符串中,以 ? 分割 URL 和传输数据,参数之间以 & 相连,如 EditBook?name=test1&id=123456.(请求头里面那个 content-type 做的这种参数形式,后面讲)。但提交的数据有长度限制(主要因为浏览器对 URL 长度有限制),且不安全。

HEAD 与 GET 类似,但是响应数据不要正文,只要头部。

POST 用于向服务器提交表单数据,有正文,且提交的数据在正文中,因此没有长度限制(甚至不限于 ASCII 字符)。

请求方法的属性

请求方法 说明 HTTP版本 RFC Request payload body Response payload body 安全的 幂等的 可缓存的
GET 获取资源 1.0、1.1 RFC 9110 可选
HEAD 获得报文首部 1.0、1.1 RFC 9110 可选
POST 传输实体主体 1.0、1.1 RFC 9110
PUT 传输文件 1.0、1.1 RFC 9110
DELETE 删除文件 1.0、1.1 RFC 9110 可选
CONNECT 要求用隧道协议连接代理 1.1 RFC 9110 可选
OPTIONS 询问支持的方法 1.1 RFC 9110 可选
TRACE 追踪路径 1.1 RFC 9110

GET 方法

获取/查询资源的方法。是最常用的 HTTP 方法,常用于请求 URL 指定的资源,服务端经过处理将资源返回给客户端。比如访问百度首页,请求包如下,百度的服务器收到请求后,将百度首页返回给浏览器。

Request

GET / HTTP/1.1
Host: www.baidu.com
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept:text/html, application/xhtml+xml, application/xml; q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip,deflate
Accept-Language: zh-CN, zh; q=0.9
Cookie: BAIDUID=3EB9DFOBEFF98AASCC37B1DA4866644F:FG=1; BIDUPSID=3EBSDFOBBFF98AASCC37B1DA4866644F; PSTM=1531984106;_cfduid=d6caa21eb35d80a3e61cdea5ca03e80231533714714; BD_UPN=12314753; ispeed_lsm=2; BDORZ=B490ESEBF6F3CD402E515D22BCDA1598; delPer=0; BD_CK_SAM=1; PSINO=6; H _PS_PSSID=26524_1444_21114_27401_27376_26350; H_PS_645EC=521311xfITt632BihMECE2DBv67E3%2Bid7J2qBOKsVXXK2BcIC1LLHr6dLancPDtI; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4 HKm; userFrom=www.baidu.com; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm; BD_HOME=0

有时请求会传递一些参数给服务器,在 GET 方法中,这些参数会被包含在 URL 中,放在文件路径后面,用 ? 分隔,被称为查询字符串。查询字符串以键值对的形式存在,每个参数的键和值用 = 连接,不同参数之间用 & 符号连接。(详情请看 URL 格式)

百度搜索,对应的 URL 为:https://www.baidu.com/s?wd=google&tn=52176495_dg&ch=3&ie=utf-8。可以看出,google 被当成了 wd (word) 参数的参数值,放在请求行的 URL 字段中。抓包结果如下:

Request

GET /baidu?&ie=utf-s&word=google HTTP/1.1
Host: www.baidu.com
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/63.0.3239.132 Safari/537.36
Accept:text/html, application/xhtml+xml, application/xml; q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN, zh; q=0.9
Cookie: BAIDUID=3EB9DFOBBFF98AA8CC37E1DA1866644F:FG=1;BIDUPSID=3EESDFOEBFF98AASCC3TB1DA4866644F; PSTM=1531984106;_cfduid=dcaa21eb35d80a3e61cdea5ca03e80231533714714; BD_UPN=12314753;ispeed_lsm=2;BDORZ=B490B5EBFGF3CD402E515D22BCDA1598; H_PS_PSSID=26524_144421114_27401_27376_26350;delPer=0; BD_CK_SAM=1; BD_HOME=0; rsv_jmp_slow=1542165083809; PSINO=3;H_PS_645EC=05a2w00cxE6HWIKTGHW590yrQ7hIpMwPTL6hcxoTHRlbp596jUQyysDdlik; BDSVRIM=0

特意将请求报文全选,大家可以看到,首部字段下方有一空行,然后空行下面有一光标,这再次体现了 HTTP 报文的结构,也告诉大家,GET 请求是不包含请求主体的。


POST 方法

传输实体主体的方法。常用于向指定资源发送数据,指定的资源会对数据进行处理,然后将处理结果返回给客户端。数据被包含在请求主体中,一般用于表单提交、文件上传等。

Request

POST /index.php?c=login&m=tologin HTTP/1.1
Host: xxx.xxx.xxx
Content-Length: 48
Accept: application/json, text/javascript,*/*;q=0.01
Origin: <略>
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOWG4) AppleWebKit/537.36(KHTML,like Gecko) Chrome/63.0.3239.132_Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: <略>
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh; q=0.9
Cookie: <略>
Connection: close

name=root&pwd=root&code=eanwt&saveusername=false

以上是登录时的请求包,请求主体中包含了用户名、密码、验证码、是否记住用户名的参数数据,服务器接收到请求后,会交给 index.php 文件去处理,然后会返回一个处理结果,可能是登录失败,也可能是跳转到系统内部。可以看到,这些数据的格式和查询字符串的格式一致,Content-Type 字段值为 application/x-www-form-urlencoded,这是 POST 提交数据的几种格式之一,POST 提交数据的几种格式会在以后的文章中介绍。

当然,GET 方法也可以用来传输数据,但是首先 URL 的长度受浏览器、服务器、操作系统影响,其次是 GET 方法提交的参数都会在地址栏中显示出来,不安全,因此涉及到大量数据、敏感数据的时候,一般采用 POST 方法。


HEAD 方法

获取报文首部的方法。HEAD 方法和 GET 方法很像,但服务器接收到 HEAD 请求时,在响应中只会返回报文首部,不会返回报文主体。常用于测试请求资源是否存在或是否被修改。

我们再来看一下 GET 请求方法的响应体格式。

Request 01

GET / HTTP/1.1
Host :www.baidu.com
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html, application/xhtml+xml, application/xml; q=0.9, image/webp, image/apng, */*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Langaage: zh-CN,zh; q=0.9
Cockie: BAIBUID=3EB9DF0BBFF98AA8CC37B1DA4866644F:FG=1;BIDUPSID=3EB9DF0BBFF98AA8CC37B1DA4866644F; PSIM=1531984106; __cfduid=d6caa21eb35d80a3e61cdea5ca03e80231533714714; BD_UPN=12314753; ispeed_lsm=2
 BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; delPer=O; BD_CK_SAN=1; PSINO=6; H_PS_PSSID=26524_1444_21114_27401_27376_26350;
 H_PS_645EC=52131lxfHt6%2BihMEcEkOBv67E3%2Bid7J2qB0KsVXX%2BcTClULwr6dLancPDtI; BDRCVFR[dG2JNUJb_ajR]=mk3SLVN4HKm; userFrom=www.baidu.com; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm
 

Response 01

HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: Ox8c0d593c0007ce95
Cache-Control: private
Content-Type: text/html
Cxy_all:. baidu+5c6559c8b999bl73a4ef66f7a285a3fa
Date: wed, 14 Nov 2018 08:31:55 GMT
Expires: wed.14 Nov 2018 08:31:04 CMT
Server: BWS/1.1
Set-Cookie: delPer=0; path=/; domain=.baida.com
Set-Cookie: BDSVRIM=0; path=/
Set-Cookie: BD_HOME=O; path=/
Set-Ccokie: H_PS_PSSID=26524_1444_21114_27401_27376_26350; path=/; domain=.baida.com
Strict-Transport-Security: max-age=172800
Vary: Accept-Eneoding
X-Ua-Compatible: IE=Edge, chrome=1
Connection: close
Content-Length: 121509

<!DOCTYPE html>
<!--STATUS OK-->

GET 请求,除了返回报文首部,还返回了主体,经浏览器解析,成为我们眼中的百度首部。而 HEAD 请求方法的响应体只包含状态行和 HEADER 部分。

Request 02

HEAD / HTTP/1.1
Host: www.baidu.com
Connection: close
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Accept: text/html, application/xaml+xml,application/xml; q=0.9,image/webp, image/apng, */*; q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN, zh; q=0.9
Cookie: BAIDUID=3EB9DFOBBFF98AA8CC37B1DA4866644F:FG=1; BIDUPSID=3EB9DF0BBFF98AA8CC37B1DA4866644F; psim=1531984106; __cfduid=d6caa21eb35d89a3e61cdea5ca03e80231533714714; BD_UPN=12314753; ispeed_lsm=2; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598; delPer=0; abd_ck_sam=1; PSINO=6; H_PS_PSSID=26524_1444_21144_27376_26350; H_PS_645EC=52131lxfHt6%2BihMEcEk0Bc67E3%2Bid7J2qB0KsVXX%2BcTClULwr6dLancPDtI; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; userFrom=www.baidu.com; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm

Response 02

HTTP/1.1 200 OK
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Content-Type: text/html
Date: Wed,14 Nov 2018 08.32.51 GMT
Last-Modified: Mon, 13 Jun 2016 02:50:37 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
Connection: close

HEAD 请求,只返回首部,没有主体。


OPTIONS 方法

查询资源支持的方法。用于查询 URL 指定的资源支持哪些方法,资源支持哪些方法,会在响应包的 Allow 字段中显示。

Request

OPTIONS / HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/xaml+xml, application/x-ms-xbap,*/*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.OE;   Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host: <略>
Pragma: no-cache
Cookie: <略>
Connection: close

Response

HTTP/1.1 200 OK
Allow: OPTIONS, TRACE, GET, HEAD, POST
Server: Microsoft-IIS/7.5
Public: OPTIONS, TRACE, GET, HEAD, POST
X-Powered-By: ASP.NET
Date: Wed, 14 Nov 2018 09:44:51 GMT
Connection: close
Content-Length: 0

PUT 方法

上传文件的方法。服务器会将请求主体的内容保存到 URL 指定的资源位置,包含两种情况,(1) URL 指定的资源不存在;(2) URL 指定的资源存在。

如果 URL 指定的资源不存在,服务器会新建一个文件,将请求主体中的内容保存到新建的文件里,响应码为 201。

Request 01

PUT /test.html HTTP/1.1
Accept: text/html, application/xhtml+xml, imagel jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0;WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 192.168.208.130:8080
Pragma: no-cache
Connection: close
Content-Length: 20

this is a test page!

Response 01

HTTP/1.1 201 Created
Content-Length:0 
Date: Thu, 15 Nov 2018 03:27:21 GMT
Connection: close

如果 URL 指定的资源存在,服务器会重置文件内容,用请求主体中的内容覆盖原文件内容,响应码为 200 或 204。

Request 02

PUT /test.html HTTP/1.1
Accept: text/html, application/xhtml+xml, imagel jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0;WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 192.168.208.130:8080
Pragma: no-cache
Connection: close
Content-Length: 20

this is a test page!

Response 02

HTTP/1.1 204 No Content
Date: Thu, 15 Nov 2018 08:27:41 GMT
Connection: close

需要注意的是,PUT 方法自身不带验证机制,任何人都可以执行,存在安全问题,所以网站一般不会使用 PUT 方法。


DELETE 方法

删除文件的方法。删除 URL 指定的资源,和 PUT 相反。

文件删除成功,响应码为 204。

Request 01

DELETE /test.html HTTP/1.1
Accept: text/html, application/ xhtml+xml, image/jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0: WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host : 192.168.208.130;8080
Pragma: no-cache
Connection: close
Content-Length: 0

Response 01

HTTP/1.1 204 No Content
Date: Thu, 15 Nov 2018 08:35:48 GMT
Connection: close

若删除的文件不存在,响应码为 404。

Request 02

DELETE /test.html HTTP/1.1
Accept: text/html, application/xhtml_xml, image/jxr, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host:192.168.208.130:8080
Pragma: no-cache
Connection: close
Content-Length: 0

Response 02

HTTP/1.1 404 Not Found
Content-Type: text/html:charset=utf-8
Content-Language: en
Content-Length:1047
Date: Thu, 15 Nov 2018 08:37:47 GMT
Connection: close

<!doctype html><html lang="en"><head><title>HTTP Status 404 - Not Found</title><style type="text/css">h1
{fout-family:tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;}h2
 ... ...

和 PUT 一样,DELETE 方法同样不带验证机制,所以网站一般也不使用 DELETE 方法。


TRACE 方法

路径追踪的方法。主要用于诊断,让服务器将收到的请求放在响应主体中,环回给客户端,这样客户端就可以判断发出的请求是否被请求/响应链(在客户端和服务器端之间,请求可能会经过代理、网关、防火墙等应用程序)篡改。

TRACE 请求不能带有实体的主体部分,TRACE 响应的实体主体包含服务器收到的请求。

Request 01

TRACE / HTTP/1.1
Accept: image/gif,image.jpeg,image/pjpeg,application/x-ms-application,application/xaml+xml,application/x-ms-xbap, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible: MSIE 7.0; Windows NT 10.0; WOW64: Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host:xxx.xxx.xxx
Pragma: no-cache
Connection: close

请求数据包被作为响应体返回,通过与原始请求数据包的对比,可以判断请求是否遭受篡改。

Response 01

HTTP/1.1 200 OK
Date: Thu, 15 Nov 2018 09:11:58 GMT
Server: Apache/2.2.23 (Unix) mod_jk/1.2.14
Connection: close
Content-Type: message/http
Content-Length: 378

TRACE / HTTP/1.1
Accept: image/gif,image.jpeg,image/pjpeg,application/x-ms-application,application/xaml+xml,application/x-ms-xbap, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible: MSIE 7.0; Windows NT 10.0; WOW64: Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host:xxx.xxx.xxx
Pragma: no-cache
Connection: close

以上都是方法没被禁用时的响应,如果方法被禁用,响应码为 405。

Request 02

PUT /test.html HTTP/1.1
Accept: image/gif,image.jpeg,image/pjpeg,application,application/xaml+xml,application/x-ms-xbap, */*
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible: MSIE 7.0; Windows NT 10.0; WOW64: Trident/7.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)
Accept-Encoding: gzip, deflate
Host:xxx.xxx.xxx
Pragma: no-cache
Connection: close
Content-Length: 19

this is a test page!

Response 02

HTTP/1.1 405 Method Not Allowed
Date: Thu, 15 Nov 2018 09:14:23 GMT
Server: Apache/2.2.23 (Unix) mod_jk/1.2.14
Allow: GET,HESD,POST,OPTIONS,TRACE
Content-Length: 230
Connection: close
Content-Type: text/html: charset-iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>405 Method Not Allowed</title>
</head><body>
<h1>Method Not Allowed</h1>
<p>The requested method PUT is not allowed for the URL /test.html.</p>
</body></html>

拓展请求方法

除了使用 HTTP/1.1 规范中定义的方法,特定的 HTTP 服务器还能够扩展自定义的方法,被称为扩展方法。例如 PATCH (由 RFC 5789 指定的方法) 用于将局部修改应用到资源。以下是 HTTP 扩展包含的方法。

请求方法 说明 HTTP版本 RFC Request payload body Response payload body 安全的 幂等的 可缓存的
COPY 在服务器上复制资源 1.1 RFC 4918 可选
LINK 建立和资源之间的联系 1.0 - 可选
LOCK 允许用户锁定资源 1.1 RFC 4918
MKCOL 允许用户创建集合(如目录) 1.1 RFC 4918 可选
MOVE 允许用户移动/重命名资源 1.1 RFC 4918 可选
PATCH 部分修改资源 1.1 RFC 5789
UNLINK 断开资源之间的联系 1.0 - 可选

补充说明。LOCK 允许用户锁定资源。一个常见的场景是,在编辑某个资源的时候将其锁定,以防别人同时对其进行修改。


GET 和 POST 的区别

GET 和 POST 是 HTTP 请求的两种基本方法,要说它们的区别,接触过 WEB 开发的人都能说出一二。譬如,GET 把参数包含在 URL 中,POST 通过 request body 传递参数。巴拉巴拉。甚至你可能自己写过无数个 GET 和 POST 请求,你非常清楚知道什么时候该用什么。当你在面试中被问到这个问题,你的内心充满了自信和喜悦。你轻轻松松的给出了一个标准答案

1. GET 在浏览器回退时是无害的,而 POST 会再次提交请求;
2. GET 产生的 URL 地址可以被 Bookmark,而 POST 不可以;
3. GET 请求会被浏览器主动cache,而 POST 不会,除非手动设置;
4. GET 请求只能进行 URL 编码,而 POST 支持多种编码方式;
5. GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留;
6. GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有;
7. 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制;
8. GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息;
9. GET 参数通过 URL 传递,POST 放在 Request body 中。

很遗憾,这不是我们要的回答!—— 如果我告诉你GET和POST本质上没有区别你信吗?

首先我们来理解清楚请求方法地本质。

GET 和 POST 是什么?HTTP 协议中的两种发送请求的方法。HTTP 是什么?HTTP 是基于 TCP/IP 的关于数据如何在万维网中如何通信的协议。HTTP 的底层是 TCP/IP。所以 GET 和POST 的底层也是 TCP/IP,也就是说,GET/POST 都是 TCP 链接。GET 和 POST 能做的事情是一样一样的。你要给 GET 加上 request body,给 POST 带上 url 参数,技术上是完全行的通的。

那么,标准答案里的那些区别是怎么回事?

在我大万维网世界中,TCP 就像汽车,我们用 TCP 来运输数据,它很可靠,从来不会发生丢件少件的现象。但是如果路上跑的全是看起来一模一样的汽车,那这个世界看起来是一团混乱,送急件的汽车可能被前面满载货物的汽车拦堵在路上,整个交通系统一定会瘫痪。为了避免这种情况发生,交通规则 HTTP 诞生了。HTTP 给汽车运输设定了好几个服务类别,有 GET, POST, PUT, DELETE 等等,HTTP 规定,当执行 GET 请求的时候,要给汽车贴上 GET 的标签(设置 method 为 GET),而且要求把传送的数据放在车顶上(url 中)以方便记录。如果是 POST 请求,就要在车上贴上 POST 的标签,并把货物放在车厢里。当然,你也可以在 GET 的时候往车厢内偷偷藏点货物,但是这是很不光彩;也可以在 POST 的时候在车顶上也放一些数据,让人觉得傻乎乎的。HTTP 只是个行为准则,而 TCP 才是 GET 和 POST 怎么实现的基本。

但是,我们只看到 HTTP 对 GET 和 POST 参数的传送渠道(url 还是 requrest body)提出了要求。标准答案里关于参数大小的限制又是从哪来的呢?

在我大万维网世界中,还有另一个重要的角色:运输公司。不同的浏览器(发起 http 请求)和服务器(接受 http 请求)就是不同的运输公司。 虽然理论上,你可以在车顶上无限的堆货物(url 中无限加参数)。但是运输公司可不傻,装货和卸货也是有很大成本的,他们会限制单次运输量来控制风险,数据量太大对浏览器和服务器都是很大负担。业界不成文的规定是,(大多数)浏览器通常都会限制 url 长度在 2K 个字节,而(大多数)服务器最多处理 64K 大小的 url。超过的部分,恕不处理。如果你用 GET 服务,在request body 偷偷藏了数据,不同服务器的处理方式也是不同的,有些服务器会帮你卸货,读出数据,有些服务器直接忽略,所以,虽然 GET 可以带 request body,也不能保证一定能被接收到哦。

好了,现在你知道,GET 和 POST 本质上就是 TCP 链接,并无差别。但是由于 HTTP 的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。

下课!同学们先别着急,我快速地讲两句(乐)。

其实 GET 和 POST 还有一个重大区别,简单的说:GET产生一个TCP数据包;POST产生两个TCP数据包。

详细的说。对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据)。而对于POST,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。也就是说,GET 只需要汽车跑一趟就把货送到了,而 POST 得跑两趟,第一趟,先去和服务器打个招呼“嗨,我等下要送一批货来,你们打开门迎接我”,然后再回头把货送过去。因为 POST 需要两步,时间上消耗的要多一点,看起来 GET 比 POST 更有效。因此 Yahoo 团队有推荐用 GET 替换 POST 来优化网站性能。但这是一个坑!跳入需谨慎。为什么?

  1. GET 与 POST 都有自己的语义,不能随便混用。
  2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的 TCP 在验证数据包完整性上,有非常大的优点。
  3. 并不是所有浏览器都会在 POST 中发送两次包,Firefox 就只发送一次。

区别总结

  1. GET 请求会被浏览器主动缓存下来,留下历史记录,而 POST 默认不会;
  2. GET 请求只能进行 URL 编码,而 POST 支持多种编码方式;
  3. GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留;
  4. GET 请求在 URL 中传送的参数是有长度限制的(一般为 2k),而 POST 没有;
  5. 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制;
  6. GET 比 POST 更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息;
  7. GET 参数通过 URL 传递,POST 放在 Request body 中;
  8. GET 请求会把请求报文一次性发出去,而 POST 会分为两个 TCP 数据包,首先发 header 部分,如果服务器响应 100(continue), 然后发 body 部分。(火狐浏览器除外,它的 POST 请求只发一个 TCP 包)。

POST 请求常见四种参数

  1. application/x-www-form-urlencoded
  2. multipart/form-data
  3. application/json
  4. text/xml

request里面最重要的是Content-type :如果是application/json,那么request里面就把一个字符串化后的json 数据放在body里,这样服务器可以把它恢复成正确的数据结构,比如说{["ab","bc"]},到服务器端恢复出来就是String XX[],String数组。

Reuqest如果是直接发binary stream过来,服务器这端就用byte[]接收。

对于Response也一样,有字节流类型,有字符串类型(text类型),也有json转成字符串类型(另一种text类型)

HTTP/1.1 协议规定的 HTTP 请求方法有 options、get、head、post、put、delete、trace、connect这几种。其中 POST 一般用来向服务端提交数据,本文主要讨论 POST 提交数据的几种方式。

HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规范。规范把 HTTP 请求分为三个部分:状态行、请求头、消息主体。类似于下面这样:

<method> <request-URL> <version>
<headers>

<entity-body>

协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。实际上,开发者完全可以自己决定消息主体的格式,只要最后发送的 HTTP 请求满足上面的格式就可以。

但是,数据发送出去,还要服务端解析成功才有意义。一般服务端语言如 php、python 等,以及它们的 framework,都内置了自动解析常见数据格式的功能。服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。所以说到 POST 提交数据方案,包含了 Content-Type 和消息主体编码方式两部分。下面就正式开始介绍它们。


application/x-www-form-urlencoded

这应该是最常见的 POST 提交数据的方式了。浏览器的原生

表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。请求类似于下面这样(无关的请求头在本文中都省略掉了):

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

首先,Content-Type 被指定为 application/x-www-form-urlencoded;其次,提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。大部分服务端语言都对这种方式有很好的支持。例如 PHP 中,$_POST['title'] 可以获取到 title 的值,$_POST['sub'] 可以得到 sub 数组。


multipart/form-data

这种方式一般用来上传文件,各大服务端语言对它也有着良好的支持

这又是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,必须让 表单的 enctyped 等于 multipart/form-data。直接来看一个请求示例:

POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"

title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png

PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--

这个例子稍微复杂点。首先生成了一个 boundary 用于分割不同的字段,为了避免与正文内容重复,boundary 很长很复杂。

然后 Content-Type 里指明了数据是以 multipart/form-data 来编码,本次请求的 boundary 是什么内容。消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 --boundary 开始,紧接着是内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 --boundary-- 标示结束。关于 multipart/form-data 的详细定义,请前往 ==RFC1867== 查看。

上面提到的这两种 POST 数据的方式,都是浏览器原生支持的,而且现阶段标准中原生 <form> 表单也只支持这两种方式(通过 <form> 元素的 enctype 属性指定,默认为 application/x-www-form-urlencoded。其实 enctype 还支持 text/plain,不过用得非常少)。

随着越来越多的 Web 站点,尤其是 WebApp,全部使用 Ajax 进行数据交互之后,我们完全可以定义新的数据提交方式,给开发带来更多便利。


application/json

application/json 这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。

JSON 格式支持比键值对复杂得多的结构化数据,这一点也很有用。记得我几年前做一个项目时,需要提交的数据层次非常深,我就是把数据 JSON 序列化之后来提交的。不过当时我是把 JSON 字符串作为 val,仍然放在键值对里,以 x-www-form-urlencoded 方式提交。

Google 的 AngularJS 中的 Ajax 功能,默认就是提交 JSON 字符串。例如下面这段代码

var data = {'title':'test', 'sub' : [1,2,3]}; $http.post(url, data).success(function(result) { ... }); 

最终发送的请求是:

POST http://www.example.com HTTP/1.1 
Content-Type: application/json;charset=utf-8

{"title":"test","sub":[1,2,3]} 

这种方案,可以方便的提交复杂的结构化数据,特别适合 RESTful 的接口。各大抓包工具如 Chrome 自带的开发者工具、Firebug、Fiddler,都会以树形结构展示 JSON 数据,非常友好。


text/xml

我的博客之前提到过 XML-RPC(XML Remote Procedure Call)。它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范。典型的 XML-RPC 请求是这样的:

POST http://www.example.com HTTP/1.1 
Content-Type: text/xml

<?xml version="1.0"?>
<methodCall>
    <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params> </methodCall> 

XML-RPC 协议简单、功能够用,各种语言的实现都有。不过, XML 结构还是过于臃肿,一般场景用 JSON 会更灵活方便。


HTTP 报文头部字段

在介绍 HTTP 数据报文时已经对 HTTP 报文结构有了初步的说明。在 HTTP 请求方法小节中也看到了一些不同的 HTTP 数据包。这一小节来重点了解下 HTTP 报文中常见的一些头部字段。

头部字段:包含请求和响应的各种条件和属性的各类首部,一般有 4 种首部,分别是:通用首部请求首部响应首部,以及实体首部

HTTP 首部字段将定义成缓存代理和非缓存代理的行为,分为 2 种类型。(1)端到端首部 (End-to-end Header):分在此类别中的首部会转发给请求/响应对应的最终接受目标,且必须保存在由缓存生成的响应中,另外规定它必须被转发。(2)逐跳首部 (Hop-by-hop Header):分在此类别中的首部只对单次转发有效,会因通过缓存或代理而不再转发。HTTP/1.1 和之后版本中,如果要使用 hop-by-hop 首部,需提供Connection 首部字段。

逐跳首部一般为下面 8 个,其余的为端到端首部:Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、Trailer、TE、Transfer-Encoding、Upgrade


通用首部字段

无论是请求还是响应报文,都有的字段。以下这些头只是限制性的,声明性的作用,没有强制约束力。只是为代理服务器设置了这些头,要求按照规范去做,但是完全可以不按照这个规范做。

Cache-Control:HTTP 协议中关于缓存的响应头,它由一些能够允许你定义一个响应资源应该何时、如何被缓存以及缓存多长时间的指令组成。并且这个字段有一个特点,它在请求报文和响应报文中的取值以及含义是不一样的。

参考资料:图文讲解 Cache-Control 浅显易懂

请求值枚举:
 # no-cache - 不要缓存的实体,要求现在从WEB服务器去取
 # max-age - 只接受Age值小于max-age值,并且没有过期的对象
 # max-stale - 可以接受过去的对象,但是过期时间必须小于max-stale值
 # min-fresh - 接受其新鲜生命期大于其当前Age跟min-fresh值之和的缓存对象

响应值枚举:
 # public - 可以用Cached内容回应任何用户
 # private - 只能用缓存内容回应先前请求该内容的那个用户
 # no-cache - 可以缓存,但是只有在跟WEB服务器验证了其有效后,才能返回给客户端
 # max-age - 本响应包含的对象的过期时间
 # ALL: no-store - 不允许缓存

Connection:HTTP 的 Connection 头部字段核心作用是控制 TCP 连接的持久化(长连接 / 短连接)

场景 1:短连接(Connection: close) + 缓存控制(含请求/响应枚举值)

+--------------+                                                                           +--------------+
|    Client    |                                                                           |    Server    |
|              | (1) HTTP    | GET /index.html HTTP/1.0                                    |              |
|              |     Request | Connection: close                                           |              |
|              |             | Cache-Control: no-cache, max-age=0                          |              |
|              | ---------------------------------------------------------------------->>> |              |
|              |                      (2) HTTP     |  HTTP/1.0 200 OK                      |              |
|              |                          Response |  Connection: close                    |              |
|              |                                   |  Cache-Control: private, max-age=3600 |              |
|              |                                   |                                       |              |
|              |                                   |  [CONTENT Of /index.html]             |              |
|              | <<<---------------------------------------------------------------------- |              |
|              |                                 (3) Close      | FIN=1, ACK=1             |              |
|              |                                     TCP        | Sequence Number: X       |              |
|              |                                     Connection | Acknowledgment Number: Y |              |
|              | <<<---------------------------------------------------------------------- |              |
|              | (4) Confirm    | ACK=1, FIN=0                                             |              |
|              |     Closing    | Sequence Number: Y                                       |              |
|              |     Connection | Acknowledgment Number: X+1                               |              |
|              | ---------------------------------------------------------------------->>> |              |
|    Client    |                                                                           |    Server    |
+--------------+                                                                           +--------------+

场景 2:长连接(Connection: keep-alive) + 缓存控制(含请求/响应枚举值)

+--------------+                                                                           +--------------+
|    Client    |                                                                           |    Server    |
|              | (1) HTTP    | GET /index.html HTTP/1.1                                    |              |
|              |     Request | Connection: keep-alive                                      |              |
|              |             | Cache-Control: max-stale=300, min-fresh=60                  |              |
|              | ---------------------------------------------------------------------->>> |              |
|              |                           (2) HTTP     |  HTTP/1.1 200 OK                 |              |
|              |                               Response |  Connection: keep-alive          |              |
|              |                                        |  Keep-Alive: timeout=5, max=100  |              |
|              |                                        |  Cache-Control: public, no-store |              |
|              |                                        |                                  |              |
|              |                                        |  [CONTENT Of /index.html]        |              |
|              | <<<---------------------------------------------------------------------- |              |
|              |                                 (3) Close      | FIN=1, ACK=1             |              |
|              |                                     TCP        | Sequence Number: X       |              |
|              |                                     Connection | Acknowledgment Number: Y |              |
|              | <<<---------------------------------------------------------------------- |              |
|              | (4) HTTP    | GET /style.css HTTP/1.1                                     |              |
|              |     Request | Connection: keep-alive                                      |              |
|              |             | Cache-Control: no-cache                                     |              |
|              | ---------------------------------------------------------------------->>> |              |
|              |                                   (5) HTTP     |  HTTP/1.1 200 OK         |              |
|              |                                       Response |  Connection: keep-alive  |              |
|              |                                                |  Cache-Control: no-cache |              |
|              |                                                |                          |              |
|              |                                                |  [CONTENT Of /style.css] |              |
|              | <<<---------------------------------------------------------------------- |              |
|              |                                                                           |              |
|              | ------+                                                                   |              |
|              |       | (6) No New Request in 5 second (Keep-Alive: timeout=5)            |              |
|              | <<<---+                                                                   |              |
|              |                                                                           |              |
|              | (7) Close      | FIN=1, ACK=1                                             |              |
|              |     TCP        | Sequence Number: X                                       |              |
|              |     Connection | Acknowledgment Number: Y                                 |              |
|              | ---------------------------------------------------------------------->>> |              |
|              |                               (8) Confirm    | ACK=1, FIN=0               |              |
|              |                                   Closing    | Sequence Number: Y         |              |
|              |                                   Connection | Acknowledgment Number: X+1 |              |
|              | <<<---------------------------------------------------------------------- |              |
|    Client    |                                                                           |    Server    |
+--------------+                                                                           +--------------+
participant 客户端(浏览器)
participant 服务器(Web服务器)

客户端(浏览器)->>服务器(Web服务器): 1. GET /index.html HTTP/1.1
客户端(浏览器)->>服务器(Web服务器): 请求头:
客户端(浏览器)->>服务器(Web服务器): - Connection: keep-alive (长连接,复用连接)
客户端(浏览器)->>服务器(Web服务器): - Cache-Control: max-stale=300, min-fresh=60 (枚举值:max-stale+min-fresh)
服务器(Web服务器)->>客户端(浏览器): 2. 200 OK
服务器(Web服务器)->>客户端(浏览器): 响应头:
服务器(Web服务器)->>客户端(浏览器): - Connection: keep-alive (确认长连接)
服务器(Web服务器)->>客户端(浏览器): - Keep-Alive: timeout=5, max=100 (长连接配置:超时5秒,最多复用100次)
服务器(Web服务器)->>客户端(浏览器): - Cache-Control: public, no-store (枚举值:public+ALL: no-store)
服务器(Web服务器)->>客户端(浏览器): 3. 响应体(index.html内容)
服务器(Web服务器)->>客户端(浏览器): 4. 保持TCP连接不关闭(因Connection: keep-alive)

客户端(浏览器)->>服务器(Web服务器): 5. GET /style.css HTTP/1.1 (复用同一TCP连接)
客户端(浏览器)->>服务器(Web服务器): 请求头:
客户端(浏览器)->>服务器(Web服务器): - Connection: keep-alive (继续复用长连接)
客户端(浏览器)->>服务器(Web服务器): - Cache-Control: no-cache (枚举值:no-cache)
服务器(Web服务器)->>客户端(浏览器): 6. 200 OK
服务器(Web服务器)->>客户端(浏览器): 响应头:
服务器(Web服务器)->>客户端(浏览器): - Connection: keep-alive (维持长连接)
服务器(Web服务器)->>客户端(浏览器): - Cache-Control: no-cache (枚举值:no-cache)
服务器(Web服务器)->>客户端(浏览器): 7. 响应体(style.css内容)

客户端(浏览器)->>服务器(Web服务器): 8. 无新请求,超时5秒(Keep-Alive配置)
客户端(浏览器)->>服务器(Web服务器): 9. 主动关闭TCP连接
服务器(Web服务器)->>客户端(浏览器): 10. 确认关闭连接
请求值枚举:
 # close - 告诉WEB服务器或者代理服务器,在完成本次请求的响应后,断开连接,不要等待本次连接的后续请求了 
 # keepalive - 告诉WEB服务器或者代理服务器,在完成本次请求的响应后,保持连接,等待本次连接的后续请求

响应值枚举:
 # close - 连接已经关闭。 
 # keepalive - 连接保持着,在等待本次连接的后续请求。 
 # Keep-Alive - 如果浏览器请求保持连接,则该头部表明希望WEB服务器保持连接多长时间(秒)
   e.g.: Keep-Alive:300

Pramga:主要使用 Pramga: no-cache,相当于 Cache-Control: no-cache

Pragma:no-cache

Transfer-Encoding:WEB 服务器表明自己对本响应消息体(不是消息体里面的对象)作了怎样的编码,比如是否分块(chunked)。

Transfer-Encoding: chunked

Via:列出从客户端到 OCS 或者相反方向的响应经过了哪些代理服务器,他们用什么协议(和版本)发送的请求。

  • 当客户端请求到达第一个代理服务器时,该服务器会在自己发出的请求里面添加 Via 头部,并填上自己的相关信息;
  • 当下一个代理服务器收到第一个代理服务器的请求时,会在自己发出的请求里面复制前一个代理服务器的请求的Via 头部,并把自己的相关信息加到后面;
  • 以此类推,当 OCS 收到最后一个代理服务器的请求时,检查 Via 头部,就知道该请求所经过的路由。
Via:1.0 236.D0707195.sina.com.cn:80 (squid/2.6.STABLE13)

2.2 请求首部字段


HTTP 状态码

状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。注意,状态码是服务端向客户端发送的反映响应结果的标识码。共有 5 种类型:

已定义范围 类别 原因短语 整体范围
100~101 Informational(信息提示性) 接收的请求正在处理 100~199
200~206 Success(请求成功) 请求正常处理完毕 200~299
300~305 Redirection(重定向) 需要进行附加操作以完成请求 300~399
400~415 Client Error(客户端错误) 服务器无法处理请求 400~499
500~505 Server Error(服务器错误) 服务器处理请求出错 500~599

1XX 信息性状态码

这些状态代码表示临时的响应。客户端在收到常规响应之前,应准备接收一个或多个 1xx 响应。

  • 100 | Continue:**继续。**客户端想向服务器发送实体,但不确定服务器能不能接受,所以首先会向服务器发送一个携带了 100 continueExcept,服务器受到这个请求之后如果能接收客户端发来的实体,那就返回一个 100 Continue 响应,如果不能就返回一个错误码。
  • 101 | Switching Protocols:**切换协议。**服务器正在根据客户端的指定,将协议切换成 Update 首部所列的协议。

2XX 成功

这类状态代码表明服务器成功地接受了客户端请求。不同的代码分别对应于不同类型的请求。

  • 200 | OK确定。客户端请求已成功。从客户端发来的请求在服务器端被正常处理了,实体的主体部分包含了所请求的资源。
  • 201 | Created已创建。用于创建服务器对象的请求(比如:PUT),响应的实体主体部分中应该包含各种引用了已经创建好的资源的URL,Location首部包含的则是具体的引用。
  • 202 | Accepted已接受。请求已经被接收,但服务器还没有执行任何操作。并不意味着服务器会完成这个请求。
  • 203 | Non-Authoritative-Information非权威性信息。实体首部包含的信息不是来自于源端服务器,而是来自资源的一份副本。
  • 204 | No Content无内容。服务器成功处理了请求,但没有返回任何内容。主要用于在浏览器不转为显示新文档的情况下,对其进行更新(比如刷新表单页面)。
  • 205 | Reset Content重置内容。用于浏览器的代码,告诉浏览器清空当前页面中所有HTML表单元素。
  • 206 | Partial Content部分内容。成功执行了一个部分或者Range请求,因为客户端可以通过一些特殊的首部来获取部分或者范围内的文档。响应报文中包含由 Content-Range 指定范围的实体内容。

3XX 重定向

客户端浏览器必须采取更多操作来实现请求。例如,浏览器可能不得不请求服务器上的不同的页面,或通过代理服务器重复该请求。重定向状态码要么告诉客户端使用代替位置来访问他们所感兴趣的资源,要么就提供一个替代的响应而不是资源的内容。如果资源已被移动,可以发送一个重定向状态码和一个可选的 Location 首部来告知客户端资源已被移走。以及现在可以在那里找到它。这样浏览器就可以自己转向新的位置了。

  • 300 | Multiple Choise:客户端请求一个世纪指向多个资源的 URL 时会返回这个状态码,比如服务器上有某个 HTML 文档的英语和发育版本,返回这个状态码时会有一个选项列表,这样客户端就可以选择了。
  • 301 | Moved Permanently对象已永久移走。永久性重定向。该状态码表示请求的资源已被分配了新的 URI (该 URL 存在 Location 首部中),以后应使用资源现在所指的 URI。
  • 302 | Found对象已临时移动。临时性重定向。该状态码表示请求的资源已被分配了新的 URI (该 URL 存在 Location 首部中),希望用户(本次)能使用新的 URL 访问,将来的请求还应使用老的 URL。注意:刚开始客户端发送 POST 请求,在收到 302 状态码后,使用GET请求访问新给的URL。在 HTTP1.0 生效。
  • 303 | See Other:告知客户端应该用另一个 URL (该URL存在Location首部中)来获取资源,其主要目的是允许 POST 请求的响应将客户端定向到某个资源上去。在HTTP1.1生效。
  • 304 | Not Modified未修改。此状态码适用于客户端发送了一个有条件的请求( If-Match,If-ModifiedSince,If-None-Match,If-Range,If-Unmodified-Since )。比如客户端想获取某个资源,并且是在XXX时间修改过的新的资源,如果这个资源没有修改,服务端就返回304给客户端。
  • 305 | Use Proxy:用来告诉客户端必须通过一个代理来访问资源,代理的位置在 Location 里。
  • 306这个状态码还没使用
  • 307 | Temporary Redirect临时重定向。临时重定向。该状态码与 302 Found 有着相同的含义。307 会遵照浏览器标准,不会从 POST 变成 GET。

4XX 客户端错误

发生错误,客户端似乎有问题。例如,客户端请求不存在的页面,客户端未提供有效的身份验证信息。4XX 的响应结果表明客户端是发生错误的原因所在。但很多 4xx 错误都被浏览器解决了,所以用户经常看到的也就是 404 了。

  • 400 | Bad Request错误的请求。该状态码表示请求报文中存在语法错误。
  • 401 | Unauthorized访问被拒绝。告诉客户端,要想获取资源的访问权,首先要对自己认证。
    IIS定义了许多不同的401错误,它们指明更为具体的错误原因。这些具体的错误代码在浏览器中显示,但不在IIS日志中显示:
     # 401.1-登录失败。
     # 401.2-服务器配置导致登录失败。
     # 401.3-由于ACL对资源的限制而未获得授权。
     # 401.4-筛选器授权失败。
     # 401.5-ISAPI/CGI应用程序授权失败。
     # 401.7–访问被Web服务器上的URL授权策略拒绝。这个错误代码为IIS6.0所专用。 
    
  • 402 | Payment Required:此状态码还未被使用,保留中。
  • 403 | Forbidden禁止访问。表明服务器拒绝了这个来自客户端的请求。一般不会说明缘由。
    IIS定义了许多不同的403错误,它们指明更为具体的错误原因:
     # 403.1-执行访问被禁止。
     # 403.2-读访问被禁止。
     # 403.3-写访问被禁止。
     # 403.4-要求SSL。
     # 403.5-要求SSL128。
     # 403.6-IP地址被拒绝。
     # 403.7-要求客户端证书。
     # 403.8-站点访问被拒绝。
     # 403.9-用户数过多。
     # 403.10-配置无效。
     # 403.11-密码更改。
     # 403.12-拒绝访问映射表。
     # 403.13-客户端证书被吊销。
     # 403.14-拒绝目录列表。
     # 403.15-超出客户端访问许可。
     # 403.16-客户端证书不受信任或无效。
     # 403.17-客户端证书已过期或尚未生效。
     # 403.18-在当前的应用程序池中不能执行所请求的URL。这个错误代码为IIS6.0所专用。
     # 403.19-不能为这个应用程序池中的客户端执行CGI。这个错误代码为IIS6.0所专用。
     # 403.20-Passport登录失败。这个错误代码为IIS6.0所专用。
    
  • 404 | Not Found文件或资源未找到。表明服务器上无法找到请求的资源。一般还会包含一个实体(比如 404 页面),以便客户端给用户看。
     # 404.0-(无)–没有找到文件或目录。
     # 404.1-无法在所请求的端口上访问Web站点。
     # 404.2-Web服务扩展锁定策略阻止本请求。
     # 404.3-MIME映射策略阻止本请求。
    
  • 405 | Method Not Allowed用来访问本页面的HTTP谓词不被允许(方法不被允许)。客户端发起的请求中带有所有请求的URL不支持的方法。同时应该在响应中包含Allow首部,以告诉客户端可以使用什么方法。
  • 406 | Not Accepted客户端浏览器不接受所请求页面的 MIME 类型。客户端可以在请求首部中指明自己愿意接收什么类型的实体,但是当服务器没有这种类型实体的时候,会发送406。
  • 407 | proxy Authentication Required要求进行代理身份验证。与401类似,但是用于要求对资源进行认证的代理服务器。
  • 408 | Request TImeout:如果客户端完成请求所话的时间太长,服务器返回此代码并关闭连接。
  • 409 | Conflict:用于说明请求可能在资源上引发一些冲突。服务器担心请求会引发冲突时,发送此代码。并在响应的主体中描述冲突。
  • 410 | Gone:与404类似,只是服务器曾经拥有过此资源。
  • 411 | Length Required:服务器要求客户端发请求的时候包含Content-Length首部的时候发送此代码。
  • 412 | Precondition Failed前提条件失败。客户端发起了条件请求,且其中一个条件失败了的时候会收到此状态码。
  • 413 | Request Entity Too large请求实体太大。客户端发送的实体主体比服务器所能希望处理的要大时,使用此代码。
  • 414 | Request URL Too Long请求URI太长。客户端发送的请求URL比服务器所能希望处理的要长时,使用此代码。
  • 415 | Unsupported Media Type不支持的媒体类型。服务器无法理解或无法支持客户端所发实体内容类型时,使用此状态码。
  • 416 | Request Range Not Satisfiable所请求的范围无法满足。请求报文所请求的是指定资源的某个范围,而此范围无效或者无法满足时,使用此状态码
  • 417 | Expectation Failed执行失败。请求的Expect请求首部包含了一个期望,但是服务器无法满足此期望时,使用此状态码。

5XX 服务器错误

服务器由于遇到错误而不能完成该请求。

  • 500 | Internal Server Error内部服务器错误。该状态码表明服务器端在执行请求时遇到了一个妨碍它为请求提供服务的错误,也有可能是 Web 应用存在的 bug 或某些临时的故障。

     # 500.12-应用程序正忙于在Web服务器上重新启动。
     # 500.13-Web服务器太忙。
     # 500.15-不允许直接请求Global.asa。
     # 500.16–UNC授权凭据不正确。这个错误代码为IIS6.0所专用。
     # 500.18–URL授权存储不能打开。这个错误代码为IIS6.0所专用。
     # 500.100-内部ASP错误。
    
  • 501 | Not Implemented页眉值指定了未实现的配置。客户端发起的请求超出服务器的能力范围(比如使用了服务器不支持的请求方法)

  • 502 | Bad GatewayWeb 服务器用作网关或代理服务器时收到了无效响应。作为代理或网关使用的服务器从请求响应链的下一条链路上受到了一条伪响应(比如,它无法连接到其他父网关)时,使用此码。

    # 502.1-CGI应用程序超时。
    # 502.2-CGI应用程序出错。application. 
    
  • 503 | Service Unavailable服务不可用。这个错误代码为 IIS6.0 所专用。该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求,但是将来可以。如果服务器知道什么时候能回复,可以在响应首部中添加 Retry-After。

  • 504 | Gateway Timeout网关超时。与408类似,只是这里的响应来自一个网关或者代理,他们等待另一个服务器对齐请求进行响应超时了。

  • 505 | HTTP Version Not SupportedHTTP版本不受支持。服务器收的请求使用了它无法或者不愿支持的协议版本时,使用此状态码。


拓展:状态码301和302的区别

301,302对用户来说没有区别,他们看到效果只是一个跳转,浏览器中旧的 URL 变成了新的 URL。页面跳到了这个新的 URL 指向的地方。对于引擎及站长 302 转向可能会有 URL 规范化及网址劫持的问题。可能被搜索引擎判为可疑转向,甚至认为是作弊。

详细来说,301 和 302 状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的 URL 地址,这个地址可以从响应的 Location 首部中获取(用户看到的效果就是他输入的地址 A 瞬间变成了另一个地址 B )——这是它们的共同点。他们的不同在于。301 表示旧地址 A 的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302 表示旧地址 A 的资源还在(仍然可以访问),这个重定向只是临时地从旧地址 A 跳转到地址 B,搜索引擎会抓取新的内容而保存旧的网址。【推荐阅读:HTTP 301 状态码

注意:许多 HTTP/1.1 版以前的浏览器不能正确理解 303 状态。如果需要考虑与这些浏览器之间的互动,302 状态码应该可以胜任,因为大多数的浏览器处理 302 响应时的方式恰恰就是上述规范要求客户端处理 303 响应时应当做的。【HTTP 302 状态码


User-Agent(用户代理)是什么

User-Agent 即用户代理,简称 UA,它是一个特殊字符串头。网站服务器通过识别 UA 来确定用户所使用的操作系统版本、CPU 类型、浏览器版本等信息。而网站服务器则通过判断 UA 来给客户端发送不同的页面。

UA能干什么呢?举个例子,网络爬虫使用程序代码来访问网站,而非人类亲自点击访问,因此爬虫程序也被称为网络机器人。绝大多数网站都具备一定的反爬能力,禁止网爬虫大量地访问网站,以免给网站服务器带来压力。而对 User-Agent 的识别就是反爬策略的第一步。网站通过识别请求头中 User-Agent 信息来判断是否是爬虫访问网站。如果是,网站首先对该 IP 进行预警,对其进行重点监控,当发现该 IP 超过规定时间内的访问次数, 将在一段时间内禁止其再次访问网站。

常见的 User-Agent 请求头,如下所示:

系统 浏览器 User-Agent字符串
Mac Chrome Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36
Mac Firefox Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:65.0) Gecko/20100101 Firefox/65.0
Mac Safari Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.3 Safari/605.1.15
Windows Edge Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/18.17763
Windows IE Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Windows Chrome Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
iOS Chrome Mozilla/5.0 (iPhone; CPU iPhone OS 7_0_4 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) CriOS/31.0.1650.18 Mobile/11B554a Safari/8536.25
iOS Safari Mozilla/5.0 (iPhone; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Version/8.0 Mobile/12F70 Safari/600.1.4
Android Chrome Mozilla/5.0 (Linux; Android 4.2.1; M040 Build/JOP40D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Mobile Safari/537.36
Android Webkit Mozilla/5.0 (Linux; U; Android 4.4.4; zh-cn; M351 Build/KTU84P) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30

使用上表中的浏览器 UA,我们可以很方便的构建出 User-Agent。通过在线识别工具,可以查看本机的浏览器版本以及 UA 信息。

UA 头的格式为:浏览器标识 (操作系统标识; 加密等级标识; 浏览器语言) 渲染引擎标识 版本信息,通常可以简化为:Mozilla/5.0 (平台) 引擎版本 浏览器版本号

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36

以上面的 UA 头为例,解析出来的信息如下:

浏览器名称 Chrome
浏览器标识 Mozilla/5.0
操作系统标识 Windows NT 10.0; Win64; x64
加密等级标识 -
浏览器语言 -
渲染引擎标识 AppleWebKit/537.36 (KHTML, like Gecko)
版本信息 Chrome/88.0.4324.182 Safari/537.36

UA 头格式详解

第1部分: Mozilla

Mozilla/5.0

由于历史上的浏览器大战,当时想获得图文并茂的网页,就必须宣称自己是 Mozilla 浏览器。此事导致如今 User-Agent 里通常都带有 Mozilla 字样,出于对历史的尊重,大家都会默认填写该部分。例如:Mozilla/5.0 表示是 5.0 版本

第2部分: 平台版本

格式为:操作系统标识; 加密等级标识; 浏览器语言

(Windows NT 10.0; Win64; x64)

(1) 操作系统标识显示用户的操作系统信息。例如 Windows NT 10.0; Win64; x64 表示使用的系统版本是 64 位的 win10,如果是 32 位的 win7,则对应 Windows NT 6.1; WOW64。不同操作系统/版本对应的标识如下:

系统类型 标识内容 系统架构 标识类型 说明
FreeBSD X11; FreeBSD (version no.) i386 i386(32位) 版本+架构 FreeBSD桌面环境下的32位系统标识
FreeBSD X11; FreeBSD (version no.) AMD64 AMD64(64位) 版本+架构 FreeBSD桌面环境下的64位系统标识
Windows Windows 98 i386(32位) 版本 早期Windows 32位系统
Windows Windows ME i386(32位) 版本 Windows 9x系列最后一个版本
Windows Windows NT 5.0 i386/x86_64 版本 对应Windows 2000
Windows Windows NT 5.1 i386/x86_64 版本 对应Windows XP
Windows Windows NT 5.2 i386/x86_64 版本 对应Windows 2003
Windows Windows NT 6.0 i386/x86_64 版本 对应Windows Vista
Windows Windows NT 6.1 i386/x86_64 版本 对应Windows 7
Windows Windows NT 6.2 i386/x86_64 版本 对应Windows 8
Windows Windows NT 6.3 i386/x86_64 版本 对应Windows 8.1
Windows Windows NT 10.0 x86_64(主流) 版本 对应Windows 10/11
Windows Win64; x64 x86_64 架构 原生64位Windows系统
Windows WOW64 x86_64(运行32位程序) 架构 64位Windows下兼容32位程序的模式
Linux X11; Linux ppc; PowerPC(32位) 架构 Linux桌面32位PowerPC架构
Linux X11; Linux ppc64; PowerPC(64位) 架构 Linux桌面64位PowerPC架构
Linux X11; Linux i686; i686(32位x86) 架构 Linux桌面32位x86架构
Linux X11; Linux x86_64; x86_64(64位) 架构 Linux桌面64位x86架构
Linux X11; Linux i686 on x86_64 x86_64(运行32位程序) 架构 64位Linux下运行32位i686程序
macOS Macintosh; Intel Mac OS X 10_9_0 x86/x86_64 版本+架构 Intel芯片macOS(版本10.9),下划线分隔版本号
macOS Macintosh; PPC Mac OS X 10_9_0 PowerPC 版本+架构 PowerPC芯片macOS(版本10.9)
macOS Macintosh; Intel Mac OS X 10.12; x86_64 版本+架构 Intel芯片macOS(版本10.12),点分隔版本号
Solaris X11; SunOS i86pc i386/x86_64 架构 Solaris系统x86架构
Solaris X11; SunOS sun4u SPARC 架构 Solaris系统SPARC架构

(2) 加密等级标识。浏览器共有 N/I/U 三种加密等级

浏览器的 N/I/U 是 SSL/TLS 加密等级标识,对应无加密低强度加密高强度加密,常见于 IE6-IE11、旧版 Opera 等早期浏览器的配置或 UA 字符串中。现代浏览器(Chrome、Edge、Firefox 等)已取消该明确标识,默认支持高强度加密(对应 U 级),且自动禁用低强度加密和无加密模式。

标识 英文全称 中文含义 核心特点
N None 无加密 不支持SSL/TLS加密,数据传输以明文形式进行,无安全防护
I Intermediate 中/低强度加密 支持早期低版本SSL(如SSL 2.0/3.0)或弱加密套件(如40位、56位密钥),安全性较低,易被破解
U Ultra 高强度加密 支持TLS 1.0+协议或强加密套件(如128位、256位密钥),符合现代网络安全标准,数据传输安全性高

(3) 浏览器语言标识。核心作用是告诉服务器浏览器的默认语言偏好。

服务器根据该标识返回对应语言的网页内容(如中文用户看到中文界面,英文用户看到英文界面)。浏览器语言标识还会影响资源加载(如语言相关的脚本、样式、字体文件)。

表示单语言时直接用语言代码(如 en 英文、zh 中文、ja 日文)。对单语言进行地区细分时使用语言代码+地区代码的格式(如 zh-CN 简体中文-中国大陆、zh-TW 繁体中文-台湾、en-US 英文-美国)。也可以带上优先级为不同的语言标识赋予权重值,用分号分隔,q 值表示优先级,取值区间为 (0-1] (默认1),如 zh-CN;q=0.9,en;q=0.8(优先简体中文,其次英文)。

典型示例解析:

UA中语言字段 含义 应用场景
en 默认英文(无地区限定) 国际版浏览器,未指定具体地区
zh-CN 优先简体中文(中国地区) 国内版浏览器,默认中文界面+中文内容请求
en-US;q=1,zh-CN;q=0.7 首选英文(美国),次选简体中文 海外用户,需兼顾中文内容
U; en 通用模式+英文(早期Opera浏览器格式) 旧版Presto内核Opera,如之前解析的UA案例

常见语言代码对照表:

语言代码 语言(地区) 常见使用场景
en 英文(通用) 国际版浏览器默认
en-US 英文(美国) 北美地区浏览器
en-GB 英文(英国) 欧洲英语地区
zh 中文(通用) 未细分简繁的中文环境
zh-CN 简体中文(中国) 大陆地区主流浏览器
zh-TW 繁体中文(台湾) 台湾地区浏览器
zh-HK 繁体中文(香港) 香港地区浏览器
ja 日文 日本地区浏览器
ko 韩文 韩国地区浏览器
fr 法文 法国及法语地区

第3部分:渲染引擎版本

格式为:渲染引擎/引擎版本号。主流的浏览器渲染引擎有:Gecko、WebKit、KHTML、Presto、Trident、Tasman 等。

AppleWebKit/537.36 (KHTML, like Gecko)

表示使用 AppleWebKit 渲染引擎的 537.36 版本。历史上,苹果依靠了 WebKit 内核开发出 Safari 浏览器,WebKit 包含了 WebCore 引擎,而 WebCore 又从 KHTML 衍生而来。由于历史原因,KHTML 引擎需要声明自己是类似Gecko的,因此引擎部分这么写。

再后来,Google 开发 Chrome 也是用了 WebKit 内核,于是也跟着这么写。借用Littern的一句话:“Chrome 希望能得到为Safari编写的网页,于是决定装成Safari,Safari使用了WebKit渲染引擎,而WebKit呢又伪装自己是KHTML,KHTML呢又是伪装成Gecko的。同时所有的浏览器又都宣称自己是Mozilla。"。不过,后来Chrome 28某个版本改用了blink内核,但还是保留了这些字符串。而且,最近的几十个版本中,这部分已经固定,没再变过。

渲染引擎具有明显的沿袭、替代关系:

KHTML(2001) —> WebKit(2005, Apple) —> Blink(2013, Google/Opera) Trident(1997, Microsoft IE) —> EdgeHTML(2015, Microsoft Edge) —> 被 Blink 替代(2019) Presto(2003, Opera) —> 被 Blink 替代(2013)

除了分支为 Blink,WebKit 也是 Safari、早期 Chrome、Android 浏览器的内核基础。浏览器内核发展历程深刻的影响了 UA 这个字段。这部分内容会单开一个文档来详细说明。

浏览器内核是包含渲染引擎、JavaScript 引擎、网络模块、存储模块等多个核心组件。渲染引擎是内核中负责页面显示的核心模块,没有渲染引擎,浏览器无法呈现网页的结构和样式。不同浏览器内核的核心差异,很大程度上体现在渲染引擎的实现上(如解析规则、渲染性能)。

主流浏览器中多数内核与渲染引擎为同一概念,内核核心与渲染引擎一一对应,所以可以认为内核即渲染引擎。部分早期或定制化浏览器会有特殊对应关系。

内核 浏览器 出生年份 JS 引擎 开源 核心特点
Trident IE4-IE11 1997 JScript
9+Chakra
支持 ActiveX 控件拓展功能;对现代 Web 标准支持不足,兼容性差;单进程架构,渲染速度慢;曾因市场占比高成为黑客攻击重点目标,安全性较弱;适配早期 Windows 平台旧网页
Gecko Firefox 2004 SpiderMonkey MPL 跨平台性强,采用多进程架构,主进程与内容进程分离;通过 IonMonkey JIT 编译技术加速 JS 执行,支持硬件加速渲染;对 Web 标准支持积极,迭代紧跟标准更新;开源且可定制性高,支持丰富扩展插件,注重用户隐私保护
WebKit Safari
Chromium(早期)
Android浏览器
Chrome(-2013)
2005 WebCore
JavaScriptCore
BSD 基于 KHTML 引擎重构,渲染速度快,有高效缓存机制和预测渲染技术;支持多进程隔离的 WebKit2 架构,提升安全性;适配苹果全生态,iOS 端第三方浏览器需基于其开发;扩展能力强,提供丰富 API 支持插件拓展
Blink Chrome
Opera
Edge(2019+)
2013 V8 GPL 源于 WebKit 分支,采用多进程架构,进程隔离+沙箱技术保障安全;搭配 V8 引擎提升 JS 执行效率,集成 Skia 图形库优化绘制;优化资源加载减少功耗和流量;快速迭代适配现代 Web 标准,支持 HTML5、CSS3 等富媒体特性
Edge Edge(2015-2019版本) 2015 EdgeHTML
Chakra
MIT(chakra) 由 Trident 演变而来,采用多进程架构分离渲染、网络等任务;对 HTML5、CSS3 等现代标准支持较好;Chakra 引擎保障 JS 高效执行;后因生态兼容问题被 Blink 内核替代
Presto Opera(-12.17) 2003 Carakan 曾以渲染速度快著称,采用间歇式渲染技术,无需等整页下载即可渲染;兼容性好,支持多种 Web 应用常见功能;可定制性强,能灵活增减浏览器特性;因研发维护成本过高被 Opera 放弃,后被 Blink 替代

部分国产浏览器使用双核技术,面对不同的场景切换不同的浏览器内核。通常使用 Trident 和 Blink 内核的组合。

浏览器 浏览器内核 渲染引擎 核心特点
360 安全浏览器 双核(Trident+Blink) Trident/Blink 兼容模式用 Trident 适配旧网页,极速模式用 Blink 保障现代网页渲染速度,适配国内多样网页场景
QQ 浏览器 双核(Trident+Blink) Trident/Blink 普通模式依赖 Trident 保障旧网站兼容,极速模式用 Blink 提升加载和渲染性能,兼顾日常浏览和特殊场景需求
搜狗高速浏览器 双核(Trident+Blink) Trident/Blink 早期为 Trident 单内核,后续加入 Blink 内核,可自动切换模式,平衡旧网页兼容性和新网页渲染效率

第4部分:浏览器版本信息

显示浏览器真实版本信息,格式为: Version/版本号例如:Chrome/88.0.4324.182表示该浏览器是 Chrome,并且包含了版本号信息 (88.0.4324.182,其中 88.0 是大版本,4324 是持续增大的一个数字,而 182 则是修补漏洞的小版本。)。

我们再来看一个示例:Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11。各部分拆解如下:

字段 内容 含义
Opera/9.80 浏览器核心版本 Opera浏览器的核心基础版本为9.80(早期Opera版本号命名规则,非最终对外版本)
Windows NT 6.1 操作系统版本 对应Windows 7系统(此前表格中已明确:Windows NT 6.1 = Windows 7)
U 语言设置标识 代表Universal(通用),表示浏览器默认语言为英文(后续en进一步确认)
en 具体语言 英文(English),说明浏览器界面或默认访问语言为英文
Presto/2.8.131 渲染引擎版本 浏览器使用Presto内核,引擎版本为2.8.131(Presto是Opera早期自研内核,已废弃)
Version/11.11 浏览器对外版本 用户可见的Opera最终版本号为11.11(核心版本9.80与对外版本11.11为早期命名差异)

UA 头的作用

根据前面介绍的 User-Agent 的历史我们知道,通过 User-Agent 不能完全准确的判断是属于那款浏览器。由于 UA 字符串在每次浏览器 HTTP 请求时发送到服务器,所以服务器就可以根据它来做好多事。比如:

  1. 统计用户浏览器使用情况。有些浏览器说被多少人使用了,实际上就可以通过判断每个 IP 的 UA 来确定这个 IP 是用什么浏览器访问的,以得到使用量的数据。
  2. 根据用户使用浏览器的不同,显示不同的排版从而为用户提供更好的体验。有些网站会根据这个来调整打开网站的类型,如是手机的就打开 WAP,显示非手机的就打开 PC 常规页面。用手机访问谷歌和电脑访问是不一样的,这些是谷歌根据访问者的 UA 来判断的。

既然知道了 UA 的作用,那么其实客户端也可以使用 UA 来做一些神奇的事。比如:伪装 User-Agent 来回避某些侦测特定浏览器才能读取的网站。如果使用 Firefox 浏览器插件 User agent switcher,用户就可以轻松地在不同 UA 之间切换,把自己伪装成其他浏览器。这样就可以在 PC 上预览 WAP 或移动格式的网页,比如专门为 iPhone 设计的页面


不同编程语言获得 UA 值的方式

既然已经知道 User-Agent 是 HTTP 的头域,那我们在编程的时候就可以获得它。

ASP.NET 中使用 Request.Header["User-Agent"] 得到浏览器的 User Agent,也可以使用 Request.UserAgent 来获取。Java 中使用 request.getHeader("User-Agent") 来获得;

PHP 中相应使用:$_SERVER[HTTP_USER_AGENT];。JS 中则使用 navigator.userAgent 来获得(是否记得在客户端经常使用它来做浏览器兼容呢)。


UA 头伪造(python)

有时候我们需要在脚本中伪造UA,这里提供两种方式来进行伪造:

方法一:python库:fake-useragent

注意:需要梯子,否则不能从 Server 获取到 UA json 信息。

首先安装 fake-useragent 库:

pip install fake-useragent

验证安装结果:

from fake_useragent import UserAgent
ua = UserAgent()

ua.ie  # 调用指定ua:
Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)

ua.random  # 随机ua:

编写 python 脚本:

from fake_useragent import UserAgent
# ua = UserAgent(use_cache_server=False)
ua = UserAgent()
print(ua.chrome)
print(ua.edge)
print(ua.random)

''' 输出结果:
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1623.0 Safari/537.36
Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36 
'''

方法二:简单自定义脚本

通过自定义脚本,抽取不同的字段随机组合为 UA

import random

# 定义get_ua()函数
def get_ua():
    first_num = random.randint(55, 62)
    third_num = random.randint(0, 3200)
    fourth_num = random.randint(0, 140)
    os_type = [
        '(Windows NT 6.1; WOW64)', '(Windows NT 10.0; WOW64)', '(X11; Linux x86_64)', '(Macintosh; Intel Mac OS X 10_12_6)'
    ]
    chrome_version = f'Chrome/{first_num}.0.{third_num}.{fourth_num}'
    ua = f"Mozilla/5.0 {random.choice(os_type)} AppleWebKit/537.36 (KHTML, like Gecko) {chrome_version} Safari/537.36"
    return ua

print(get_ua()) # 调用get_ua()函数

''' 输出结果:
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.1614.34 Safari/537.36
'''

参考文档


附录A:常见 PC 端 UA 枚举

类型 枚举
Opera Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60
Opera Opera/8.0 (Windows NT 5.1; U; en)
Opera Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50
Opera Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50
Firefox Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0
Firefox Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10
Safari Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2
Chrome Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36+
Chrome Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11
Chrome Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16
360 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36
360 Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
淘宝浏览器 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11
猎豹浏览器 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER
猎豹浏览器 Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)
猎豹浏览器 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)”
QQ浏览器 Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)
QQ浏览器 Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)
Sogou浏览器 Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0
Sogou浏览器 Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)
Maxthon浏览器 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.3.4000 Chrome/30.0.1599.101 Safari/537.36
UC浏览器 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36

附录B:常见移动端 UA 枚举

类型 枚举
IPhone Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
IPod Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
IPAD Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5
IPAD Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5
Android Mozilla/5.0 (Linux; U; Android 2.2.1; zh-cn; HTC_Wildfire_A3333 Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Android Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
QQ 浏览器 Android版本 MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
Android Opera Mobile Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10
Android Pad Moto Xoom Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13
BlackBerry Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.337 Mobile Safari/534.1+
WebOS HP Touchpad Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0
Nokia N97 Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124
Windows Phone Mango Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)
UC浏览器 UCWEB7.0.2.37/28/999
UC浏览器 NOKIA5700/ UCWEB7.0.2.37/28/999
UCOpenwave Openwave/ UCWEB7.0.2.37/28/999
UC Opera Mozilla/4.0 (compatible; MSIE 6.0; ) Opera/UCWEB7.0.2.37/28/999