如何在Ubuntu16.04上使用Nginx的标头模块实现浏览器缓存
介绍
网站加载速度越快,访问者留下的可能性就越大。 当网站充满了由后台加载的脚本运行的图像和交互式内容时,打开网站并不是一件容易的事。 它包括从服务器一个一个地请求许多不同的文件。 尽量减少这些请求的数量是加快网站速度的一种方法。
这可以通过多种方式完成,但更重要的步骤之一是配置 浏览器缓存 。 这告诉浏览器下载一次的文件可以从本地副本中重复使用,而不是一次又一次地向服务器请求它们。 为此,必须引入新的 HTTP 响应标头,告诉浏览器如何操作。
这就是 Nginx 的头模块发挥作用的地方。 该模块可用于向响应添加任意标头,但其主要作用是正确设置缓存标头。 在本教程中,我们将了解如何使用 Nginx 的 header 模块来实现浏览器缓存。
先决条件
要遵循本教程,您将需要:
- 使用 this initial server setup tutorial 设置的一台 Ubuntu 16.04 服务器,包括 sudo 非 root 用户。
- 按照 如何在 Ubuntu 16.04 上安装 Nginx 教程 在您的服务器上安装 Nginx。
除了 header 模块,我们还将在本文中使用 Nginx 的 map 模块。 要了解更多关于地图模块的信息,您可以阅读如何在 Ubuntu 16.04 上使用 Nginx 的地图模块。
第 1 步 - 创建测试文件
在这一步中,我们将在默认的 Nginx 目录中创建几个测试文件。 稍后我们将使用这些文件来检查 Nginx 的默认行为,然后测试浏览器缓存是否正常工作。
为了决定通过网络提供哪种文件,Nginx 不会分析文件内容; 那将非常缓慢。 相反,它只是查找文件扩展名来确定文件的 MIME 类型,它表示文件的用途。
由于这种行为,我们的测试文件的内容是不相关的。 通过适当地命名文件,我们可以诱使 Nginx 认为,例如,一个完全空的文件是图像,另一个是样式表。
使用 truncate
在默认 Nginx 目录中创建一个名为 test.html
的文件。 此扩展名表示它是一个 HTML 页面。
sudo truncate -s 1k /var/www/html/test.html
让我们以同样的方式再创建几个测试文件:一个 jpg
图像文件、一个 css
样式表和一个 js
JavaScript 文件。
sudo truncate -s 1k /var/www/html/test.jpg sudo truncate -s 1k /var/www/html/test.css sudo truncate -s 1k /var/www/html/test.js
下一步是检查 Nginx 在使用我们刚刚创建的文件进行全新安装时发送缓存控制标头的行为。
第 2 步 — 检查默认行为
默认情况下,所有文件都将具有相同的默认缓存行为。 为了探索这一点,我们将使用我们在步骤 1 中创建的 HTML 文件,但您可以使用任何示例文件运行这些测试。
因此,让我们检查 test.html
是否提供了有关浏览器应缓存响应多长时间的任何信息。 以下命令从我们的本地 Nginx 服务器请求一个文件并显示响应标头。
curl -I http://localhost/test.html
您应该会看到几个 HTTP 响应标头:
Output: Nginx response headersHTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:12:26 GMT Content-Type: text/html Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" Accept-Ranges: bytes
在倒数第二行中,您可以看到 ETag
标头,其中包含所请求文件的此特定版本的唯一标识符。 如果重复执行前面的 curl
命令,您将看到完全相同的 ETag
值。
当使用网络浏览器时,ETag
值被存储并在浏览器想要再次请求相同文件时以 If-None-Match
请求标头发送回服务器 - 例如,刷新页面时.
我们可以使用以下命令在命令行上模拟这一点。 确保更改此命令中的 ETag
值以匹配之前输出中的 ETag
值。
curl -I -H 'If-None-Match: "57d40685-400"' http://localhost/test.html
现在的响应将有所不同:
Output: Nginx response headersHTTP/1.1 304 Not Modified Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:20:31 GMT Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400"
这一次,Nginx 将响应 304 Not Modified。 它不会再次通过网络发送文件; 相反,它会告诉浏览器它可以重用它已经在本地下载的文件。
这很有用,因为它减少了网络流量,但还不足以实现良好的缓存性能。 ETag
的问题是浏览器 总是 向服务器发送请求,询问它是否可以重用其缓存文件。 即使服务器以 304 响应而不是再次发送文件,仍然需要时间来发出请求并接收响应。
在下一步中,我们将使用 headers 模块来附加缓存控制信息。 这将使浏览器在本地缓存一些文件,而无需明确询问服务器是否可以这样做。
第 3 步 — 配置 Cache-Control 和 Expires 标头
除了ETag
文件验证头之外,还有两个缓存控制响应头:Cache-Control
和Expires
。 Cache-Control
是较新的版本,它比 Expires
具有更多选项,如果您想更好地控制缓存行为,通常更有用。
如果设置了这些标头,它们可以告诉浏览器所请求的文件可以在本地保存一段时间(包括永久)而无需再次请求。 如果未设置标头,浏览器将始终从服务器请求文件,期望 200 OK 或 304 Not Modified 响应。
我们可以使用 header 模块来设置这些 HTTP 标头。 头模块是一个核心的 Nginx 模块,这意味着它不需要单独安装即可使用。
要添加头模块,请在 nano
或您喜欢的文本编辑器中打开默认的 Nginx 配置文件。
sudo nano /etc/nginx/sites-available/default
找到 server
配置块,如下所示:
/etc/nginx/sites-available/default
. . . # Default server configuration # server { listen 80 default_server; listen [::]:80 default_server; . . .
在此处添加以下两个新部分:一个在 server
块之前,用于定义缓存不同文件类型的时间,另一个在其中,用于适当地设置缓存头。
修改 /etc/nginx/sites-available/default
. . . # Default server configuration # # Expires map map $sent_http_content_type $expires { default off; text/html epoch; text/css max; application/javascript max; ~image/ max; } server { listen 80 default_server; listen [::]:80 default_server; expires $expires; . . .
server
块之前的部分是一个新的 map
块,它定义了文件类型和该类型文件应该被缓存多长时间之间的映射。
我们在这张地图中使用了几种不同的设置:
- 默认值设置为
off
,不会添加任何缓存控制头。 对于我们对缓存的工作方式没有特别要求的内容,这是一个安全的选择。 - 对于
text/html
,我们将值设置为epoch
。 这是一个特殊的值,它明确导致没有缓存,这迫使浏览器总是询问网站本身是否是最新的。 - 对于
text/css
和application/javascript
,它们是样式表和 Javascript 文件,我们将值设置为max
。 这意味着浏览器将尽可能长时间地缓存这些文件,从而大大减少请求数量,因为这些文件通常有很多。 - 最后一个设置用于
~image/
,它是一个正则表达式,它将匹配在其 MIME 类型 名称中包含image/
的所有文件类型(如image/jpg
和image/png
)。 和样式表一样,网站上通常有很多图片可以安全缓存,所以我们也将其设置为max
。
在 server 块内部,expires
指令(headers 模块的一部分)设置缓存控制头。 它使用地图中设置的 $expires
变量的值。 这样,生成的标题将根据文件类型而有所不同。
保存并关闭文件以退出。
要启用新配置,请重新启动 Nginx。
sudo systemctl restart nginx
接下来,让我们确保我们的新配置有效。
第 4 步 — 测试浏览器缓存
对测试 HTML 文件执行与之前相同的请求。
curl -I http://localhost/test.html
这一次的反应会有所不同。 您应该会看到两个额外的 HTTP 响应标头:
Nginx 响应头
HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:48:53 GMT Content-Type: text/html Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:33 GMT Connection: keep-alive ETag: "57d40685-400" Expires: Thu, 01 Jan 1970 00:00:01 GMT Cache-Control: no-cache Accept-Ranges: bytes
Expires
标头显示过去的日期,Cache-Control
设置为 no-cache
,它告诉浏览器始终询问服务器是否有更新版本的文件(像以前一样使用 ETag
标头)。
您将看到测试图像文件的不同响应。
curl -I http://localhost/test.jpg
Nginx 响应头
HTTP/1.1 200 OK Server: nginx/1.10.0 (Ubuntu) Date: Sat, 10 Sep 2016 13:50:41 GMT Content-Type: image/jpeg Content-Length: 1024 Last-Modified: Sat, 10 Sep 2016 13:11:36 GMT Connection: keep-alive ETag: "57d40688-400" Expires: Thu, 31 Dec 2037 23:55:55 GMT Cache-Control: max-age=315360000 Accept-Ranges: bytes
在这种情况下,Expires
显示了遥远的将来的日期,而 Cache-Control
包含了 max-age
信息,它告诉浏览器它可以以秒为单位缓存文件多长时间。 这告诉浏览器尽可能长时间地缓存下载的图像,因此该图像的任何后续出现都将使用本地缓存,根本不会向服务器发送请求。
test.js
和 test.css
的结果应该相似,因为 JavaScript 和样式表文件也都设置了缓存头。
这意味着缓存控制标头已正确配置,您的网站将受益于性能提升和由于浏览器缓存而减少的服务器请求。 您应该根据您网站的内容自定义缓存设置,但本文中的默认设置是一个合理的起点。
结论
headers 模块可用于向响应添加任意标头,但正确设置缓存控制标头是其最有用的应用之一。 它提高了网站用户的性能,尤其是在延迟较高的网络上,如移动运营商网络。 它还可以在将速度测试纳入其结果的搜索引擎上产生更好的结果。 设置浏览器缓存标头是 Google PageSpeed 测试工具的主要建议之一。
关于 headers 模块的更多详细信息可以在 Nginx 的官方 headers 模块文档 中找到 。