http cache and nginx configurati

2018-09-06  本文已影响0人  我真的是昵称啊

概述

浏览器http缓存,既是网页静态资源服务性能优化的一把利器,也是无数web开发者在工作之初谈之色变的一大难题。
在开发过程中我们极力避免缓存,但在生产环境中,我们又在想尽办法利用缓存。
所以了解浏览器缓存的机制,是一个优秀开发者绕不开的重要基础知识。
各种缓存的命中与否,说到底不过是几个与之相关的http header数据的匹配与校验。如果了解了每个相关header的意义与关系,那么就能将缓存策略运用自如。


http_caches.jpg user-action2.png

cache type

  1. 200 from cache
    直接从本地缓存(有的文章称之为强缓存)中获取响应,返回200状态,chrome网络面板中的size项显示 from cache
    最快速,最省流量,因为根本没有向服务器发送请求。
cache-from-cache.png
  1. 304 Not Modified
    在本地缓存没有命中的情况下,请求头中发送一定的校验数据到服务端,校验成功后服务器返回304 not modified表示资源未被修改,浏览器从本地缓存中获取响应,这种缓存方式通常被称为协商缓存。
    快速,发送的数据很少,只返回一些基本的响应头信息,数据量很小,不发送实际响应体。


    cache-304.png
  2. 200 OK
    以上两种缓存全都失败,服务器返回完整响应。
    没有用到缓存,相对最慢。

本地缓存

本地缓存的使用,相当于基于之前服务器的response header,浏览器认为当前的缓存可以直接使用,于是不再与服务器进行任何交互而直接使用缓存的过程。

与本地缓存命中相关的http header:

  1. Pragma

这个是http1.0时代的遗留产物,该字段被设置为no-cache时(实际上现有的RFC标准标明只有这个可选值),会告知浏览器禁用本地缓存,即每次都向服务器发送请求。

  1. Expires
    http1.0时代用来启用本地缓存的字段,expires值对应一个形如Thu, 31 Dec 2037 23:55:55 GMT的格林威治时间,告诉浏览器缓存实现的时刻,如果还没到该时刻,标明缓存有效,无需发送请求。
    但是这个方式有个很明显的问题,就是浏览器与服务器的时间是不能保证一致的,如果时间差距较大,那么会影响缓存管理的结果。
        # example:
        # vim /etc/nginx/sites-available/your_server_config
        location ~* \.(?:css|js)$ {
          expires 1d;
          access_log off;
          add_header Cache-Control "public";
        }

这告诉浏览器,在date+1号之前,可以直接使用该文本的缓存副本。但是,可能会因为服务器和客户端的GMT时间不同,会有一定的bug。 所以,这里只提议在长时间缓存的情况下使用。否则,应该选择Cache-Control

  1. Cache-Control
    Cache-Control 头在 HTTP/1.1 规范中定义,取代了之前用来定义响应缓存策略的头(例如 Expires)。当前的所有浏览器都支持 Cache-Control,因此,使用它就够了。
    不过,目前大部分服务器都会将两者添加上,因为HTTP规定,如果Cache-Control和expires同时出现的话,expires会默认被覆盖掉。 此时,返回的响应码不再是304(文件未改动),而是200(资源成功访问).
    http1.1针对Expires时间不一致的问题,采取了一个十分聪明的设定,运用Cache-Control来告知浏览器缓存过期的时间间隔而不是时刻,那么即使具体时间不一致,也不影响缓存的管理。

Cache-Control允许的值如下:

no-store
禁止浏览器缓存响应,通常一些非常隐私的数据会启用这个值

no-cache
不允许直接使用本地缓存,必须先发起请求和服务器协商。

max-age=seconds
告知浏览器该响应本地缓存有效的最长期限,以秒为单位。
其他可选值不常见,以后遇到再补充

public: 共有缓存,可被缓存代理服务器缓存,比如CDN
private: 私有缓存,不能被共有缓存代理服务器缓存,可被用户的代理缓存如浏览器。

max-age=[秒]:表示在这个时间范围内缓存是新鲜的无需更新。类似Expires时间,不过这个时间是相对的,而不是绝对的。也就是某次请求成功后多少秒内缓存是新鲜的。

s-maxage=[秒]:类似max-age, 除了仅应用于共享缓存(如代理)。

no-cache:这里不是不缓存的意思,只是每次在使用缓存之前都强制发送请求给源服务器进行验证,检查文件该没改变(其实这里和ETag/Last区别不大)

no-store:就是禁止缓存,不让浏览器保留缓存副本

must-revalidate:告诉浏览器,你这必须再次验证检查信息是否过期, 返回的代号就不是200而是304了。

proxy-revalidate:类似must-revalidate,除了只能应用于代理缓存。

比如,这里我可以设置Cache-Control为:

#Response Headers
Cache-Control:private, max-age=0, must-revalidate

该文件是一个私有文件,只能被浏览器缓存,而不能被代理缓存。max-age标识该缓存立即过期,其实和no-cache实际上区别不大. 然后must-revalidate告诉浏览器,你必须给我再验证文件过没过期,比如接下来可能会验证Last-Modified或者ETag.如果没有过期则使用本地缓存. 其实上面可以直接等同于:

#Response Headers
Cache-Control:private,no-cache

这三个字段主要用来告知浏览器的本地缓存管理策略,优先级为Pragma > Cache-Control > Expires。

协商缓存

当浏览器没有命中本地缓存,如本地缓存过期或者响应中声明不允许直接使用本地缓存,那么浏览器肯定会发起请求。

在http缓存模型中,即使浏览器向服务器发起请求,服务器也不一定要返回整个资源的实体内容。而可以返回协商结果:“浏览器,我的资源没有修改过,你可以直接使用你的本地缓存”。

很显然,服务器要判断浏览器的缓存是否可用,那么必须浏览器告诉服务器一些自己缓存的信息,所以协商缓存相关的header字段,必然是成对出现的。

Last-Modified

格式:Response Headers : Last-Modified: Tue, 08 Nov 2016 01:50:36 GMT

告诉浏览器资源的最后修改时间,相当于对资源进行了版本管理,至于这个时间怎么生成的,那是服务器的事儿,不在这里讨论。

得知资源的最后修改时间后,客户端会将这个信息提交到服务器做检查,如果服务器验证出最后修改时间是一致的,那么表示该资源没有修改过,可以返回304状态。

浏览器请求头中标记最终修改时间的header字段:

Request Headers : If-Modified-Since: Thu, 31 Mar 2016 07:07:52 GMT

ETag

通常情况下,服务器默认是打开Etag的,但是为了防止配置文件不正确,关闭了Etag,这时候,就需要你对对配置文件做一些设置。 这里我以Nginx为例: 打开ngnix.conf文件,检查是否有以下语句:()

etag off;
more_set_headers -s 404 -t 'ETag';
more_clear_headers 'Etag';

即使我没有讨论服务器怎么生成最终修改时间,也可以相见,这个模式会存在不准确的问题:如果资源明明没有改变,但是Last-Modified发生了变化,那么就会返回整个资源实体。
针对这个问题,http1.1还推出了ETag字段,服务器会根据某种计算方式(常见的如md5)给出一个标识符,这个标识符其实标记的是资源的实际内容。

格式:Response Headers : ETag:"58212f6c-22f23"

检测过程与Last-Modified类似,浏览器请求头中标记ETag的字段:

Request Headers : If-None-Match:"58212f6c-22f23"

协商缓存.png

浏览器对缓存的影响

注意观察浏览器行为的开发者很容易发现,输入url访问与f5刷新,各个资源的请求速度好像不太相同。

常见的浏览器会将访问行为分为3种:

具体的实现方式可以想见的是发送不同的请求头~~

缓存策略的选择

对大多数站点来说,以下内容是非常适合缓存的:

普通不变的图像,如logo,图标等
js、css静态文件
可下载的内容,媒体文件
这些文件很少改变,适合长时间强缓存。

以下内容是做缓存时需要注意的,建议主要使用协商缓存的:

HTML文件
经常替换的图片
经常修改的js、css文件
其中,js、css文件可以通过md5修改文件名的方式改变url来失效缓存,
即在文件内容变化后将main.95d21235.css改为main.1bcbf5de.css,由于url变化,所以不存在缓存的问题。

以下内容从来都不应该使用缓存:

用户隐私等敏感数据
经常改变的api数据接口
其中,后台rest api数据接口的如果需要引入缓存策略,必须要进行比较谨慎的规划,
将频繁改变的接口与基本不变的接口区分,并且在应用服务器中实现Last-Modified/ETag的生成机制以保证缓存不会造成错误的结果。

从这里延伸出去的话,理想情况下,一切网络资源都应该尽可能选择不同策略的缓存,但考虑到开发的成本与难度,这在现实中很难发生,因此应该尝试设置一些明智的缓存策略(最常见的就是给大量的静态图片设置缓存),以在长期缓存和站点改变的需求间达到平衡。

nginx配置缓存策略

  1. 强缓存

    • add_header指令
     Syntax:         add_header name value [always];
     Default:    
     Context:        http, server, location, if in location
    
    • expires
    Syntax:         expires [modified] time;
                        expires epoch | max | off;
    Default:    
                        expires off;
    Context:      http, server, location, if in location
    
        # example:
        # vim /etc/nginx/sites-available/your_server_config
        location ~* \.(?:css|js)$ {
          expires 1d;
          access_log off;
          add_header Cache-Control "public";
        }
    

expires为负值时,表示Cache-Control: no-cache; 当为正或者0时,就表示Cache-Control: max-age=指定的时间(秒); 当为max时,会把Expires设置为 “Thu, 31 Dec 2037 23:55:55 GMT”, Cache-Control 设置到 10 年;

通过expires设置过期时间为一天,此时,服务器会根据当前的时间,(此处2018-09-06设置的)加上一天.同时添加Expires和Cache-Control头部标签。 即,得到的Response Header为:

Expires: Fri, 07 Sep 2018 07:09:30 GMT
Cache-Control: max-age=86400 //24*60*60

(HTTP规定,如果出现max-age和expires,则max-age默认覆盖掉expires) 当expires为负数表示no-cache,正数或零表示max-age=time。 如果你不想缓存,可以直接设置:

expires -1;  //永远过期,Cache-Control: no-cache

详细可以直接参阅:nginx配置

  1. 协商缓存
Syntax:        etag on | off;
Default:        etag on;
Context:       http, server, location
vim /etc/nginx/nginx.conf
http {
        etag on;
        ##
        # Other Settings
        ##
}
etag on;

扩展

##设置no-cache
//Nginx
expires -1;
//cache-control
Cache-Control:no-cache

##设置max-age=0
//Nginx
expires 0;
//cache-control
Cache-Control:max-age=0

##设置其他头部
//nginx
add_header  Cache-Control "no-cache";
add_header  Pragma no-cache;

上面说的基本上是服务器的响应头,那在浏览器的Request headers里存在cache-control代表什么呢? 当请求头中有:Cache-Control: max-age=0,表示缓存需要进行验证(ETag||Last-Modified),如果缓存未过期,则可以使用。 当请求头中有:Cache-Control: no-cache,表示浏览器只能获取最新的文件。 和Response Header中的no-store相对应。

组合拳法之缓存策略

上面介绍的last/ETag/Expires/Cache都是HTTP协议的缓存策略。当然,缓存不止这一种,比如在HTML 4.0中定义的某些meta也可以实现自定义缓存的

<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Expires" content="0" />

但,实际情况是,这些meta只能在file:// 本地文件中使用,如果是服务器则默认被覆盖。现在目前主流的就是使用HTTP1.1协议缓存 不过我们一般都不会单独使用某一项。 但是,组合之后他们的效果是怎样的呢?

清除缓存

来源统计之 Referrer 操作

Referrer 头里面会带上本次请求发送的 domain,比如:

Referer: https://now.qq.com/qq/market/index.html

不过,并不是所有的资源都会带上 referrer 头,比如,HTML 资源。默认情况下,HTML 的发送一般会遵循 No Referrer When Downgrade 的策略,即仅当发生协议降级(如 HTTPS 页面引入 HTTP 资源,从 HTTPS 页面跳到 HTTP 等)时不发送 Referrer 信息。。而这个策略的定义,主要根据 referrerPolicy 这个请求头来定制。其基本属性值为:

no-referrer: 不发送 referrer 头。
no-referrer-when-downgrade[default]: 当发生协议降级时采用,(如 HTTPS 页面引入 HTTP 资源,从 HTTPS 页面跳到 HTTP 等)。
origin: 值发送 host
origin-when-crossorigin: 在跨域的时候只发送 host,其余是完整 url。
unsafe-url: 都发送 referrer 信息。

referrerPolicy 内容设置可以通过 Response 中的 Referrer-Policy 来确定。它可以手动指定 referrer 策略。基本内容为:

Referrer-Policy: referrer no-referrer|no-referrer-when-downgrade|origin|origin-when-cross-origin|unsafe-url;

不过,在发送 HTML 的时候并不保证会遵循这个策略,应该 HTML 文档的发起 是 没有任何响应头来做参考的。所以,可以通过 HTML 文档中的 meta 标签来指定发送的请求头

<meta name="referrer" content="no-referrer|no-referrer-when-downgrade|origin|origin-when-crossorigin|unsafe-url">

不过,上述的设置对于纯粹从浏览器输出网址发送 HTML 请求的情况都是无效的。其默认都是 no-referrer-when-downgrade 策略。

上一篇下一篇

猜你喜欢

热点阅读