How to add browser caching rules to your .htaccess file on Apache

One of the items benchmarking tools like GTMetrix and Google PageSpeed Insights rate your site for, is whether it leverages browser caching or not.

Often these tools reveal more pressing matters to concern yourself with, that have a larger impact on the benchmarking score. Practices like optimizing images and minifying CSS, HTML, and JavaScript, for instance.

On the other hand, I’m not a server-side expert, but I found that achieving a better score on these tools – and getting a slightly faster page load – by adding just a couple of lines to your .htaccess file to enable browser caching, is quite simple.

Here’s how I did it.

How does browser caching work

Your site contains files that change often and some that change not so often. Some files hardly – if ever – change.

When you place an image on your site, that image will almost never change. Same with video files. JavaScript files, like those that your theme or plugins use, change more often. PDF files also tend to change more often.

CSS files generally change most often, especially when you’re a designer or developer. Visitors need a fresh copy of those on their computer, since CSS files change the look of your site.

When a person visits your site, their browser downloads these files and saves them on their hard drive. Plus, for each of these files, the browser sends a separate request to the server. This all adds to the load of your server, slowing it down.  Especially image files and video add to the load.

With browser caching turned on, not all of these files will be requested and downloaded again when your visitor clicks on another page on your site that uses the same files, or revisits your site later. Their computer uses the stored files instead of downloading the same files again. This practice helps your site load faster for that visitor, since these downloaded files and file requests don’t need to be transferred over the internet again.

By the way, the effect of browser caching on page loading times depends greatly on the size of your pages. When your site is image-heavy, and the visitor’s browser needs to download large files from your site, the effect is greater than when your site takes a more minimalist approach.

Now, adding browser caching doesn’t totally solve the issue these tools report. Some external .js files, like Google Analytics and Google Fonts, do not allow caching, but your site will score better with browser caching enabled.

It took me a long time figuring out the best caching setup for my WordPress sites on Apache servers. And I am open to changes in the future when technology changes.

How do you add browser caching to your site’s setup

Browser caching rules tell the browser whether a file only needs refreshing once a year, once a month, or whatever time you decide is appropriate for your situation. Refreshing, in this case, means downloading it again.

The rules that tell the browser when to download which files are set in the .htaccess file in the root of your site.

Note: messing around in your .htaccess file is a great way to bring your site down. Always, always, always make sure you download a copy of your working .htaccess file before you change anything in the file.

Using Expires headers for browser caching

Until recently, I used the recommended settings GTmetrix prescribes on their site.

For these settings, GTmetrix takes the approach that:

  • image and video files change least often, so these will download again after a year since the last downloaded version
  • JavaScript, and PDF files change a little more frequently, so these will download after a month
  • CSS files change most often, so need downloading after a week
  • all other files will download again after a month

Caveat: I noticed some problems with forms on a site using a general one-month expiry, so I took that rule out.

# BROWSER CACHING USING EXPIRES HEADERS
<IfModule mod_expires.c>
    ExpiresActive On

    # Images
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType image/x-icon "access plus 1 year"
  
    # Video
    ExpiresByType video/mp4 "access plus 1 year"
    ExpiresByType video/mpeg "access plus 1 year"

    # CSS, JavaScript
    ExpiresByType text/css "access plus 1 week"
    ExpiresByType text/javascript "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"

    # Others
    ExpiresByType application/pdf "access plus 1 month"
    ExpiresByType application/x-shockwave-flash "access plus 1 month"
</IfModule>

The solution GTmetrix prescribes uses Expires headers that specify expiration times for specific file extensions to the browser. This way, the browser knows when to download a fresh copy of a specific file.

When added to the .htaccess file, these rules turn on browser caching without any problems.

However, there is another – and apparently better – way.

Using Cache-Control headers for browser caching

I recently came across this article on using Cache-Control headers for browser caching.

Cache-Control headers replaces Expires headers is more flexible (accepts more directives), and is currently the preferred method for browser caching.

Since all modern browsers support Cache-Control headers, you should only need to add these lines to your .htaccess file:

# BROWSER CACHING USING CACHE-CONTROL HEADERS
<ifModule mod_headers.c> 
    # One year for image and video files
    <filesMatch ".(flv|gif|ico|jpg|jpeg|mp4|mpeg|png|svg|swf|webp)$">
        Header set Cache-Control "max-age=31536000, public"
    </filesMatch>

    # One month for JavaScript and PDF files
    <filesMatch ".(js|pdf)$">
        Header set Cache-Control "max-age=2592000, public"
    </filesMatch>

    # One week for CSS files
    <filesMatch ".(css)$">
        Header set Cache-Control "max-age=604800, public"
    </filesMatch>
</ifModule>

As you can see, I’m using the exact same file expiration settings in these Cache-Control headers as in the Expires headers example. All max-age values are in seconds, e.g. one month equals:

60 (seconds in a minute) x 60 (minutes in an hour) x 24 (hours in a day) x 30 (average number of days in a month) = 2592000

According to the Google Developers site, Cache-Control headers are all we need:

The Cache-Control header was defined as part of the HTTP/1.1 specification and supersedes previous headers (for example, Expires) used to define response caching policies. All modern browsers support Cache-Control, so that’s all you need.

Google Developers website

Fail-safe method for setting browser caching in your .htaccess file

However, Dutch hosting provider Byte describes using both Expires headers and Cache-Control headers on their servers, to ensure proper browser caching on servers that may not support one. This might, for now, be the fail-safe method of choice, and what I’m using for my own and my client’s sites:

# BROWSER CACHING USING EXPIRES HEADERS
<IfModule mod_expires.c>
    ExpiresActive On
  
    # Images
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType image/x-icon "access plus 1 year"
  
    # Video
    ExpiresByType video/mp4 "access plus 1 year"
    ExpiresByType video/mpeg "access plus 1 year"

    # CSS, JavaScript
    ExpiresByType text/css "access plus 1 week"
    ExpiresByType text/javascript "access plus 1 month"
    ExpiresByType application/javascript "access plus 1 month"

    # Others
    ExpiresByType application/pdf "access plus 1 month"
    ExpiresByType application/x-shockwave-flash "access plus 1 month"
</IfModule>

# BROWSER CACHING USING CACHE-CONTROL HEADERS
<ifModule mod_headers.c> 
    # One year for image and video files
    <filesMatch ".(flv|gif|ico|jpg|jpeg|mp4|mpeg|png|svg|swf|webp)$">
        Header set Cache-Control "max-age=31536000, public"
    </filesMatch>

    # One month for JavaScript and PDF files
    <filesMatch ".(js|pdf)$">
        Header set Cache-Control "max-age=2592000, public"
    </filesMatch>

    # One week for CSS files
    <filesMatch ".(css)$">
        Header set Cache-Control "max-age=604800, public"
    </filesMatch>
</ifModule>

Disabling ETag: yes or no

The Byte article also recommended disabling ETag by adding these lines to the .htaccess file:

# REMOVE AND DISABLE ETAG
Header unset ETag
FileETag None
# Remove Last-Modified
Header unset Last-Modified

However, when I did that, GTmetrix told me to specify a cache validator, stating:

All static resources should have either a Last-Modified or ETag header. This will allow browsers to take advantage of the full benefits of caching.

GTmetrix

… and punished me in their validation. For a client’s site, it cost me about 4% on their Page Speed Score, so I took it out quickly!

Teaches me not to mess with a good thing when I don’t know exactly what I’m doing 馃槼

What are your recommended browser caching settings, and why do you do it the way you do it? Add your thoughts and/or settings in the comments below.

Resources

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of