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

24

我最近在Rails应用程序堆栈中添加了一个Varnish实例。 Varnish在其默认配置下可以使用Cache-Control头来阻止缓存某个资源,如下所示:

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和用户的浏览器都缓存了某个资源。该资源从Varnish中正确地清除,但是浏览器不会尝试再次请求它,除非到达max-age。

因此,我想知道是否应该完全使用'expires_in'和Varnish的组合?我可以在Varnish前端的Nginx或Apache实例中过滤Cache-Control头文件,但这似乎很奇怪。

有人能够启示我吗?

问候 Felix


我也遇到了同样的问题。你找到解决方案了吗? - uwe
1个回答

15

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

问题在于只有一个Cache-Control属性,它是为客户端浏览器(私有缓存)和/或代理服务器(共享缓存)设计的。如果你不希望第三方代理缓存你的内容,并希望每个请求都由你的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中有一个默认的TTL为600秒,并为明确在更改时显式清除的页面定义较长的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头部。

3
感谢您提供详细的回答。我们通过将 Cache-Control 标头更改为 Cache-Control: max-age=0 s-maxage=86400, public=true 的方式解决了问题,这样浏览器就不会缓存该资源,但像 Varnish 这样的共享缓存可以缓存。这样做有问题吗? - GeorgieF
4
您也可以使用s-maxage。但是,如果您的用户在透明代理后面(某些ISP仍在使用),他们也将看到缓存版本长达24小时。如果需要这样做,可以使用s-maxage。如果不需要,则应该在VCL中从响应中beresp.unset缓存控制标头。 - Ketola
谢谢您关于此事的信息。我们一定会重构代码,并根据您的解决方案进行更改。 - GeorgieF
3
当然。s-maxage 明显是最简单的方法。确保你在 vcl_deliver 中将 Cache-Control 头中的 s-maxage 删除,以防止下游缓存使用它。你可以使用 regsub 来过滤它。如果在后端同时设置了 s-maxage 和 max-age,则可以获得非常灵活的系统,允许你为 Varnish 和客户端设置不同的 TTL,避免在 VCL 代码中硬编码 TTL。 - Martijn Heemels

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接