如何在 Rails 环境中使用 Cache-Control: max-age Header 来控制 Varnish 和浏览器?

Posted

技术标签:

【中文标题】如何在 Rails 环境中使用 Cache-Control: max-age Header 来控制 Varnish 和浏览器?【英文标题】:Howto control Varnish and a Browser using Cache-Control: max-age Header in a Rails environment? 【发布时间】:2012-05-04 07:35:54 【问题描述】:

最近我向 Rails 应用程序堆栈添加了一个 Varnish 实例。可以通过使用 Cache-Control Header 缓存某个资源来说服其默认配置中的清漆,如下所示:

Cache-Control: max-age=86400, public=true

我使用控制器中的 expires_in 语句实现了这一点:

def index
  expires_in 24.hours, public: true
  respond_with 'some content'
end

效果很好。我没想到的是,Cache-Control 标头也会影响浏览器。这导致了一个问题 - Varnish 和我的用户浏览器都缓存了某个资源。资源已从清漆中正确清除,但浏览器不会尝试再次请求它,除非达到 max-age。

所以我想知道我是否应该将“expires_in”与 Varnish 结合使用?我可以在 Varnish 前面过滤 nginx 或 Apache 实例中的 Cache-Control 标头,但这似乎很奇怪。

谁能赐教?

问候 费利克斯

【问题讨论】:

我遇到了同样的问题。您找到解决方案了吗? 【参考方案1】:

这实际上是一个非常好的和有效的问题,也是一个非常常见的反向代理问题。

问题在于只有一个 Cache-Control 属性,它是为客户端浏览器(私有缓存)和/或代理服务器(共享缓存)设计的。如果您根本不希望 3rd 方代理缓存您的内容,并且希望您的 Varnish(或 Rails 后端)处理每个请求,您必须从 Varnish 发送适当的 Cache-Control 标头。

修改后端发送的Cache-Control标头在https://www.varnish-cache.org/trac/wiki/VCLExampleLongerCaching详细讨论

您可以从两个不同的角度来解决问题。如果你想在 Rails 后端定义 max-age,例如为不同的对象指定不同的 TTL,你可以使用上面链接中描述的方法。

另一种解决方案是根本不从后端发送 Cache-Control 标头,而是在 varnish vcl_fetch() 中为对象定义所需的 TTL。这是我们采取的方法。

我们在 Varnish 中有一个 600 秒的默认 TTL,并为在进行更改时明确清除的页面定义更长的 TTL。这是我们当前的 vcl_fetch() 定义:

sub vcl_fetch 
  if (req.http.Host ~ "(forum|discus)") 
    # Forum pages are purged explicitly, so cache them for 48h
    set beresp.ttl = 48h;
  

  if (req.url ~ "^/software/") 
    # Software pages are purged explicitly, so cache them for 48h
    set beresp.ttl = 48h;
  

  if (req.url ~ "^/search/forum_search_results" ) 
    # We don't want forum search results to be cached for longer than 5 minutes
    set beresp.ttl = 300s;
  

  if(req.url == "/robots.txt") 
    # Robots.txt is updated rarely and should be cached for 4 days
    # Purge manually as required
    set beresp.ttl = 96h;
  

  if(beresp.status == 404) 
    # Cache 404 responses for 15 seconds
    set beresp.http.Cache-Control = "max-age=15";
    set beresp.ttl = 15s;
    set beresp.grace = 15s;
  

在我们的例子中,我们根本不从 Web 后端服务器发送 Cache-Control 标头。

【讨论】:

感谢您的详细回答。我们通过简单地将 Cache-Control 标头更改为: Cache-Control: max-age=0 s-maxage=86400, public=true 解决了这个问题,因此浏览器不会缓存资源,而是像 Varnish 那样共享缓存。有错吗? 您也可以使用 s-maxage。但是,如果您的用户使用透明代理(一些 ISP 仍在使用它们),他们也会看到缓存版本长达 24 小时。如果需要,可以使用 s-maxage。如果没有,那么您应该从 VCL 中的响应中删除缓存控制标头。我将编辑我的答案以提及 s-maxage。 感谢您提供相关信息。我们一定会根据您的解决方案对此进行重构和更改。 确实如此。 s-maxage 绝对是最简单的方法。确保从 vcl_deliver 中的 Cache-Control 标头中删除 s-maxage 以防止下游缓存使用它。您可以使用 regsub 对其进行过滤。如果您在后端同时设置 s-maxage 和 max-age,您将获得一个非常灵活的系统,允许您为 Varnish 和客户端设置不同的 TTL,同时避免在 VCL 代码中硬编码 TTL。

以上是关于如何在 Rails 环境中使用 Cache-Control: max-age Header 来控制 Varnish 和浏览器?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 i18n 和 rails 配置语言环境别名?

如何在 Rails 环境中使用 Cache-Control: max-age Header 来控制 Varnish 和浏览器?

如何随时检测内部的Rails环境

如何使用 @rails/webpacker 加载本地字体?

如何从 Rails 中访问 Rack 环境?

如何在 Ruby on Rails 中为我的开发和生产环境设置不同的 api 密钥?