首页 > 其他分享 >HTTP缓存机制详解

HTTP缓存机制详解

时间:2023-04-13 14:35:58浏览次数:26  
标签:Control 缓存 浏览器 请求 Cache Modified 详解 HTTP

目录

什么是 HTTP 缓存

HTTP 缓存可以说是HTTP性能优化中简单高效的一种优化方式了,缓存是一种保存资源副本并在下次请求时直接使用该副本的技术,当 web 缓存发现请求的资源已经被存储,它会拦截请求,返回该资源的拷贝,而不会去源服务器重新下载。

一个优秀的缓存策略可以缩短网页请求资源的距离,减少延迟,节省网络流量,并且由于缓存文件可以重复利用,降低网络负荷,提高客户端响应。

缓存策略

在阐述HTTP不同缓存策略之前,我们需要知道用户刷新/访问行为 的手段分成三类:

  • 在URI输入栏中输入然后回车/通过书签访问

    这种情况下,默认使用浏览器的缓存机制,如果缓存有对应的资源,则直接从缓存中读取,否则会向服务器请求资源。

  • F5/点击工具栏中的刷新按钮/右键菜单重新加载(忽略浏览器缓存,重新请求并获取相应)

    这种情况下,浏览器发送的请求头中加入了Cache-Control: no-cache,这表示客户端不想使用缓存数据。服务器收到请求后,会忽略客户端的缓存,重新生成响应返回给客户端。

  • Ctl+F5 (完全不使用HTTP缓存,会删掉浏览器的缓存内容重新下载)

    这种情况下,浏览器发送的请求头中加入了Cache-Control: no-store,这表示客户端不希望在任何情况下使用缓存,所有数据都必须从服务器获取。服务器收到请求后,不仅忽略客户端的缓存,而且还不会在服务器端缓存响应数据。

不同的刷新手段,会导致浏览器使用不同的缓存策略,我们下面会分析到

HTTP 缓存主要是通过请求和响应报文头中的对应 Header 信息,来控制缓存的策略。

响应头中相关字段为ExpiresCache-ControlLast-ModifiedEtag

HTTP缓存的类型很多,根据是否需要重新向服务器发起请求来分类包括两种:强制缓存对比缓存

假设浏览器有一个缓存数据库用于本地缓存,先看看浏览器请求资源的情况:

强制缓存

在浏览器已经缓存数据的情况下,使用强制缓存去请求数据的流程是这样的:

从流程图可以看到,强制缓存,在缓存数据未失效的情况下,可以直接使用缓存数据,不需要再请求服务器,那么浏览器是如何判断缓存数据是否失效呢?

对于强制缓存来说,响应header中会有两个字段来标明失效规则(Expires/Cache-Control):

Expires

ExpiresHTTP1.0 的产物了,现在默认浏览器均默认使用 HTTP 1.1,所以它的作用基本忽略。但是很多网站还是对它做了兼容。它的值为服务端返回的到期时间,即下一次请求时,请求时间小于服务端返回的到期时间,直接使用缓存数据。

但有一个问题是到期时间是由服务端生成的,如果客户端时间跟服务器时间不一致,这就会导致缓存命中的误差。

HTTP 1.1 的版本,ExpiresCache-Control 替代。

Cache-Control

Cache-Control 是最重要的规则。常见的取值有 privatepublicno-cachemax-ageno-store,默认为 private

  • max-age:用来设置资源(representations)可以被缓存多长时间,单位为秒;

  • s-maxage:和 max-age 是一样的,不过它只针对代理服务器缓存而言;

  • public:指示响应可被任何缓存区缓存;

  • private:只能针对个人用户,而不能被代理服务器缓存;

  • must-revalidate:表示客户端在使用本地缓存之前,必须先向服务器验证缓存是否过期。如果验证结果是缓存过期,客户端必须使用服务器端的新的响应数据。

  • no-cache:强制客户端直接向服务器发送请求,也就是说每次请求都必须向服务器发送。服务器接收到请求,然后判断资源是否变更,是则返回新内容,否则返回304,未变更。这个很容易让人产生误解,使人误以为是响应不被缓存。实际上Cache-Control: no-cache是会被缓存的,只不过每次在向客户端(浏览器)提供响应数据时,缓存都要向服务器评估缓存响应的有效性。服务器端根据是否过期来决定是否使用本地缓存

  • no-store:禁止一切缓存(这个才是响应不被缓存的意思)。表示浏览器不能存储任何与这个请求和响应相关的资源。这个选项的目的是防止敏感的信息被暴露在公共计算机上。

注:以上每个选项都是可选的,Web服务器或代理可以自由选择是否使用这些选项中的任何一个或多个选项。另外,Cache-Control字段中可以通过逗号间隔的方式指定多个选项,这些选项可以共同存在,例如:Cache-Control: private, max-age=3600。

举个例子,比如一个资源响应头是:

cache-control: public, max-age= 31536000 

那么这个资源会被缓存31536000秒(365天),在365天内再次请求这条数据,都会直接获取缓存数据库中的数据,直接使用。

那么我们试试再次访问资源,会有以下的响应:

可以看到HTTP状态码是200,Size这个字段显示:disk cache,说明浏览器确实走了强制缓存,没有再跟浏览器交互。

我们上面说了,不同的访问/刷新手段,会使浏览器使用不同的缓存策略,要让浏览器走强制缓存对请求方式有一个要求: 在URI输入栏中输入然后回车/通过书签访问。

对比缓存

在浏览器已经缓存数据的情况下,使用对比缓存去请求数据的流程是这样的。

对比缓存,顾名思义,需要进行比较判断是否可以使用缓存。

浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给浏览器,浏览器将二者备份至缓存数据库中。

当浏览器再次请求数据时,浏览器将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。

举个例子,第一次访问:

第二次访问:

对于对比缓存来说,响应 header 中会有两个字段来标明规则

Last-Modified / If-Modified-Since

服务器响应请求时,会告诉浏览器一个告诉浏览器资源的最后修改时间:Last-Modified,浏览器之后再请求的时候,会带上一个头:If-Modified-Since,这个值就是服务器上一次给的 Last-Modified 的时间,服务器会比对资源当前最后的修改时间,如果大于If-Modified-Since,则说明资源修改过了,浏览器不能再使用缓存,否则浏览器可以继续使用缓存,并返回304状态码。

Etag / If-None-Match(优先级高于Last-Modified / If-Modified-Since)

服务器响应请求时,通过Etag头部告诉浏览器当前资源在服务器的唯一标识(生成规则由服务器决定),浏览器再次请求时,就会带上一个头If-None-Match,这个值就是服务器上一次给的Etag的值,服务器比对一下资源当前的Etag是否跟If-None-Match一致,不一致则说明资源修改过了,浏览器不能再使用缓存,否则浏览器可以继续使用缓存,并返回304状态码。

看个例子:第一次请求,服务器的响应头包含了:

第二次请求,浏览器的请求头:

优先级

HTTP缓存机制中,不同头部字段的优先级如下:

Cache-Control > Pragma > Expires

Cache-ControlExpires在功能上有所重叠,但Cache-Control更加灵活且功能更加强大,因此,建议使用Cache-Control来设置缓存头部信息。Pragma可忽略,因为它本质上已经被Cache-Control取代。

Nginx 缓存配置

默认设置

当 Nginx 服务器配置没有显式设置缓存策略时,它会遵循以下默认规则:

  • 对于静态文件(如图片、CSS 文件和 JavaScript 文件),Nginx 会自动启用浏览器缓存,默认过期时间为 1 天(即 expires 24h)。

  • 对于动态页面(如 PHP)和动态内容(如用户上传的文件),默认情况下会发送 Cache-Control: no-cache 头信息,告诉客户端不要缓存这些内容。这是出于安全考虑和避免网络攻击。

  • 对于静态文件,如果没有在配置文件中指定缓存时间,则 Nginx 会根据文件的 MIME 类型生成 “ETag” 和 “Last-Modified” 头信息。在后续的客户端请求中,任何发送同样的 “ETag” 或 “If-Modified-Since” 头信息的条件请求将会被服务器视为缓存命中。

如果你想要显式配置 ETagLast-Modified 头信息,你可以在 Nginx 的配置文件的 location 中添加下列内容:

location / {
    # Always send Last-Modified and ETag
    add_header Last-Modified $date_gmt;
    add_header ETag $etag;

    # Use weak ETag (more compatible with caches)
    etag on;
    etag_hash on;

    # Other caching directives
    expires 1h;
    add_header Cache-Control 'private, max-age=3600';
}

这个示例将 ETagLast-Modified 添加到 HTTP 响应头中,同时还启用了 weak ETag,并设置了其他缓存指令,如 expiresCache-Control

需要注意的是,配置 ETagLast-Modified 并不是必需的,因为 Nginx 默认就已经为静态文件启用了协商缓存和条件请求。但是,根据具体情况,显式配置这些缓存头可能会更好地符合你的需要。

显示设置

在 Nginx 配置文件中配置缓存策略。具体而言,你应该在 sites-available 路径下找到与你的网站有关的配置文件(通常是 default 或 your-site)。

以下是一些示例 Nginx 配置片段,你可以添加到你的 Nginx 配置文件中,以启用 HTTP 缓存:

# Cache static files for one year
location ~* \.(jpg|jpeg|png|gif|css|js|ico)$ {
    expires 365d;
}

# Make sure requests have a proper cache-control header set
location / {
    add_header Cache-Control "no-cache, must-revalidate";
}

# Cache other assets for 60 minutes
location ~* \.(pdf|xml|ogg|mp4|wav)$ {
    expires 60m;
}

以上示例配置将静态文件的缓存时间设置为一年、其他资源的缓存时间设置为一小时。

注意,在修改 Nginx 配置文件之前,务必备份原文件并进行测试以确保不会对网站造成任何不良影响。

浏览器缓存策略

Chrome 浏览器是根据一些策略来决定资源是应该存储在内存缓存还是硬盘缓存中的。以下是一些在 Chrome 中常见的缓存策略:

  • Cache-Control 头信息:当服务器返回响应时,可以通过设置 Cache-Control 头信息来控制该资源在客户端的缓存行为。例如,通过设置 max-age 标识符为一个较小的值,可以让客户端将该资源缓存到内存中。而通过设置 max-age 标识符为较大的值,则可以让客户端将该资源缓存到硬盘中。

  • 文件大小:当文件大小小于给定的阈值时,Chrome 可以将其缓存到内存中。这是因为内存读写速度更快,可以加速相应的加载速度。

  • 频繁访问:当资源被频繁访问时,Chrome 会将其缓存到内存中,以加快其加载速度。

  • 手动控制:Chrome 浏览器提供了一些工具,允许用户手动控制缓存策略。例如,可以在开发者工具中配置资源的读写策略、缓存大小等参数。

需要注意的是,这些策略是可以被修改和重写的,如果服务器返回了新的、带有不同缓存控制信息的响应,浏览器可能会重新评估缓存策略,决定将资源存储到另一个位置。

总结

我们再看一下HTTP缓存的一个总概流程图:

  • HTTP缓存主要分强制缓存和对比缓存
  • 强制缓存的 HTTP 相关头部 Cache-ControlExipres(HTTP1.0),浏览器直接读本地缓存,不会再跟服务器端交互,状态码 200。
  • 对比缓存的 HTTP 相关头部 Last-Modified / If-Modified-SinceEtag / If-None-Match (优先级比Last-Modified / If-Modified-Since高),每次请求需要让服务器判断一下资源是否更新过,从而决定浏览器是否使用缓存,如果是,则返回304,否则重新完整响应。
  • 对于静态文件Nginx 会自动启用浏览器缓存,默认过期时间为 1 天(即 expires 24h);对于动态页面默认情况下会发送 Cache-Control: no-cache 头信息。
  • 浏览器会把小于给定阈值频繁访问缓存生命期短的文件存储在内存,其他的存储在硬盘里

标签:Control,缓存,浏览器,请求,Cache,Modified,详解,HTTP
From: https://www.cnblogs.com/xiaobingch/p/17314690.html

相关文章

  • oracle中if/else的三种实现方式详解
    oracle中if/else的三种实现方式详解1、标准sql规范1、单个IFIFv=...THENENDIF;2、IF...ELSEIFv=...THENELSEt....;ENDIF;3、多个IFIFv=...THENELSIFv=...THENt...;ENDIFL注意:多个IF的是'ELSIF'不是'ELSEIF'2、decode函数DECODE(VALUE,IF......
  • 2.1万字,30张图详解操作系统常见面试题(收藏版)
    耗时两周,新版的操作系统常见知识点/问题总结总算搞完了,手绘了30多张图。大家可以用来复习操作系统或者准备操作系统面试。对于大部分公司的面试来说基本够用了,不过,像腾讯、字节这种大厂的面试还是要适当深入一些。这篇文章总结了一些我觉得比较重要的操作系统相关的问题比如用......
  • AlertDialog(对话框)详解
    本节继续给大家带来是显示提示信息的第三个控件AlertDialog(对话框),同时它也是其他Dialog的的父类!比如ProgressDialog,TimePickerDialog等,而AlertDialog的父类是:Dialog!另外,不像前面学习的Toast和Notification,AlertDialog并不能直接new出来,如果你打开AlertDialog的源码,会发现构造方法......
  • Redis——缓存穿透、缓存击穿、缓存雪崩
    一、缓存穿透1、含义缓存穿透是指查询一个缓存中和数据库中都不存在的数据,导致每次查询这条数据都会透过缓存,直接查库,最后返回空。2、解决方案1)缓存空对象就是当数据库中查不到数据的时候,我缓存一个空对象,然后给这个空对象的缓存设置一个过期时间,这样下次再查询该数据的时候,......
  • JDBC详解(韩顺平教程)
    JDBC一、原理示意图二、前提步骤IDEA导入MySQL的jdbc驱动,并操作数据库-打点-博客园(cnblogs.com)三、JDBC编写步骤:用法1:packageHsp.JDBC;​importcom.mysql.jdbc.Driver;importjava.sql.Connection;importjava.sql.SQLException;importjava.sql.Statement;......
  • C# Byte数组转化String详解(c# byte转化为string)
    C#Byte数组转化String详解(c#byte转化为string)原文链接:https://www.zhiu.cn/148955.htmlC#编程过程中将Byte数组转化String是咱们常常碰到的问题,那么怎么处理C#Byte数组转化String呢?那么咱们来看看详细的涉及到的办法以及关于怎么处理C#Byte数组转化String的评论。FCL得许多......
  • JavaWeb之Servlet详解(以及浏览器调用 Servlet 流程分析图)
    Servlet1.什么是ServletServlet(java服务器小程序)他是由服务器端调用和执行的(一句话:是Tomcat解析和执行)他是用java语言编写的,本质就是Java类他是按照Servlet规范开发的(除了tomcat->Servletweblogic->Servlet)功能强大,可以完成几乎所有的网站功能2.开发......
  • HTTP协议和MQTT协议对比谁更好
    HTTP协议是Web联网的基础,也是应用最为广泛和流行的协议,HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。MQTT是一种基于发布/订阅模式的“轻量级”通讯协议,该协议构建于TCP/IP协议上,由IB......
  • VUE中的插槽使用详解
     <!--什么是插槽?  *插槽就是子组件中的提供给父组件使用的一个占位符,插槽显不显示、怎样显示是由父组件来控制的,而插槽在哪里显示就由子组件来进行控制。  一般用slot标签来表示插槽要渲染的位置,slot的用法可以分为三类,分别是默认插槽、具名插槽、作用域插槽 ......
  • 记一次提取Shaft缓存
    引子aka,pixiv在境内没办法正常访问,于是有了一众第三方App能够实现直连pixiv的操作。通过绕过SNI审查的方式实现直连,其中我所提到的Shaft就是其中之一。ref:https://github.com/CeuiLiSA/Pixiv-Shaft/issues/243ref:https://github.com/CeuiLiSA/Pixiv-Shaft背景介绍有一台......