Nginx 浏览器之HTTP缓存的那些事

网友投稿 1097 2022-09-27

Nginx 浏览器之HTTP缓存的那些事

Nginx 浏览器之HTTP缓存的那些事

缓存是提升用户访问速度,节省带宽,减轻服务器压力的必经之道。

下面都是针对的Http 1.1来说明,HTTP缓存都是针对浏览器客户端,其他第三方客户端不考虑。

什么是浏览器缓存

简单来说,浏览器缓存就是把一个已经请求过的Web资源(如html,图片,js)拷贝一份副本储存在浏览器中。缓存会根据进来的请求保存输出内容的副本。当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求(当然还有304的情况)。缓存是根据url来处理的,只要url不一样就是新的资源。

浏览器HTTP执行机制

浏览器对于请求资源,拥有一系列成熟的缓存策略。只要有相应的缓存响应头(要求缓存),浏览器客户端都会对资源缓存一份,当然缓存响应头也有优先级的。

缓存模式

浏览器缓存可以分为两种模式,​​强缓存​​​和​​协商缓存​​。

强缓存(无HTTP请求,无需协商)直接读取本地缓存,无需跟服务端发送请求确认,memory cache或者from disk cache ,不同浏览器返回的信息不一致的)。对应的​​Http header​​有:

Cache-ControlExpires

location / { expires 1h; #一个小时后过期 root html; index index.html index.htm; } [root@localhost ~]# date -uTue May 26 01:54:10 UTC 2020 [root@localhost ~]# curl 192.168.179.99 -IHTTP/1.1 200 OKServer: nginx/1.16.0Date: Tue, 26 May 2020 01:52:15 GMTContent-Type: text/htmlContent-Length: 612Last-Modified: Mon, 25 May 2020 07:36:21 GMTConnection: keep-aliveETag: "5ecb7575-264"Expires: Tue, 26 May 2020 02:52:15 GMT #Date时间和Expires时间间隔刚好是一个小时Cache-Control: max-age=3600 #设置一小时过期可以看到max-age即为3600,正好是一个小时Accept-Ranges: bytes

协商缓存(有HTTP请求,需协商)浏览器虽然发现了本地有该资源的缓存,但是不确定是否是最新的,于是想服务器询问,若服务器认为浏览器的缓存版本还可用,那么便会返回304(Not Modified) header​​有:

Last-ModifiedETag

流程图

流程图只考虑了200和304的状态码,其他异常状态码不考虑。

缓存相关的Http Header

​​Http Header​​​包括请求头和响应头,header​​,浏览器还有web服务一般都会考虑进去。

Http Header

描述

Cache-Control

指定缓存机制,优先级最高

Pragma

data-id="t31e458f-n7E5WMYP" style="height: 30px;">

Expires

data-id="t31e458f-8elZhX3q" style="height: 30px;">

Last-Modified

data-id="t31e458f-c5MLLpJS" style="height: 30px;">

ETag

唯一标识请求资源的字符串,会覆盖Last-Modified

Cache-Control

浏览器缓存里, Cache-Control是金字塔顶尖的规则, 它藐视一切其他设置, 只要其他设置与其抵触, 一律覆盖之.

不仅如此, 它还是一个复合规则,包含多种值,同时在请求头和响应头都可设置(基本都可以)。下面列举了常用的​​Cache-Control​​用法。

Cache-Control

描述

no-store

请求和响应都不缓存

no-cache

相当于​​max-age:0​​,即资源被缓存,但是缓存立刻过期, 同时下次访问时强制验证资源有效性

max-age

缓存资源, 但是在指定时间(单位为秒)后缓存过期

Expires

​​1.1版开始,使用​​Cache-Control: max-age=秒​​替代,这样就不存在不一致问题了。

Last-Modified

​​Last-Modified​​​和​​If-Modified-Since​​是一对的。

当浏览器第一次请求一个url时,服务器端的返回状态码为200,同时​​HTTP响应头​​会有一个Last-Modified标记着文件在服务器端最后被修改的时间。

浏览器第二次请求上次请求过的url时,浏览器会在​​HTTP请求头​​添加一个If-Modified-Since的标记,用来询问服务器该时间之后文件是否被修改过。

但是​​Last-Modified​​是ETag的计算方式把最后修改时间也算进去了(所有这个算是弱ETag)。

Nginx ETag计算方式:计算页面文件的最后修改时间,将文件最后修改时间的秒级Unix时间戳转为16进制作为etag的第一部分 计算页面文件的大小,将大小字节数转为16进制作为etag的第二部分。

ETag有两种类型:

强ETag强ETag值,不论实体发生多么细微的变化都会改变其值。强ETag表示形式:​​"22FAA065-2664-4197-9C5E-C92EA03D0A16"​​。弱ETag弱 ETag 值只用于提示资源是否相同。只有资源发生了根本改变产 生差异时才会改变 ETag 。这时,会在字段值最开始处附加​​W/​​。弱ETag表现形式:​​W/"22FAA065-2664-4197-9C5E-C92EA03D0A16"​​。

ETag和If-None-Match是一对:

当浏览器第一次请求一个url时,服务器端的返回状态码为200,同时HTTP响应头会有一个Etag,存放着服务器端生成的一个序列值。

浏览器第二次请求上次请求过的url时,浏览器会在HTTP请求头添加一个If-None-Match的标记,用来询问服务器该文件有没有被修改。

一般网站都会把​​Last-Modified​​​和​​ETag​​一起用,同时对对比,两个条件都满足了才会返回304。

Nginx实例

注意:nginx在设置​​Cache-Control:max-age=xxx​​​和​​expires​​时,谷歌访问后,后面会变成200(from memory cache),然后就造成了文件修改后无法更新的问题。这个也很好解决,只要设置过期时间为0,这样就一定不是强缓存,就不存在这些问题。

下面的例子​​Http Header​​只需要关注上面提到的相关字段。

ETag和Last-Modified例子

Nginx默认开启​​ETag​​​和​​Last-Modified​​​。由于Nginx ETag可知,​​ETag​​​比​​Last-Modified​​​多了文件大小比较,理论上有ETag就可以不用​​Last-Modified​​​,但是为了兼容off;

​​Last-Modified​​关闭如下(没有找到具体关闭方式,只好在响应头中直接赋值为空):

add_header 'Last-Modified' '' always;

这些配置,可以随便设置在不同层级,{ listen 80; server_name localhost; location / { #定义自己的web服务根目录 root /Users/Sam/ #默认访问文件夹时,访问index.html或者index.htm文件 index index.html index.htm; location ~* \.(jpg|jpeg|gif|bmp|png|js|css){ #nginx在没有设置Cache-Control:max-age=xxx和expires时, #谷歌访问后,后面会变成200(from memory cache), #然后就造成了文件修改后无法更新的问题。 #这个很好解决,只要设置过期时间为0,这样就一定不是强缓存,就不存在这些问题 expires 0s; } }}

然后在根目录​​index.html​​​中引入​​./test.js​​​文件,然后访问​​index.html​​。

首次访问,返回200,然后再次访问才会返回304。然后无论如何修改,只要文件被保存(即使内容不变),再次访问浏览器返回200,然后再次访问返回304(内容没修改)。

首次访问 Http Header(先清理缓存,才算首次访问)

#通用的headerRequest URL: Method: GETStatus Code: 200 OKRemote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=0Connection: keep-aliveContent-Encoding: gzipContent-Type: application/javascriptDate: Thu, 27 Sep 2018 03:21:00 GMTETag: W/"5bab7b36-15"Expires: Thu, 27 Sep 2018 03:21:00 GMTLast-Modified: Wed, 26 Sep 2018 12:27:34 GMTServer: nginx/1.10.1Transfer-Encoding: chunked#请求头Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostReferer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

第二次访问 Http Header(无修改)

#通用的headerRequest URL: Method: GETStatus Code: 304 Not ModifiedRemote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=0Connection: keep-aliveDate: Thu, 27 Sep 2018 03:23:22 GMTETag: "5bab7b36-15"Expires: Thu, 27 Sep 2018 03:23:22 GMTLast-Modified: Wed, 26 Sep 2018 12:27:34 GMTServer: nginx/1.10.1#请求头Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostIf-Modified-Since: Wed, 26 Sep 2018 12:27:34 GMTIf-None-Match: W/"5bab7b36-15"Referer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

第三次访问 Http Header(有修改)

#通用的headerRequest URL: Method: GETStatus Code: 200 OKRemote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=0Connection: keep-aliveContent-Encoding: gzipContent-Type: application/javascriptDate: Thu, 27 Sep 2018 03:25:14 GMTETag: W/"5bac4d98-15"Expires: Thu, 27 Sep 2018 03:25:14 GMTLast-Modified: Thu, 27 Sep 2018 03:25:12 GMTServer: nginx/1.10.1Transfer-Encoding: chunked#请求头Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostIf-Modified-Since: Wed, 26 Sep 2018 12:27:34 GMTIf-None-Match: W/"5bab7b36-15"Referer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

第四次访问 Http Header(无修改)

#通用的headerRequest URL: Method: GETStatus Code: 304 Not ModifiedRemote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=0Connection: keep-aliveDate: Thu, 27 Sep 2018 03:26:46 GMTETag: "5bac4d98-15"Expires: Thu, 27 Sep 2018 03:26:46 GMTLast-Modified: Thu, 27 Sep 2018 03:25:12 GMTServer: nginx/1.10.1#请求头Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostIf-Modified-Since: Thu, 27 Sep 2018 03:25:12 GMTIf-None-Match: W/"5bac4d98-15"Referer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

Cache-Control 和 Expires

server { listen 80; server_name localhost; location / { #定义自己的web服务根目录 root /Users/Sam/ #默认访问文件夹时,访问index.html或者index.htm文件 index index.html index.htm; location ~* \.(jpg|jpeg|gif|bmp|png|js|css){ #设置30秒缓存有效期 expires 30s; } }}

然后在根目录​​index.html​​​中引入​​./test.js​​​文件,然后访问​​index.html​​。

首次访问,浏览器会返回200,然后再次访问才会返回200(from memory cache),然后30秒后过期访问,如果文件没修过过,会返回304,否则返回200,继续访问如果没过期期,返回200(from memory cache)。

具体请看上面的浏览器缓存流程图。

首次访问 Http Header(先清理缓存,才算首次)

#通用的headerRequest URL: Method: GETStatus Code: 200 OKRemote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=30Connection: keep-aliveContent-Encoding: gzipContent-Type: application/javascriptDate: Thu, 27 Sep 2018 03:28:03 GMTETag: W/"5bac4d98-15"Expires: Thu, 27 Sep 2018 03:28:33 GMTLast-Modified: Thu, 27 Sep 2018 03:25:12 GMTServer: nginx/1.10.1Transfer-Encoding: chunked#请求头Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostReferer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

第二次访问 Http Header(不能过期,上面设置的是30秒,要在上次访问的30秒内再次访问)

#通用的headerRequest URL: Method: GETStatus Code: 200 OK (from memory cache)Remote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=30Connection: keep-aliveContent-Encoding: gzipContent-Type: application/javascriptDate: Thu, 27 Sep 2018 03:28:03 GMTETag: W/"5bac4d98-15"Expires: Thu, 27 Sep 2018 03:28:33 GMTLast-Modified: Thu, 27 Sep 2018 03:25:12 GMTServer: nginx/1.10.1Transfer-Encoding: chunked#请求头Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostReferer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

第三次访问 Http Header(需要过期,上面设置的是30秒,上一次访问等待30秒后访问)

#通用的headerRequest URL: Method: GETStatus Code: 304 Not ModifiedRemote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=30Connection: keep-aliveDate: Thu, 27 Sep 2018 03:32:44 GMTETag: "5bac4d98-15"Expires: Thu, 27 Sep 2018 03:33:14 GMTLast-Modified: Thu, 27 Sep 2018 03:25:12 GMTServer: nginx/1.10.1#请求头Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostIf-Modified-Since: Thu, 27 Sep 2018 03:25:12 GMTIf-None-Match: W/"5bac4d98-15"Referer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

第四次访问 Http Header(不能过期,上面设置的是30秒,要在上次访问的30秒内再次访问修改后的文件)

访问获取的还是旧文件,文件虽然修改了,但是浏览器直接缓存中获取,没发出请求,无法获取最新的内容。

#通用的headerRequest URL: Method: GETStatus Code: 200 OK (from memory cache)Remote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=30Connection: keep-aliveContent-Encoding: gzipContent-Type: application/javascriptDate: Thu, 27 Sep 2018 03:28:03 GMTETag: W/"5bac4d98-15"Expires: Thu, 27 Sep 2018 03:28:33 GMTLast-Modified: Thu, 27 Sep 2018 03:25:12 GMTServer: nginx/1.10.1#请求头Transfer-Encoding: chunkedAccept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostReferer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

第五次访问 Http Header(需过期,在第四次访问后等待30秒访问修改后的文件)

访问后获取到了最新文件。

#通用的headerRequest URL: Method: GETStatus Code: 200 OKRemote Address: 127.0.0.1:80Referrer Policy: no-referrer-when-downgrade#响应头Cache-Control: max-age=30Connection: keep-aliveContent-Encoding: gzipContent-Type: application/javascriptDate: Thu, 27 Sep 2018 03:36:51 GMTETag: W/"5bac4fcc-15"Expires: Thu, 27 Sep 2018 03:37:21 GMTLast-Modified: Thu, 27 Sep 2018 03:34:36 GMTServer: nginx/1.10.1Transfer-Encoding: chunked#请求头Accept: */*Accept-Encoding: gzip, deflate, brAccept-Language: zh-CN,zh;q=0.9,en;q=0.8Connection: keep-aliveHost: localhostIf-Modified-Since: Thu, 27 Sep 2018 03:25:12 GMTIf-None-Match: W/"5bac4d98-15"Referer: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36

不需要缓存的场景

缓存是会提升访问速度、节省带宽、减轻服务器压力,但是也不能滥用,否则会出现一些意想不到的问题。图片、css、js等资源文件一般都是需要缓存的,但是像接口数据等数据会变动的memory cache)。html文件最好设置过期时间为0,强制跟服务器做文件修改对比(当然具体场景具体分析)。因为js文件和css文件是可以使用版本做控制或者随机数。js代码版本迭代更新这个场景不是不需要缓存,而是更新了js代码版本,但是如果用户还在缓存期内,就会导致页面出错。这种情况就需要进行js类库版本控制,如:

升级到到2.0.0时,我们需要把代码改成

这样就不会访问到缓存的jquery.js。缓存是根据url来处理的,只要url不一样就是新的资源。

前后端使用ajax请求接口数据

解决方式

Url添加随机数这种情况,是前端做处理。请求头添加​​Cache-Control: no-cache​​​为了兼容no-cache​​​,Cache-Control的选项有很多,具体如何选择,看场景。前端或者服务端都可以处理。

一些说明

做一些补充说明。

memory cache 和 disk cache

这个两个说明是谷歌浏览器的状态码附加提示语,内存缓存和磁盘缓存。其实很简单,只要页面在网页上打开访问,然后不关闭,刷新的一定是​​from memory cache​​​,而页面关闭在打开一定是​​from disk cache​​。这是浏览器自身的缓存手段,磁盘缓存一定会有一份备份的,然后页面访问的时候也会在内存中缓存一份,这样刷新当前页面就不会读取硬盘,而是直接内存中获取(减少访问磁盘的次数)。

不同浏览器的一些表现差异

下面是针对已经强缓存,同时访问文件无修改的情况下,不同刷新方式返回的状态码总结(mac系统):

这里只说明下可能会遇到的疑惑。浏览器软件自身的处理方式,跟http缓存挂不上钩,我们也无法处理。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Nginx 一文总结Nginx负载均衡
下一篇:Shell Reids自动配置脚本 redis自带脚本参考参考
相关文章

 发表评论

暂时没有评论,来抢沙发吧~