Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Feature Request: Leverage Browser Caching #134

Open
raamdev opened this Issue · 24 comments

8 participants

@raamdev
Owner

Google PageSpeed Insights Analysis often recommends to site owners that they "leverage browser caching".

Setting an expiry date or a maximum age in the HTTP headers for static resources instructs the browser to load previously downloaded resources from local disk rather than over the network.
Leverage browser caching

Several Quick Cache users have inquired about this and I think it would be a good idea to add this feature to Quick Cache Pro.

Requested here:
http://wordpress.org/support/topic/how-to-leverage-browser-caching-1
http://wordpress.org/support/topic/google-pagespeed-insights-suggests-leverage-browser-caching

@KTS915

I have often found that recommendation on Pingdom too, so this sounds like a good idea to me!

@jaswsinc
Owner

I agree, so long as the site owner knows what they are doing. Currently, QC doesn't impact static content on the site at all; i.e. images, JS, CSS, etc. That is, the site continues to leverage browser caching on those resources in whatever way it chooses to do so. However, QC does (by default) send no-cache headers to the browser for PHP (i.e. dynamically generated content) built by WordPress. You can turn this on/off if you prefer though. That's already possible.

Generally speaking, if it has a .php extension I send no-cache headers to the browser; whether I am in WordPress or not; and whether I have QC running or not. There are some exceptions, but if the underlying HTML is being generated dynamically I don't want folks caching that HTML content; because it's always subject to change dynamically.

Remember, when QC sends a no-cache header, that doesn't impact static resources, it only impacts the HTML that loads them. In other words, the benefit of sending a no-cache header when serving content generated via PHP, far outweighs the very tiny performance hit there IMO. This is what allows your site to remain dynamic.

Imagine a user that visits Page A for the first time while logged-in. Their browser caches that version of the content. Now they log out of the site. The next time they visit Page A they will still see the same page they saw while logged into the site, because that's the version of the content that their browser cached first. Should it still be the same? No! Might it have their username at the top? Or maybe a link that says "logout"? ; even though they are no longer logged-in. See how this might create confusion?

Of course, that's just one scenario. On a site that generates content dynamically, there could be a few other scenarios similar to this one, where having one variation in the cache, might result in a visitor NOT seeing what you intended for them to see on a return visit. This is particularly hairy when your site operates on sessions or other client-side cookie values in one way or another.

All of that said, if getting every ounce of speed possible is more important that maintaining the integrity of the site itself (i.e. to be able to dynamically alter the HTML output via PHP when you need to); then you can certainly allow a browser to cache dynamically generated content. That's fine if you know exactly what you're doing. It would help to reduce the overall number of HTTP connections that your server deals with. It's just important to realize the impact of such a decision.

@raamdev
Owner

@jaswsinc Thank you for sharing your thoughts. That's definitely a very important point.

I wasn't under the impression that this GitHub issue was referring to caching HTML in the browser.

This is about allowing browser caching for all other resources that are more likely to be static, e.g., JS and CSS files, image files, and any other binary files that are unlikely to change. See this overview description from the Google PageSpeed Leverage Browser Caching recommendations page.

Browser caching for static resources can save a user time if they visit your site more than once. Caching headers should apply to all cacheable static resources, not just a small subset (such as images). Cacheable resources include JS and CSS files, image files, and other binary object files (media files, PDFs, etc.). In general, HTML is not static and shouldn't be considered cacheable by default. You should consider what caching policy would work well for your site’s HTML.

@raamdev
Owner

What I'm thinking is a section of Quick Cache that makes a recommendation for what should be inserted into their main .htaccess file, and perhaps even allows them to have Quick Cache insert it for them.

Here's an example of what that might look like:

## EXPIRES CACHING ##
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access 1 year"
ExpiresByType image/jpeg "access 1 year"
ExpiresByType image/gif "access 1 year"
ExpiresByType image/png "access 1 year"
ExpiresByType text/css "access 1 month"
ExpiresByType text/html "access 1 month"
ExpiresByType application/pdf "access 1 month"
ExpiresByType text/x-javascript "access 1 month"
ExpiresByType application/x-shockwave-flash "access 1 month"
ExpiresByType image/x-icon "access 1 year"
ExpiresDefault "access 1 month"
</IfModule>
## EXPIRES CACHING ##
@jaswsinc
Owner

@raamdev I see, thank you. Here is what I use in Apache to achieve this.

# Browser Caching

FileETag MTime Size

<IfModule expires_module>
    ExpiresActive on
    ExpiresDefault "access plus 1 week"
</IfModule>
@jaswsinc
Owner

What I'm thinking is a section of Quick Cache that makes a recommendation for what should be inserted into their main .htaccess file, and perhaps even allows them to have Quick Cache insert it for them.

I agree with you on that. Great idea!

@jaswsinc
Owner

Note the default value of FileETag is FileETag INode MTime Size. It's good to remove the INode from this. See: http://www.geekride.com/configure-remove-etags-apache-http-optimize-site/

@raamdev
Owner

Note the default value of FileETag is FileETag INode MTime Size. It's good to remove the INode from this. See: http://www.geekride.com/configure-remove-etags-apache-http-optimize-site/

Very good to know. Thanks for the tip! :)

@jaswsinc
Owner

nd perhaps even allows them to have Quick Cache insert it for them.

I agree that we could certainly do this for them. I don't see any harm in doing so. So long as it's optional.

@KTS915

Just a couple of comments as a user, not a programmer:

  1. What you both have come up with as the best way of approaching this sounds very helpful. Thank you!

  2. When the feature is included in a future version of QC, perhaps it would be a good idea to explain what this feature is for, and what it is not for (along the lines of Jason's initial explanation of potential pitfalls) so as to avoid users like me getting the wrong idea.

For example, I would think that anyone wanting to cache content for logged-in users should really be using that specific feature in QC Pro (especially if you're able to get s2Member to permit that without causing other issues).

Thanks again!

@raamdev
Owner

@KTS915 Thank you VERY much for the feedback. :) That's very helpful.

@raamdev
Owner

I found the following example configuration from @jaswsinc in my notes that I'm copying here for reference:

FileETag none
<IfModule mod_headers.c>
    Header unset etag
</IfModule>

<IfModule mod_expires.c>
    ExpiresActive on
    ExpiresByType text/plain "access plus 1 week"
    ExpiresByType text/css "access plus 1 week"
    ExpiresByType text/javascript "access plus 1 week"
    ExpiresByType application/javascript "access plus 1 week"
    ExpiresByType application/x-javascript "access plus 1 week"
    ExpiresByType image/svg+xml "access plus 1 week"
    ExpiresByType image/gif "access plus 1 week"
    ExpiresByType image/png "access plus 1 week"
    ExpiresByType image/ico "access plus 1 week"
    ExpiresByType image/x-icon "access plus 1 week"
    ExpiresByType image/jpg "access plus 1 week"
    ExpiresByType image/jpe "access plus 1 week"
    ExpiresByType image/jpeg "access plus 1 week"
    ExpiresByType font/truetype "access plus 1 week"
    ExpiresByType application/x-font-ttf "access plus 1 week"
    ExpiresByType font/opentype "access plus 1 week"
    ExpiresByType application/x-font-otf "access plus 1 week"
    ExpiresByType application/font-woff "access plus 1 week"
    ExpiresByType application/vnd.ms-fontobject "access plus 1 week"
    ExpiresByType application/x-shockwave-flash "access plus 1 week"
    ExpiresByType application/x-httpd-php-source "access plus 1 week"
</IfModule>

See also, more information on leveraging browser caching: http://gtmetrix.com/leverage-browser-caching.html

@jaswsinc
Owner

@raamdev Since I originally posted that example, I spent some time going back through and trying to bring my own configurations up-to-date and also tried to simplify things just a bit further.


# Browser Caching
FileETag MTime Size

<IfModule expires_module>
    ExpiresActive on
    ExpiresDefault "access plus 1 week"
</IfModule>

Instead of disabling ETags, which actually help to alleviate the burden on a server, we can leave them enabled; and just exclude the INode. This way when caching is allowed, a browser can also use ETags to further optimize itself.

Instead of needing to worry about individual MIME types, we can throw a simple default Expires header of access plus 1 week (or whatever you think is best there). This way the configuration is not dependent upon the MIME types configured on a given server either.

So what does that mean?

  • All files (by default) are cacheable; and the cache expires after one week.
  • If WordPress (or another plugin) wants to disallow caching for a request it can call upon nocache_headers() in the WordPress core to override the headers passed by the server based on the default expiration time of one week.

Outside of WordPress, the case would be the same. If dynamic content wants to override the default Expires header it can simply do so on it's own via PHP's header() function.


In a case where a site owner wants to get more specific they could add new entries for various MIME types of their choosing. Anyway, just my thoughts. You might prefer to provide the example with a full list of MIME types. I think either way would be fine. Just wanted to point out that it doesn't necessarily need to be as complicated as my original example.

    ExpiresByType image/gif "access plus 1 week"
    ExpiresByType image/png "access plus 1 week"
    ExpiresByType image/ico "access plus 1 week"
@raamdev
Owner

All files (by default) are cacheable; and the cache expires after one week

But wouldn't that mean if WordPress (or maybe another WordPress plugin) did not explicitly override the default Expires header that HTML would be cached? For a dynamic application like WordPress, I would think that would nearly always be undesirable.

It's my understanding that by explicitly defining what file types should be cached we can avoid any possibility that dynamic HTML might accidentally get cached by the browser. Is that correct?

@jaswsinc
Owner

But wouldn't that mean if WordPress (or maybe another WordPress plugin) did not explicitly override the default Expires header that HTML would be cached? For a dynamic application like WordPress, I would think that would nearly always be undesirable.

That's correct. I don't see that as a bad thing though. It really just simplifies thing a bit.

If the content does not state that it's NOT to be cached, it's left up to a browser anyway. For instance, if you access any file on the server and that file does not set an Expires header or a Cache-Control header; the browser just decides what to do on it's own; and that is generally to cache the content; since we didn't state anything to contrary.

In short, if a file should not be cached, headers should be sent to a browser. The following just establishes the default base that we start from.

# Browser Caching
FileETag MTime Size

<IfModule expires_module>
    ExpiresActive on
    ExpiresDefault "access plus 1 week"
</IfModule>
@raamdev
Owner

if a file should not be cached, headers should be sent to a browser.

Got it. Thank you for explaining that. I haven't looked into how WordPress Core handles this, but I assume it's pretty consistent with sending the necessary no-cache headers when it should, correct?

@jaswsinc
Owner

pretty consistent with sending the necessary no-cache headers when it should, correct?

Yes, WordPress seems to do a good job of controlling the cache behavior on it's own; when it comes to the back end. Anything in the admin sends nocache_headers() from the WP core. Other static files on the server are generally left as-is; so a default server configuration would dictate how long those should be cached for. Following the example above, we might allow static resources (i.e. anything that doesn't explicitly say that it can't be cached) to be cached for up to 1 week at a time.

If we leave ETags enabled too, then a browser may actually cache it even longer than this; so long as the server says 304 Not Modified. Thus, the reason we want to leave ETags enabled. This works to further reduce load on the server when it comes to static resources.

@raamdev
Owner

Perfect. I agree then that it makes sense to keep things simple and leave the ETags enabled. In the configuration panel, I can maybe have an expandable section that explains how things can be tweaked further if setting expire times for specific file types is desired (or just link out to a wiki article).

@shubhamchaudhary

+1 for this request.

@raamdev
Owner

Noting that I had 1 request today via Quick Cache Pro support for this request.

@brunowego

+1 for this request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.