在启用 CloudFlare 的情况下利用浏览器缓存

Leveraging browser cache with CloudFlare enabled

这是我的 .htaccess 文件的片段:

# ------------------------------------------------------------------------------
# | ETag removal                                                               |
# ------------------------------------------------------------------------------

# Since we're sending far-future expires headers (see below), ETags can
# be removed: http://developer.yahoo.com/performance/rules.html#etags.

# `FileETag None` is not enough for every server.
<IfModule mod_headers.c>
    Header unset ETag

  <filesMatch "\.(ico|jpe?g|png|gif|swf)$">
    Header set Cache-Control "max-age=2592000, public"
  </filesMatch>
  <filesMatch "\.(css)$">
    Header set Cache-Control "max-age=604800, public"
  </filesMatch>
  <filesMatch "\.(js)$">
    Header set Cache-Control "max-age=216000, public"
  </filesMatch>
  <filesMatch "\.(x?html?|php)$">
    Header set Cache-Control "max-age=600, private, must-revalidate"
  </filesMatch>
</IfModule>

FileETag None

# ------------------------------------------------------------------------------
# | Expires headers (for better cache control)                                 |
# ------------------------------------------------------------------------------

# The following expires headers are set pretty far in the future. If you don't
# control versioning with filename-based cache busting, consider lowering the
# cache time for resources like CSS and JS to something like 1 week.

<IfModule mod_expires.c>

    ExpiresActive on
    ExpiresDefault                                      "access plus 1 month"

  # CSS
    ExpiresByType text/css                              "access plus 1 year"

  # Data interchange
    ExpiresByType application/json                      "access plus 0 seconds"
    ExpiresByType application/xml                       "access plus 0 seconds"
    ExpiresByType text/xml                              "access plus 0 seconds"

  # Favicon (cannot be renamed!)
    ExpiresByType image/x-icon                          "access plus 1 week"

  # HTML components (HTCs)
    ExpiresByType text/x-component                      "access plus 1 month"

  # HTML
    ExpiresByType text/html                             "access plus 0 seconds"

  # JavaScript
    ExpiresByType text/javascript                       "access plus 1 year"
    ExpiresByType application/javascript                "access plus 1 year"
    ExpiresByType application/x-javascript              "access plus 1 year"

  # Manifest files
    ExpiresByType application/x-web-app-manifest+json   "access plus 0 seconds"
    ExpiresByType text/cache-manifest                   "access plus 0 seconds"

  # Media
    ExpiresByType audio/ogg                             "access plus 1 month"
    ExpiresByType image/gif                             "access plus 1 month"
    ExpiresByType image/jpeg                            "access plus 1 month"
    ExpiresByType image/jpg                            "access plus 1 month"
    ExpiresByType image/png                             "access plus 1 month"
    ExpiresByType video/mp4                             "access plus 1 month"
    ExpiresByType video/ogg                             "access plus 1 month"
    ExpiresByType video/webm                            "access plus 1 month"

  # Web feeds
    ExpiresByType application/atom+xml                  "access plus 1 hour"
    ExpiresByType application/rss+xml                   "access plus 1 hour"

  # Web fonts
    ExpiresByType application/font-woff                 "access plus 1 month"
    ExpiresByType application/vnd.ms-fontobject         "access plus 1 month"
    ExpiresByType application/x-font-ttf                "access plus 1 month"
    ExpiresByType font/opentype                         "access plus 1 month"
    ExpiresByType image/svg+xml                         "access plus 1 month"

</IfModule>

可以看出,我在适当的语法中有 Cache-Control 和 Expires。然而 Google PageSpeed 工具说我的 JS 没有利用浏览器缓存。有什么我想补充的吗?

我知道同时包含 Cache-Control 和 Expires Headers 不是好的做法。所以我删除了 Cache-Control 部分。当 运行 超过 Google 时,本地文件(目前不关心 third-party 外部文件)仍显示为未缓存。需要缓存的文件是http://www.peppyburro.com/sandboxassets/js/burroinline.js. The .htaccess in question is located at the document root, i.e. http://www.peppyburro.com/.htaccess。具有上述缓存片段的 .htaccess 也驻留在包含 burroinline.js[=36 的 /js 文件夹中=].

更让人困惑的是,https://www.giftofspeed.com/cache-checker/ 说我的文件正在按预期进行缓存,而 Google PageSpeed 和 GTMetrix 说它不是。

更新:看起来我的 CDN (CloudFlare) 与缓存问题有关,因为一旦我禁用 CF,缓存就会开始工作。我之所以这么说,是因为 Google PageSpeed 在缓存和 un-cached 之间随机交替,尽管我的 htaccess 没有任何变化!此外,GTMetrix 仍将文件显示为 un-cached。这是 header 在我的浏览器中的样子:

**General**
Request URL:http://peppyburro.com/sandboxassets/js/burroinline.js
Request Method:GET
Status Code:200 OK (from disk cache)
Remote Address:209.99.16.94:80
**Response Headers**
Accept-Ranges:bytes
Access-Control-Allow-Origin:*
Age:0
Cache-Control:max-age=216000, public
Content-Encoding:gzip
Content-Length:38611
Content-Type:application/javascript
Date:Wed, 01 Mar 2017 16:22:41 GMT
Expires:Thu, 01 Mar 2018 16:22:41 GMT
Last-Modified:Wed, 01 Mar 2017 02:18:53 GMT
Server:Apache Phusion_Passenger/4.0.10 mod_bwlimited/1.4 mod_fcgid/2.3.9
Vary:Accept-Encoding,User-Agent
Via:1.1 varnish-v4
X-Varnish:31524632

这是否意味着文件实际上正在被缓存?如果是这样,如何才能在 Google PageSpeed 和 GTMetrix 中始终如一地反映相同的内容?我怎样才能让它在启用 CF 的情况下工作?

PS:在共享主机上,我无法访问 httpd.conf

编辑: 答案最终是 CloudFlare 和 .htaccess 文件之间的冲突。对此 post 的评论讨论了此问题的故障排除和解决方案。

我运行这个资源通过pingdom的工具看看request/response是什么样子的。

https://tools.pingdom.com/#!/d8QPQx/http://www.peppyburro.com/sandboxassets/js/burroinline.js

实际上并没有被缓存。 header 设置为 no-cache。

"no-cache" indicates that the returned response can't be used to satisfy a subsequent request to the same URL without first checking with the server if the response has changed. As a result, if a proper validation token (ETag) is present, no-cache incurs a roundtrip to validate the cached response, but can eliminate the download if the resource has not changed.

来源:https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching

服务器的响应是:

Cache-Control public, max-age=216000

因为是javascript,所以最好延长一周或更长时间。此外,此处的响应是 public,而您的设置是

Header set Cache-Control "max-age=216000, private"

年龄正确,但能见度有差距

"public" vs. "private"

If the response is marked as "public", then it can be cached, even if it has HTTP authentication associated with it, and even when the response status code isn't normally cacheable. Most of the time, "public" isn't necessary, because explicit caching information (like "max-age") indicates that the response is cacheable anyway.

By contrast, the browser can cache "private" responses. However, these responses are typically intended for a single user, so an intermediate cache is not allowed to cache them. For example, a user's browser can cache an HTML page with private user information, but a CDN can't cache the page.

来源:https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching

我也看到一些 cloudflare (CDN) headers,标记它没有被缓存。通常,私人回复用于敏感内容。我会首先尝试将其设置为 public,但前提是您不关心敏感信息。

如果您担心敏感信息请将其设为私有

虽然我认为这可能是问题所在,但还有其他几个因素(以 CDN 为中心)也可能导致该问题。

Accept-Ranges:bytes
Access-Control-Allow-Origin:*
Age:0
Cache-Control:public, max-age=216000
CF-Cache-Status:MISS
CF-RAY:338d062cb1035a6e-BOS
Connection:Keep-Alive
Content-Type:application/javascript
Date:Wed, 01 Mar 2017 15:07:08 GMT
Expires:Sat, 04 Mar 2017 03:07:08 GMT
Last-Modified:Wed, 01 Mar 2017 02:18:53 GMT
Proxy-Connection:Keep-Alive
Server:cloudflare-nginx
Vary:Accept-Encoding
Via:1.1 varnish-v4
X-Varnish:18615326

这些是来自服务器的响应 header。它们在 CF (cloudflare) 缓存中包含一个 "MISS"。另外,这里的缓存控制也设置为public.

因此,我认为中间 CDN 可能会导致缓存问题。

如果您有任何额外的信息要提供(例如CDN/CloudFlare信息),我很乐意再看一下。