阅读 920

「译」开发者的责任之 HTTP header


本文是访谈“开发者的责任之Http Header”的书面版本。 您可以查看幻灯片录音


译者注

本文篇幅较长,全部阅读完大约需要10分钟。以下是本文概览。

  • HTTPS和HSTS(如何建立安全的连接)
  • CSP(内容安全策略)
  • 缓存控制
  • immutable - 从不请求资源两次
  • Accept-Encoding (压缩算法说明)
  • Accept - 提供正确的图像格式
  • Accept-CH - 提供适当大小的图像
  • rel = preload (预加载,减少等待时间)
  • Feature-Policy (功能策略)

「译」开发者的责任之Http Header


本文是访谈“开发者的责任之Http Header”的书面版本。 您可以查看幻灯片录音


前言

原文链接:HTTP headers for the responsible developer
如今,使用网络是许多人的默认状态。 我们都花时间购物,聊天,阅读文章,寻找方向等信息。 网络将我们与整个世界联系起来,但大多数情况下,网络将人们连接起来。 我自己已经使用网络已有20年了,八年前,当我成为网络开发人员时,我与它的关系发生了变化。

开发人员连接了人们。
开发人员帮助了人们。
开发人员支持着人们。
**
开发人员有能力为每个人构建Web服务,但需要负责任地使用该功能。 最重要的是,建立有助于和帮助人们的事物。
在本文中,我想分享HTTP Header 如何帮助您构建更好的产品,以便为每个人提供更好的Web服务。

HTTP  - 超文本传输协议

我们先谈谈HTTP。 HTTP是计算机用于通过Web请求和发送数据的协议。
当浏览器从服务器请求资源时,它使用HTTP。 此请求包括一组键值对,提供浏览器版本或其理解的文件格式等信息。 这些键值对称为 request headers
服务器使用所请求的资源进行应答,但也会发送 response headers,提供有关资源或服务器本身的信息。

Request:
GET https://the-responsible.dev/
Accept: text/html,application/xhtml+xml,application/xml
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8,de;q=0.7
...

Response:
Connection: keep-alive
Content-Type: text/html; charset=utf-8
Date: Mon, 11 Mar 2019 12:59:38 GMT
...
Response Body
复制代码

HTTP是当今网络的基础,它提供了许多改善用户体验的方法。 让我们深入探讨如何使用HTTP Header构建安全,经济且值得尊重的Web服务。

网络必须安全

我从来没有在浏览互联网时感到不安全。 我对网络的了解越多,我就越关注。 您可以阅读有关黑客可能会改变全球CDN托管的 库随机网站在其访问者的浏览器中挖掘加密货币以及人们将社交工程定期用于成功的开源项目。 这不好,但为什么要关心呢?

如果您今天正在为网络构建,那么您不仅要编写代码。 如今,Web开发包括许多在同一站点上工作的人。 您可能也提供了大量的开源代码。 此外,您可以包含多个第三方脚本用于销售目的。 数百人提供您网站上运行的代码。 开发人员必须应对这一现实。

你应该相信所有这些人及其所有源代码吗?
我认为你不能相信任何第三方代码。 幸运的是,有一些方法可以保护您的网站并使其更安全。 此外,像头盔这样的工具可以帮助确保你的快递申请的安全
如果您想分析您的网站上运行了多少第三方代码,您可以查看您选择的开发人员工具的网络面板,或者尝试使用Request Map Generator

HTTPS和HSTS  - 确保您的连接安全

安全连接是安全互联网的基础。 如果没有通过HTTPS运行的加密请求,您无法确定您的网站与访问者之间是否存在任何人。 一个人可以快速启动一个公共wifi网络,并对一个连接任何人进行中间攻击。 你多久使用一次公共wifi? 而且,你经常检查它是否值得信赖?

幸运的是,今天TLS证书是免费的; HTTPS已成为一种标准,浏览器供应商仅在安全连接上提供最先进的功能,甚至将非HTTPS网站标记为不安全,从而推动HTTPS的采用。 不幸的是,我们一直没有安全浏览。 当有人想打开一个网站时,他们没有将协议输入地址栏(他们为什么要这样做?)。 此操作会导致未加密的HTTP请求。 安全运行站点然后重定向到HTTPS。 但是如果有人拦截了第一个无担保的请求怎么办?
您可以使用HSTS response headers(HTTP严格传输安全性)告诉浏览器您的网站仅通过HTTPS工作。

Strict-Transport-Security: max-age=1000; includeSubDomains; preload
复制代码

Header 告诉浏览器您不想使用HTTP请求,它会自动使用安全连接向相同的源发出以下请求。 当您尝试再次通过HTTP访问相同的URL时,浏览器将在内部使用HTTPS和重定向。

您可以配置此设置应该激活的时间长度(以秒为单位的 max-age),以便您可能希望再次使用HTTP。 如果要包含子域,则可以使用 includeSubDomains 配置子域。

如果您想加倍努力并希望确保浏览器永远不会通过HTTP请求您的站点,您还可以设置preload指令并将您的站点提交到全局列表。 如果您的站点的HSTS配置满足最小年龄为一年的要求并且对于子域是活动的,则它可以包含在仅通过HTTPS工作的站点的内部浏览器记录中。

你有没有想过为什么你不能再通过HTTP使用my-site.dev这样的本地环境了? 此内部记录的原因是 -  .dev域名自动包含在此列表中,因为它在2019年2月成为真正的顶级域名。

HSTS Header 不仅使您的网站更安全,而且还加快了它的速度。 想象一下有人通过慢速移动连接访问您的网站。 如果第一个请求是通过HTTP进行的,只是为了接收重定向,那么用户可以花费几秒钟而不会看到任何内容。 使用HSTS,您可以节省这些时间,浏览器会自动使用HTTPS。

CSP  - 明确您网站上允许的内容

既然您的站点通过安全连接运行,您可能会遇到这样的问题:由于混合内容策略,浏览器开始阻止发送到不安全地址的请求。 内容安全策略(CSP)标头提供了处理这些情况的绝佳方法。 您可以通过提供的HTML中的元素或HTTP Header 定义CSP规则集。
Content-Security-Policy: upgrade-insecure-requests
upgrade-insecure-requests 指令告诉浏览器神奇地将所有HTTP请求升级到HTTPS。
CSP不仅仅是关于使用的协议。 它提供了详细的方法来定义站点上允许的资源和操作。 您可以定义例如应该执行哪些脚本或应该从哪里加载图像。 如果不允许某些内容,浏览器会阻止它并阻止您的网站受到潜在的攻击。


在撰写本文时,CSP有24种不同的配置选项。 这些选项包括脚本而不是样式表,甚至是服务工作者的起源。

您可以在MDN上找到完整的概述。

使用CSP,您可以确定您的网站应包含哪些内容以及不包含哪些内容。

Content-Security-Policy: default-src 'self'; script-src 'self' just-comments.com www.google-analytics.com production-assets.codepen.io storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: images.contentful.com images.ctfassets.net www.gravatar.com www.google-analytics.com just-comments.com; font-src 'self' data:; connect-src 'self' cdn.contentful.com images.contentful.com videos.contentful.com images.ctfassets.net videos.ctfassets.net service.just-comments.com www.google-analytics.com; media-src 'self' videos.contentful.com videos.ctfassets.net; object-src 'self'; frame-src codepen.io; frame-ancestors 'self'; worker-src 'self'; block-all-mixed-content; manifest-src 'self' 'self'; disown-opener; prefetch-src 'self'
复制代码

上面的规则集是我为我的个人网站发布的规则集,如果您认为这个CSP示例定义非常难以提出 - 您完全正确。 在我的个人网站上实施CSP规则集让我尝试了三次尝试。 我部署它并回滚,因为它多次打破了我的网站。 有一种更好的方法可以做到这一点。

为避免破坏您的网站,CSP还提供仅限报告的选项。

Content-Security-Policy-Report-Only: default-src 'self'; ... report-uri https://stefanjudis.report-uri.com/r/d/csp/reportOnly
复制代码

使用Content-Security-Policy-Report-Only模式浏览器仅将阻止的资源记录到控制台而不是阻止它们。 此报告机制为您提供了检查和调整规则集的方法。

两个标题,Content-Security-PolicyContent-Security-Policy-Report-Only,还提供了一种定义端点以将违规和日志信息发送到(report-uri)的方法。 您可以设置日志记录服务器并使用发送的日志信息来调整CSP规则,直到它准备好上线。

建议的过程是:首先仅在报告模式下部署CSP,使用实际流量分析传入的违规行为,并且只有在不再出现违反受控资源的情况下才会启用它。

如果您正在寻找可以帮助您处理这些日志的服务,我正在使用Report URI,它会很好的帮助你。

整体采用CSP

目前,浏览器对CSP的支持很好,但不幸的是,没有多少网站在使用它。 要查看有多少网站使用CSP提供内容,我在HTTP Archive上运行了一个查询,发现只有6%的已爬网站点使用CSP。 我认为我们可以做得更好,使网络更安全,避免我们的用户在不知情的情况下挖掘加密货币

jAQW_VflYXdzcl9ER0yTVPGpTBRQAOVZrIvgjSW6ucayZ.width-1600.jpg

Web 必须是可负担的

在我写这篇文章的时候,我在家里使用我的快速Wi-Fi连接坐在一台相对较新的MacBook前面。 开发人员经常忘记这样的设置不是我们大多数用户的默认设置。 访问我们网站的人使用的是旧手机,并且处于糟糕的连接状态。 拥有数百个请求的繁重和臃肿的网站给他们带来了糟糕的体验。

这不仅仅是关于体验的。 人们根据他们居住的地点为数据支付不同的金额。 想象一下,你正在建立一个医院网站。 有关此特定网站的信息可能对人们至关重要并且可以挽救生命。

如果医院网站上的页面是5MB,那么它不仅速度慢,而且对于最需要它的人来说也可能太昂贵。 与非洲5MB数据的价格相比,欧洲或美国的5MB价格无关紧要。 开发人员有责任让每个人都能负担得起网络。 这个职责包括提供正确的资源,评估正确使用的工具(你真的需要一个登陆页面的JS框架吗?),以及避免请求。

缓存控制 - 避免对未更改资源的请求

今天的网站很容易包含数百种资源,从样式表到脚本文件再到图像。 使用Cache-Control标头,开发人员可以定义资源应被视为“新鲜”多长时间,并且可以从浏览器缓存中提供。

Cache-Control: max-age=30, public
复制代码

通过设置适当的Cache-Control,可以保存数据传输,并且可以从浏览器缓存中使用文件一定的秒数(max-age)。 浏览器应在时间跨度结束后重新验证缓存的资源。

但是,当访问者刷新页面时,浏览器仍会重新验证包含引用资源的页面,以确保缓存的数据仍然有效。 服务器以304标头响应,表示缓存的数据仍然良好,或200服务于更新的数据。 这可以节省传输的数据,但不一定是请求。

这是 immutable 特征发挥作用的地方。

immutable - 从不请求资源两次

在现代前端应用程序中,样式表和脚本文件通常具有唯一的文件名,如styles.123abc.css。 此文件名取决于特定文件的内容。 当这些文件的内容发生更改时,会生成不同的文件名。

这些唯一文件可能会被永久缓存,包括用户刷新页面时的情况。 不可变特征可以告诉浏览器不要在特定时间范围内重新验证资源。 这对指纹验证的资源很有意义,有助于避免重新验证请求。

Cache-Control: max-age=31536000, public, immutable
复制代码

最佳缓存非常困难,特别是浏览器缓存不是很直观,因为它提供了多种配置。 我建议您查看以下资源:

Accept-Encoding  - 压缩到最大值(uhm ...最小值)

使用 Cache-control,我们可以保存请求并减少重复通过线路传输的数据量。 我们不仅可以保存请求,还可以缩小转移的内容。
在提供资源时,开发人员应确保尽可能少地发送数据。 对于像HTML这样的基于文本的资源,CSS和JavaScript压缩在保存传输数据方面起着重要作用。
目前最常用的压缩是GZIP。 服务器足够快,可以动态压缩文本文件,并在请求时提供压缩数据。 GZIP不再是最好的选择了。
如果您检查浏览器对基于文本的文件(如HTML,CSS和JavaScript)所做的请求,并查看请求标头,您将找到accept-encoding Header。

Accept-Encoding: gzip, deflate, br
复制代码

此标头告诉服务器它理解的压缩算法。 不太知名的br代表是 Brotli(一种压缩算法),是谷歌和Facebook等高流量网站使用的压缩。 要使用Brotli,您的网站必须在HTTPS上运行。

此压缩算法在创建时考虑了小文件大小。 当您在本地计算机上试一试并手动压缩文件时,您会发现Brotli确实比GZIP压缩得更好。

您可能听说Brotli压缩也会变慢。 原因是Brotli有11种压缩模式,并且选择默认模式以保证较小的文件大小,从而导致编码时间较长。 另一方面,GZIP有9种模式,并且考虑到速度和文件大小,选择默认模式。
此决定使得Brotli的默认模式不适合进行即时压缩,但如果更改压缩模式,您可以实现更小的文件大小,速度与GZIP相当。 您甚至可以使用它进行即时压缩,并将其视为支持浏览器的潜在GZIP替代品。

除此之外,如果您想获得最大的文件节省,您可能会忘记在构建过程中使用zopfli和Brotli文件动态压缩文件和预生成优化的GZIP文件以静态地为它们提供服务的想法。

如果您想了解更多关于Brotli压缩以及它与GZIP的比较,Akamai的人们围绕这一主题进行了广泛的研究。

Accept and Accept-CH-为用户提供量身定制的资源

优化文本资产非常适合保存千字节,但是像图像这样的较重资源可以节省更多数据。

Accept - 提供正确的图像格式

浏览器不仅告诉我们他们理解的压缩算法。 当您的浏览器请求图像时,它还会提供有关其理解的文件格式的信息。

Accept: image/webp, image/apng, image/*,*/*;q=0.8
复制代码

围绕新图像格式的竞争已经持续了几年,但现在看起来像webp赢了。 Webp是Google发明的一种图像格式,目前对webp图像的支持非常好。

使用此请求标头,即使浏览器请求image.jpg导致文件较小,开发人员也可以提供 **webp **图像。 Dean Hume写了一篇关于如何在服务工作者中做这件事的好指南。 那太酷了!

9JczExPcmc402uz1B2mY_KmhuaQNl6UExmSYcs2ZH7yND.width-1600.jpg

Accept-CH  - 提供适当大小的图像

您还可以启用客户端提示以支持浏览器。 客户端提示是一种告诉浏览器发送附加标题的方法,这些标题提供有关视口宽度,图像宽度甚至网络条件的信息,如RTT(往返时间)和连接类型(如2g)。

您可以通过包含元元素来启用它们:

<meta http-equiv="Accept-CH" content="Viewport-Width, Downlink">
<meta http-equiv="Accept-CH-Lifetime" content="86400">
复制代码

或者在初始HTML请求上设置标题:

Accept-CH: Width, Viewport-Width
Accept-CH-Lifetime: 100
复制代码

支持浏览器将开始在以下请求中发送定义的时间跨度(Accept-CH-Lifetime,以秒为单位)的附加信息,这些请求可以帮助开发人员根据用户条件定制示例图像,而无需更改任何HTML。
要在服务器端接收例如图像宽度等附加信息,您可以为图像配备尺寸属性,以便为浏览器提供有关如何布置图像的其他信息。

<!-- this images is laid over the full width | 100 viewport width -->
<img class="w-100" src="/img/header.jpg" alt="" sizes="100vw">
复制代码

使用最初收到的Accept-CH响应标题和配备了支持浏览器的sizes属性的图像将在图像请求中包含视口宽度和宽度标题,告诉您哪个图像最适合。

8u07oEaYg3fBaV6Z35RcBDXW7hehK8wPrPSthhRpQlHN3.width-1600.jpg

拥有支持的图像格式和图像尺寸,您可以发送定制的媒体,而无需编写容易出错的图像元素,重复处理文件格式和大小如下所示。

<picture>
  <!-- serve WebP to Chrome, Edge, Firefox and Opera -->
  <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.webp 200w, /image/thing-400.webp 400w,
        /image/thing-800.webp 800w, /image/thing-1200.webp 1200w,
        /image/thing-1600.webp 1600w, /image/thing-2000.webp 2000w"
    type="image/webp">
  <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.webp 200w, /image/thing-crop-400.webp 400w,
        /image/thing-crop-800.webp 800w, /image/thing-crop-1200.webp 1200w,
        /image/thing-crop-1600.webp 1600w, /image/thing-crop-2000.webp 2000w"
    type="image/webp">
 <!-- serve JPEG to others -->
  <source
    media="(min-width: 50em)"
    sizes="50vw"
    srcset="/image/thing-200.jpg 200w, /image/thing-400.jpg 400w,
        /image/thing-800.jpg 800w, /image/thing-1200.jpg 1200w,
        /image/thing-1600.jpg 1600w, /image/thing-2000.jpg 2000w">
  <source
    sizes="(min-width: 30em) 100vw"
    srcset="/image/thing-crop-200.jpg 200w, /image/thing-crop-400.jpg 400w,
        /image/thing-crop-800.jpg 800w, /image/thing-crop-1200.jpg 1200w,
        /image/thing-crop-1600.jpg 1600w, /image/thing-crop-2000.jpg 2000w">
  <!-- fallback for browsers that don't support picture -->
  <img src="/image/thing.jpg" width="50%">
</picture>
复制代码

通过访问视口宽度和图像大小的可能性,您可以将资产调整大小逻辑移动到服务器的中心位置。
你必须考虑,只是因为你手头有准确的图像宽度,你不想为每个宽度创建图像。 发送特定维度范围(image-200,image-300,...)的图像有助于利用CDN缓存并节省计算时间。

此外,利用服务工作者等当前技术,您甚至可以在客户端中拦截和更改请求以提供最佳图像文件。 通过启用的客户端提示,服务工作人员可以访问布局信息,并结合图像API(例如Cloudinary),您可以在浏览器中调整图像URL以接收正确大小的图像。

如果您正在寻找有关客户提示的更多信息,可以查看Jeremy Wagner的文章或Ilya Grigorik关于该主题的文章。

网络必须尊重他人

我们所有人每天都要上网几个小时,我认为最后一个方面非常重要 - 网络必须尊重他人。

预加载 - 减少等待时间

作为开发人员,我们希望确保重视用户的时间。 没人想浪费时间。 如前面部分所述,提供正确的数据在节省时间和数据方面发挥着重要作用。 这不仅是关于提出的请求,还关于这些请求的时间和顺序。

让我们想一个例子:如果您在网站中包含样式表,浏览器在加载之前不会显示任何内容。 虽然没有显示任何内容,但浏览器仍会继续解析HTML以查找要获取的其他资源。 加载和解析样式表时,它可能包含对其他关键资源的引用,例如必须请求的字体。 此顺序过程可以增加访问者的加载时间。

使用rel = preload,您可以向浏览器提供有关在不久的将来请求的资源的信息。

您可以通过HTML元素预加载资源:

  <link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin="anonymous">
复制代码

或者是Headers:

Link: </font.woff2>; rel=preload; as=font; no-push
复制代码

这样,浏览器接收标头或发现链接元素并立即请求资源,以便在需要时它们已经位于浏览器缓存中。 此过程重视访问者的时间。
要以最佳方式预加载资源并了解所有配置,我建议您查看以下资源:

Feature-Policy- 不要成为讨厌的人

如果最后有一件事我不想再看到,那就是网站无缘无故地要求我获得权限。 我只能引用我的同事菲尔纳什的话题。

**不要求页面加载的通知权限。

**
开发人员应该尊重访问者,不要建立惹恼访问者的网站。 人们只需单击所有权限对话框即可。 网站和开发人员失去了很多信任,如果我们开发人员没有仔细使用它们,那么所有新的闪亮网页功能都会失去力量。

但是,如果您的站点必须包含大量第三方代码并且所有这些脚本会触发一个又一个的权限对话,该怎么办? 如何确保所有包含的脚本都表现出来?

这是Feature-Policy Header发挥作用的地方。 使用此标头,您可以定义允许的功能,并限制可能由您网站中运行代码的其他人触发的弹出权限对话框。

Feature-Policy: vibrate 'none'; geolocation 'none'
复制代码

您可以使用标头为您的网站定义它,但也可以为嵌入式内容定义它,例如iframe,这对于第三方集成是必需的。

<iframe allow="camera 'none'; microphone 'none'">
复制代码

在撰写本文时,功能策略具有很高的实验性,但如果你看看即将推出的选项,那就非常令人兴奋了。 在不久的将来,开发人员不仅可以限制自己并防止恼人的对话框的传输,还可能阻止非优化的媒体。 这些功能将在用户体验方面产生重大影响。


您可以在 MDN 上找到完整的概述。

查看上面的功能策略列表,您可能想知道最烦人的一次推送通知发生了什么。 事实证明推送通知的功能策略的实现比预期更难。 如果您想了解更多信息,可以按照提交的GitHub issues 进行操作。

使用功能政策,您可以确保您和您的第三方不会将您的网站变成许可解雇竞赛,遗憾的是,这种竞争已经成为当今许多网站的默认状态。

网络必须适合所有人

在本文中,我只介绍了一些有助于改善用户体验的标题。 如果你想看到几乎完整的标题及其可能性的概述,我可以推荐看看Christian Schaefer的幻灯片“HTTP标题 - 隐藏的冠军”。

我知道今天建立一个伟大的网站是非常具有挑战性的。 开发人员必须考虑设计,设备,框架,以及是......标题也起作用。 希望本文给出了一些想法,您将在下一个Web项目中考虑安全性,可负担性和尊重,因为这些因素使Web真正适合每个人。

作者联系方式

文章分类
前端