洞察探索open banking如何通过小程序容器技术助力金融企业实现数据安全和数字化转型
1097
2022-09-27
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 | 缓存资源, 但是在指定时间(单位为秒)后缓存过期 |
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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~