美文网首页
http cache and nginx configurati

http cache and nginx configurati

作者: 我真的是昵称啊 | 来源:发表于2018-09-06 19:22 被阅读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种:

    • 地址栏输入URL或书签访问
      按照正常策略使用缓存

    • 按照正常策略使用缓存
      按照正常策略使用缓存

    • Ctrl+F5
      跳过强缓存与协商缓存,直接加载资源实体

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

    缓存策略的选择

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

    普通不变的图像,如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. 协商缓存
    • ETag
    Syntax:        etag on | off;
    Default:        etag on;
    Context:       http, server, location
    
    • Last-Modified
      add_header指令,默认开启
    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 策略。

    相关文章

      网友评论

          本文标题:http cache and nginx configurati

          本文链接:https://www.haomeiwen.com/subject/fquxgftx.html