我真的是在瞎逼逼,由于光是 HTTP,就足够讲一块砖头了。并且,HTTP 只是协议栈的应用层的其中一个协议:),不过其余协议都不在本文讨论范围以内。若有疏漏,请指正。html
不少缩写词被用于描述客户端与服务器端交流的过程。这些词常常被不熟悉内部原理的人混淆。html5
HTTP (Hypertext Transfer Protocol) 是客户端和服务器端都必须实现的基本交流协议。它涉及到请求 (requests),响应 (responses),会话 (sessions),缓存 (caching),认证 ( authentication) 以及更多。在这个协议以及 HTML (Hypertext Markup Language) 上的工做,开始于 1989 年,由在 CERN 的 Sir Tim Berners-Lee 和他的团队主持。这个协议的第一个官方版本 (HTTP 1.0) 发布于 1996 年,随后在 1997 年发布了如今普遍使用的版本 (HTTP 1.1)。webpack
这个协议在浏览器和服务器间传输明文 (clear) 信息,容许经过传输信息的网络查看传输的信息。这会产生安全的担心,因此 HTTP Secure (HTTPS) 被引入了。HTTPS 容许客户端和服务器端先创建一个加密的通道,而后经过这个通道传输明文信息,有效地防止了信息的窃听。git
加密通道是经过 Transport Layer Security (TLS) 协议建立的,这个协议之前叫 Secure Socket Layer (SSL)。这两个术语常常互换着使用。SSL 3.0 正在被 TLS 1.0 替代。SSL 是 Netscape 开发的协议,而 TLS 是一个 IETF 标准。在写这篇文章的时候,全部版本的 SSL (1.0, 2.0, 3.0) 都因为许多的安全问题而被废弃,使用它们将会在现代浏览器中产生警告,TLS 版本 (1.0, 1.1, 1.2) 正在使用中,1.3 版本如今仍是草案。github
因此,在大约 1996 或 1997 的某个时间,咱们有了互联网的当前稳定版本 (有或者没有 SSL/TLS 的 HTTP 1.1),这个 HTTP 版本仍然驱动着如今绝大多数网站。之前,HTTP 用于非敏感通讯,好比阅读新闻,而 HTTPS 用于敏感通讯,好比认证和电子商务。然而,对隐私权的关注不断增长,一些浏览器如 Google Chrome 如今会标记 HTTP 网站 为 “not private”,将在将来针对 HTTP 引入警告。web
HTTP 协议的下一代升级版 —— HTTP/2,于 2015 年发布,正在被愈来愈多的网站所采纳。这个协议增长了新的特点,如 压缩 (compression),多路复用 (multiplexing),请求优先级 (prioritization of requests)等,用于减小等待时间、提高性能和安全性。ajax
在 HTTP 1.1 版本,安全链接是可选的,你可使用 HTTP 或 HTTPS,但在 HTTP/2,安全链接几乎是强制的,即便标准定义了有 TLS 的 HTTP/2 和没有 TLS 的 HTTP/2,但大部分浏览器提供商已经声明它们将只支持拥有 TLS 的 HTTP/2。浏览器
HTTP 的消息被设计为简单可读。这一点只要你打开浏览器控制台看看请求和响应头就知道了。缓存
除了规范规定的那些 header,咱们会看到不少非官方规定的 header,这些扩展的 header 能够用来实现新的功能。安全
这意味着即便你对服务器发起过一次请求,当你再发起一次请求时,服务器也不知道你拜访过它,基本的 HTTP 请求中是不含状态信息的。所幸咱们能够增长 cookie 的头部扩展来记录状态信息。
HTTP 依赖于运输层的 TCP 协议进行消息传递,而 TCP 是可靠的。
状态码的第一个数字表明当前响应类型
1xx - 信息,请求已被服务器接收,继续处理
2xx - 成功,请求已成功被服务器接收、理解、并接受
3xx - 重定向,须要后续操做才能完成这一请求
4xx - 客户端错误,请求含有词法错误或者没法被执行
5xx - 服务器端错误,服务器在处理某个正确请求时发生错误
OPTIONS:请求服务器告知其支持的各类功能。能够询问服务器一般支持哪些方法,或者对某些特殊资源支持哪些方法
HEAD:向服务器发出指定资源的请求,但服务器在响应中只返回首部,不会返回实体的主体即资源的 body 部分
PUT:向服务器写入资源
DELETE:请服务器删除请求 URL 所指定的资源
TRACE:HTTP 请求在传输的过程当中是可能会被修改的。这个方法容许客户端在最终将请求发送给服务器时,看看它变成了什么样子。主要用于测试或诊断
CONNECT:这个方法将请求的链接转换为透明的 TCP/IP 隧道,一般用于加快基于 SSL 加密的通讯
PATCH:这个方法请求对资源进行局部修改
GET 是最经常使用的方法,用于请求服务器发送某个资源。POST 方法用于向服务器发送数据,注意这和 PUT 的写入资源是不一样的,写入的资源是存储在服务器的,而发送的数据会发送到其余地方去处理,这可能会致使新资源的建立或已存在资源的更新,POST 一般用于支持 HTML 表单。
幂等
在数学中,幂等有两种主要含义:
在某二元运算下,幂等元素是指被本身重复运算的结果等于它本身的元素。例如 0 * 0 仍然为 0,因此 0 在乘法下是幂等的。
某一元运算为幂等的时,其做用在任一元素两次后会和其做用一次的结果相同,例如 恒等函数
f(x) = x
就是幂等的。在计算机网络中,假如在不考虑诸如错误或者过时等问题的状况下,若干次请求的反作用与单次请求相同或者根本没有反作用,那么这些请求方法就可以被视做是“幂等”的。
咱们知道,GET 用于表单数据提交时,表单数据会被编码在请求的 URI 上,这样极不安全,由于请求的地址可能在情趣到达目的地以前被打印,而后被第三方看到。而 POST 提交的数据是放在请求体中的,比 GET 提交数据安全,固然,没有 HTTPS,POST 提交的数据也能被看到。
可是最主要的区别,应该为以下三点:
安全性(safe): GET 请求是安全的,这里的安全是指 GET 方法只获取信息而不作其余操做。而 POST 请求则是不安全的,由于它会更新或建立资源。
幂等性(idempotent): GET 请求是幂等的,幂等就是上面所说的若干次请求的反作用与单次请求相同,而 POST 请求则是不幂等的。
可缓存性(cacheable): GET 请求的响应是可缓存的,POST 请求的响应通常是不可缓存的,除非设置了合适的 Cache-Control 或 Expires 头部字段。
有三个主要缘由:
机密性 (Confidentiality)
这能保护通讯的双方在公共媒介中免遭信息窃听。若是没有 HTTPS,当你经过 WiFi 接入点网上购物时,WiFi 接入点的运营者就能看到你的信用卡之类的隐私信息。
数据完整性 (Integrity)
这能保证信息能不受改变地完整到达目的地。好比,WiFi 能够在咱们的网站上增长广告,下降图片质量或者改变文章的内容。HTTPS 能确保网站不被更改。
身份验证 (Authentication)
这能保证网站不会是假冒的,域名是哪一个,站点就是哪一个。好比,一些运行 WiFi 接入点的猥琐之人能够把浏览器导向一些假冒的网站。HTTPS 能确保example.com
就是真的example.com
,有些安全证书甚至要检查网站的合法身份,让你知道yourbank.com
是YourBank, Inc
,即 YourBank 公司。
浏览器默认缓存 HTTP 的 GET 请求的响应,若是使用的是共享我的电脑 (网吧不就是么?),那么猥琐之人就能经过访问浏览器的缓存获得他人的隐私信息。因此咱们要在返回隐私信息时使用三个响应头字段来禁用客户端的缓存:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
cache-control 字段用来指定在此次的请求/响应链中的全部缓存机制都必须遵照的指令。上面的三个指令告诉客户端和代理在使用缓存前必须送请求到服务器进行验证,不要储存响应,在请求发生以后的 0 秒后缓存即过时,在使用缓存的响应前检查其状态并禁止使用过时的响应。
Pragma: no-cache
这个字段用于向后兼容 HTTP 1.0,确保老客户端能不缓存响应。
Expires: -1
这个字段用于指定响应过时的时间戳。指定为-1
的话,客户端就会当即把响应当作过时的,这就避免了缓存。
请在须要保护隐私时才禁用缓存,不然你的应用加载速度将大打折扣。
HTTPS 的重要性我在前面已经说了,要强制 HTTPS,咱们要使用 HTTP Strict Transport Security (HSTS) 头部字段,具体能够设置为Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
。
max-age
是指示浏览器为当前域名缓存这个头部字段指定秒数,这里是缓存一年。includeSubDomains
指示浏览器将 HSTS 应用于当前域名的全部子域名。preload
强制浏览器老是安全地加载你的 web 应用,甚至在你第一次访问网站且未收到响应前,这个特性是经过将一个安全预加载的域名的列表硬编码到浏览器代码中实现的,要使用这个特性你须要在 HSTS Preload List Submission 注册你的域名,固然啦,使用这个指令你得确保你的应用能只使用 HTTPS。
XSS 即跨站脚本攻击,是很广泛也很简单的网络攻击手法。咱们会使用X-XSS-Protection: 1; mode=block
,X-XSS-Protection这个字段指示浏览器是否为当前页面开启浏览器内建的 XSS 过滤机制并覆盖浏览器自己的相关设置。1
表示容许过滤器,反之为0
,mode=block
指示浏览器在检测到 XSS 攻击后禁止加载整个页面。
一个 iframe 是一个能够容许在一个父 web 应用中嵌套一个 web 应用的 DOM 元素。这个元素可让点击劫持 ( clickjacking) 更容易。点击劫持能够欺骗用户点击非用户本意的东西。为了阻止这种攻击,咱们使用X-Frame-Options: SAMEORIGIN
,X-Frame-Options这个字段指示浏览器是否容许你的 web 应用嵌套在另外一个 web 应用中,SAMEORIGIN 表示你的应用能够在同域名页面的 frame 中展现。注意当在 Content-Security-Policy 字段中指定了 frame-ancestors 时,则 X-Frame-Options 被忽略,见 这里。
CSP (Content Security Policy) 定义了一个很是强大的基于浏览器的安全机制,容许对你的 web 应用中的资源加载和脚本执行加以控制。有了它,你能够将容许脚本加载、ajax 调用、图片和样式表加载的域列入白名单,能够容许或禁止内联脚本和动态脚本等。咱们能够经过简单设置Content-Security-Policy: script-src 'self'
来容许同源的脚本加载以及阻止动态脚本和内联脚本执行。Content-Security-Policy 是个比较复杂的头部字段,详细的配置能够在这里看到。
为了使用户体验更连贯,不少浏览器实现了一个叫 “Content-Type 嗅探” 或 “MIME 嗅探” 的功能,这个功能使浏览器能经过实际资源的比特位检测 HTTP 中响应资源的类型而不理会响应头中的Content-Type
字段声明的资源类型。但这会致使 MIME 混淆攻击 (MIME confusion attack)。因而乎,咱们要使用X-Content-Type-Options: nosniff
,指示浏览器在处理获取的资源时不要使用嗅探。
多数主流浏览器已经在2015年末支持了该协议。此外,根据 W3Techs 的数据,在2017年5月,在排名前一千万的网站中,有13.7% 支持了HTTP/2。这真真是极吼滴!有图有真相。
有别于 HTTP/1.1 中的明文请求,HTTP/2 将一个 TCP 链接分为若干个流 (stream),每一个流中能够传输若干消息 (message),每一个消息由若干最小的二进制帧 (frame) 组成。这样更利于高效地解析,并且不容易出错,毕竟 HTTP/1.x 的 header 中有空白行、大小写、换行、空行之类的规定。
HTTP/1.x 有一个 head-of-line blocking 的问题,它会让一个链接一次只能发送一个请求。多路复用容许多个请求和响应消息同时发出,甚至能够混合一个消息的一部分和另外一个消息。
HTTP/1.x 中为了加载资源会同时打开多个 TCP 链接,每一个链接在响应时又都会发送大量数据,存在中间网络 (intervening network) 缓冲区溢出的危险,致使网络阻塞和重发 ( retransmits)。并且,使用那么多的 TCP 链接也是一种大量占用网络资源的行为。
在大型网站中,一个页面每每要请求大量资源并获得相应,算上那些往返的话,那么头部就会占据至关大的开销,因此压缩头部的好处便变得显而易见了。
在 HTTP/1.x 中,当浏览器请求了一个页面,服务器发送了 HTML 页面的响应,而后服务器须要等待浏览器解析了 HTML 文件后再发起嵌入在 HTML 页面中的多个资源的请求,想一想都以为慢。而服务器端推送避免了这种往返的延迟,服务器会主动推送它认为的客户端会须要缓存的资源。要当心的是,这个功能滥用的话,会损害性能。
HTTP/2 是向后兼容 HTTP/1.1 的,因此彻底不须要担忧现有的网站会发生什么问题。然而,不少对于 HTTP/1.1 来讲是最佳实践的技巧却会在使用 HTTP/2 时下降性能。再者,从 HTTP/1.1 转向 HTTP/2 势必是漫长的,由于服务器端升级容易,但只要一日绝大多数人仍然使用着老旧甚至是史前浏览器,客户端就会是扎心窝的痛。咱们须要作些过渡时期的事。
第一要务是让你的网站运行在安全链接上,由于厂商大佬们已经说过了:咱们哥们几个只支持有 TLS 的 HTTP/2,那些不支持 TLS 的能够歇菜了。因此国内大片的 HTTP 网站们要好好考虑转向 HTTPS 的事了。
在 HTTP/1.1 中,对于浏览器来讲,获取一个巨大的图片文件比请求多个小图片文件高效,这是由于多个请求会互相排队,增长加载时间。一般的作法是把多个小图片转成一个雪碧图。
转成一个雪碧图就只须要一个 HTTP 请求了。可是咧,若是一个页面中只使用到雪碧图中的一个图标,仍然下载整个雪碧图就显得不是很好了。HTTP/2 拥有多路复用的能力,因此多个请求排队的事已经不存在了,不少时候单个地请求图片将是更好的选择。固然,须要使用全部图标时,雪碧图仍然是须要的。
在 HTTP/1.1 中,为了减小 HTTP 请求,你能够把图片直接以 Data URI 的形式嵌入 CSS 或 HTML 文件中。由于转成的字符编码会很长,天然增长了 CSS 或 HTML 文件的大小。在 HTTP/2 中,HTTP 请求变得廉价,这种 “最佳实践” 便变成了一种妨碍。
咱们在应用的构建阶段,一般会合并那些小的 CSS 和 JavaScript 文件,本意是减小 HTTP 请求。可是在 HTTP/2 中,HTTP 请求是廉价的,合并便再也不显得有优点。可能更糟糕的是,若是你由于合并引入了不少非本页面所需的文件,反而拖慢了加载速度,固然,你是能够经过 webpack 来配置相应页面所需的相应文件的,但是别忘了,合并文件是要引入大量处理合并的代码的,在 HTTP/2 廉价的请求中,这些多余的处理代码看来有点多余,止增笑耳。
在 HTTP/1.1 中,每一个域名能打开的链接是受到限制的,大概为 6 - 8 个,具体取决于浏览器实现。若是你实在要加载大量资源,其中一个方法就是从多个域名中获取资源,这就是域名分片 (domain sharding)。这个方法能实现更好的加载时间,也有能致使问题,固然,准备这种服务就会有额外的开支。HTTP/2 则移除了域名分片的需求,在 HTTP/2 下,浏览器只为每一个域名打开一个链接,可是人家有多路复用嘛,并发请求的数量根本不受限制 (固然也能够经过 SETTINGS_MAX_CONCURRENT_STREAMS 来限制)。并且,域名分片在 HTTP/2 中还会损害性能,由于它建立了额外的 TCP 链接,妨碍 HTTP/2 为资源进行优先级排序。
当前对于 HTTP/1.1 的最佳实践是限制域名分片为 2 个域名。好消息是为了减小工做并在 HTTP/1.1 和 HTTP/2 都能达到很好的效果,有方法可让 HTTP/2 合并你的链接,具体方法见 Google 家的 slide 26。
可喜的是,如今已经有大量的 HTTP/2 的服务器端实现了,涵盖了多种语言,参见
Implementations。