如何使用浏览器缓存和Nginx,提升首屏访问速度

2,829 阅读5分钟

前言

  1. 适用于云原生架构下的前端微服务,微服务内配置nginx.conf
  2. 适用于前端负责web nginx的相关配置
  3. 适用于想了解协商缓存的同学

缓存操作的目标

虽然 HTTP 缓存不是必须的,但重用缓存的资源通常是必要的。然而常见的 HTTP 缓存只能存储 GET 响应,对于其他类型的响应则无能为力。缓存的关键主要包括request method和目标URI(一般只有GET请求才会被缓存)。 普遍的缓存案例:

  • 一个检索请求的成功响应: 对于 GET请求,响应状态码为:200,则表示为成功。一个包含例如HTML文档,图片,或者文件的响应。
  • 永久重定向: 响应状态码:301
  • 错误响应: 响应状态码:404 的一个页面。
  • 不完全的响应: 响应状态码 206,只返回局部的信息。
  • 除了 GET 请求外,如果匹配到作为一个已被定义的cache键名的响应。

Cache-control 头

HTTP/1.1定义的 Cache-Control 头用来区分对缓存机制的支持情况, 请求头和响应头都支持这个属性。通过它提供的不同的值来定义缓存策略。

没有缓存

缓存中不得存储任何关于客户端请求和服务端响应的内容。每次由客户端发起的请求都会下载完整的响应内容。

Cache-Control: no-store

缓存但重新验证

如下头部定义,此方式下,每次有请求发出时,缓存会将此请求发到服务器(译者注:该请求应该会带有与本地缓存相关的验证字段),服务器端会验证请求中所描述的缓存是否过期,若未过期(注:实际就是返回304),则缓存才使用本地缓存副本。

Cache-Control: no-cache

私有和公共缓存

"public" 指令表示该响应可以被任何中间人(译者注:比如中间代理、CDN等)缓存。若指定了"public",则一些通常不被中间人缓存的页面(译者注:因为默认是private)(比如 带有HTTP验证信息(帐号密码)的页面 或 某些特定状态码的页面),将会被其缓存。

而 "private" 则表示该响应是专用于某单个用户的,中间人不能缓存此响应,该响应只能应用于浏览器私有缓存中。

Cache-Control: private
Cache-Control: public

过期

过期机制中,最重要的指令是 "max-age=<seconds>",表示资源能够被缓存(保持新鲜)的最大时间。相对Expires而言,max-age是距离请求发起的时间的秒数。针对应用中那些不会改变的文件,通常可以手动设置一定的时长以保证缓存有效,例如图片、css、js等静态资源。

Cache-Control: max-age=31536000

验证方式

当使用了 "must-revalidate" 指令,那就意味着缓存在考虑使用一个陈旧的资源时,必须先验证它的状态,已过期的缓存将不被使用。详情看下文关于缓存校验的内容。

Cache-Control: must-revalidate

Expires

Expires 响应头包含日期/时间, 即在此时候之后,响应过期。

无效的日期,比如 0, 代表着过去的日期,即该资源已经过期。

如果在Cache-Control响应头设置了 "max-age" 或者 "s-max-age" 指令,那么 Expires 头会被忽略。

缓存验证

用户点击刷新按钮时会开始缓存验证。如果缓存的响应头信息里含有"Cache-control: must-revalidate”的定义,在浏览的过程中也会触发缓存验证。另外,在浏览器偏好设置里设置Advanced->Cache为强制验证缓存也能达到相同的效果。

当缓存的文档过期后,需要进行缓存验证或者重新获取资源。只有在服务器返回强校验器或者弱校验器时才会进行验证。

ETags

作为缓存的一种强校验器,ETag 响应头是一个对用户代理(User Agent, 下面简称UA)不透明(译者注:UA 无需理解,只需要按规定使用即可)的值。对于像浏览器这样的HTTP UA,不知道ETag代表什么,不能预测它的值是多少。如果资源请求的响应头里含有ETag, 客户端可以在后续的请求的头中带上 If-None-Match 头来验证缓存。

Last-Modified 响应头可以作为一种弱校验器。说它弱是因为它只能精确到一秒。如果响应头里含有这个信息,客户端可以在后续的请求中带上 If-Modified-Since 来验证缓存。

当向服务端发起缓存校验的请求时,服务端会返回 200 ok表示返回正常的结果或者 304 Not Modified(不返回body)表示浏览器可以使用本地缓存文件。304的响应头也可以同时更新缓存文档的过期时间。

强缓存

我们可以通过Expires或者Cache-Control 来实现强缓存的设置

我们一般只需要设置html为强缓存-1就可以了

  • Cache-Control
server {
        listen       8001;
        server_name  localhost;

+       add_header Cache-Control no-store;

        location / {
            ...
        }
				...
    }
  • Expires
server {
        listen       8001;
        server_name  localhost;

+       expires -1;

        location / {
            ...
        }
				...
    }

协商缓存

我们可以通过Cache-Control 来实现强缓存的设置

我们可以通过以下方法,将html设置为协商缓存,js,css等静态资源,都设置表明可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存

server {
        listen       8001;
        server_name  localhost;

        location / {
+           if ($request_uri ~* .*[.](js|css|map|jpg|png|svg|ico)$) {
+               #非html缓存1个月
+               add_header Cache-Control "public, max-age=2592000";
+           }

+           if ($request_filename ~* ^.*[.](html|htm)$) {
+               #html文件使用协商缓存
+               add_header Cache-Control "public, no-cache";
+           }
            ...
        }
				

	...
    }

从下图可以看出,html每次都会去拿服务器最新的资源,js,css走了协商缓存

image.png