Fix Leverage Browser Caching Warning
At some point in time when you are running speed tests with your website you will most likely encounter the all popular "leverage browser caching" warning in Google PageSpeed Insights. In this post, we are going to show you how to leverage browser caching in general as well as a couple of options on how to easily setup local analytics to fix this warning originating from Google's analytics.js
. We have also included a new way to more easily do this for those of you running WordPress.
What is the leverage browser caching warning?
The leverage browser caching warning is one that many web developers have likely come across when testing their page performance on popular speed testing tools. This warning relates to how browsers are instructed to cache static assets from your web server. If there are no browser caching rules in place or the expiry times are set too low, then this warning will be triggered.
As explained by the warning's details above, leveraging browser caching allows for the browser to retain certain assets and have them loaded directly from the local cache instead of retrieving them from the server for repeat visitors. This can drastically improve load times and overall helps provide visitors with a better user experience.
As we covered in our other post on Google PageSpeed Insights, a lot of people try and strive for that 100/100 score on Google PageSpeed Insights. Some do it because they are trying to speed up their site and others because a client is demanding they meet this metric (yes, this happens more than you think). It is important to take some time though and think about why we are trying to achieve that 100/100 score. Don't think of it solely from a metrics point of view. The whole reason Google developed PageSpeed Insights was as a guideline on best web performance practices to provide recommendations to optimize your site. And by following the guidelines hopefully, you will achieve a faster website. We don't recommend obsessing over this metric.
How to leverage browser caching with your server
There are a couple of ways to leverage browser caching on a server. The method you choose will primarily depend on which web server you're using (Nginx vs Apache). You may also notice in your speed tests that certain assets show an "expiration not specified" warning while others show an actual time value (e.g. 5 minutes).
In the example above, this means that your server doesn't have any browser caching rules defined for .png
files and the expiry time is only 5 minutes for .css
files. For static resources which do not change often, it is recommended to set an expiry time of at least 1 month (1 year is preferred).
Now, for how you want to define your leverage browser caching rules you have two options. You can use Cache-Control
headers or Expires
headers. It isn't required to use both and in fact using just Cache-Control
is the newer and usually recommended method. Although some still use both to satisfy certain speed test tools such as GTmetrix which continue to check for Expires headers. According to Google:
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 supportCache-Control
, so that's all you need.
The following sections show how to enable Expires
and Cache-Control
headers on both Apache and Nginx web servers.
Cache-Control
headers on Nginx
Add the following snippet to the inside of your server block which is usually located at /etc/nginx/site-enabled/default
.
location ~* \.(png|jpg|jpeg|gif)$ {
expires 365d;
add_header Cache-Control "public, no-transform";
}
location ~* \.(js|css|pdf|html|swf)$ {
expires 30d;
add_header Cache-Control "public, no-transform";
}
Cache-Control
headers on Apache
Add the following snippet to the top of your .htaccess
file.
<filesMatch ".(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">
Header set Cache-Control "max-age=2592000, public"
</filesMatch>
Expires
headers on Nginx
Add the following snippet to the inside of your server block which is usually located at /etc/nginx/site-enabled/default
.
location ~* \.(jpg|jpeg|gif|png)$ {
expires 365d;
}
location ~* \.(pdf|css|html|js|swf)$ {
expires 30d;
}
Expires
headers on Apache
Add the following snippet to the top of your .htaccess
file.
## 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 ##
Once implemented on your origin server, you can check if everything is working as expected by opening up Chrome DevTools, navigating to the Network tab and clicking on an asset. You should now see either the Cache-Control
, Expires
, or both HTTP Response headers.
Google Analytics leverage browser caching warning
If you've properly defined all of your browser caching headers on your origin server but are still receiving the "leverage browser caching" warning then this is likely related to Google's own analytics.js
script. Kind of ironic that their own optimization tools give us a warning. It is a valid warning as they set the cache time on their script very low. Most likely this is in case they push out any updates, they want everyone to get the new script within a short amount of time.
As you can see in the screenshot below on our test site, we got everything to 99/100, except for that leverage browser caching warning from Google Analytics script. To fix this we host their script locally.
There are other reasons why you might want to host local analytics besides fixing the PageSpeed Insights error. Don't expect to see huge performance gains by doing this, but you might see a little.
- By hosting locally you can ensure that their large script loads from your web server, or CDN, instead of having to reach out to Google.
- Instead of two HTTP requests to Google, you now only have one.
- When you host locally, you now have full control over browser caching, such as
Expires
andCache-Control
. - Take advantage of your single HTTP/2 connection.
Linking normally to Google Analytics
We ran some tests with WebPageTest. When we linked to Google's analytics.js
it seemed to always result in a larger DNS lookup time the first time around, as opposed to when we hosted locally.
Total load time: 1.576 s
Fully loaded: 1.740 s
Local analytics
We ran some tests with WebPageTest. When we hosted the analytics script locally, obviously it loads super fast from our same HTTP/2 single connection on our CDN. It then has to do the DNS lookup on the final HTTP request to Google, but the DNS lookup was always a lot less. Doing it this way also results in what we like to call, a nicer looking waterfall.
Total load time: 1.426 s
Fully loaded: 1.526 s
Again because this test is so small, your results might vary. Always do your own testing as each environment is different. But in our testing, we did see a slight improvement in performance.
How to fix GA leverage browser caching warning
Normally the way we would fix a leverage browser caching warning would be to simply add expires headers. However, since this is a third party script hosted on Google's servers we have no control over the headers. There are two different options below you can use to fix this warning.
Option 1
So a solution that does work is to grab a copy of Google's analytics.js
script, host it locally, and sync it periodically to ensure you are using the latest version. Again, please be aware that this is not officially supported by Google. Credits for some of the code and scripts below go to Daan van den Bergh, Matthew Horne, and Jørgen Nicolaisen. Follow the steps below.
The very first thing you need to do is grab a copy of the contents of Google's Analytics script here: google-analytics.com/analytics.js. We are using Universal Analytics. Create a new file, in our example we call ours
local-ga.js
, and paste the contents of Google's script into it.Upload the
local-ga.js
file to your web server where it is accessible. Permissions on this file need to be writable for everything to work correctly.Next, you need to update the Google Analytics tracking code on your website. Again, we are using the Universal tracking code. In our example below, you can see we have updated ours to point to our
local-ga.js
file, and or the copy that also resides on our CDN.<script> (function (i, s, o, g, r, a, m) { i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () { (i[r].q = i[r].q || []).push(arguments) }, i[r].l = 1 * new Date(); a = s.createElement(o), m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m) })(window, document, 'script', 'https://cdn.yourdomain.com/local-ga.js', 'ga'); ga('create', 'UA-xxxxxxx-x', 'auto'); ga('send', 'pageview'); </script>
That should now be working on your site. A quick way to test it is to launch your site it incognito mode and view real-time analytics. Ensure that tracking is working properly before proceeding.
The next step is to setup a script so that your local script is periodically updated with Google's hosted version. Otherwise, they might push out an update/change and the tracking of your traffic could stop reporting correctly. In our example we name ours
ga-update.php
.<? // script to update local version of Google analytics script // Remote file to download $remoteFile = 'https://www.google-analytics.com/analytics.js'; // For cPanel it will be /home/USERNAME/public_html/local-ga.js $localfile = 'ENTER YOUR ABSOLUTE PATH TO THE FILE HERE'; // Connection time out $connTimeout = 10; $url = parse_url($remoteFile); $host = $url['host']; $path = isset($url['path']) ? $url['path'] : '/'; if (isset($url['query'])) { $path .= '?' . $url['query']; } $port = isset($url['port']) ? $url['port'] : '80'; $fp = @fsockopen($host, '80', $errno, $errstr, $connTimeout ); if (!$fp) { // On connection failure return the cached file (if it exists) if (file_exists($localfile)) { readfile($localfile); } } else { // Send the header information $header = "GET $path HTTP/1.0\r\n"; $header .= "Host: $host\r\n"; $header .= "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6\r\n"; $header .= "Accept: */*\r\n"; $header .= "Accept-Language: en-us,en;q=0.5\r\n"; $header .= "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"; $header .= "Keep-Alive: 300\r\n"; $header .= "Connection: keep-alive\r\n"; $header .= "Referer: http://$host\r\n\r\n"; fputs($fp, $header); $response = ''; // Get the response from the remote server while ($line = fread($fp, 4096)) { $response .= $line; } // Close the connection fclose( $fp ); // Remove the headers $pos = strpos($response, "\r\n\r\n"); $response = substr($response, $pos + 4); // Return the processed response echo $response; // Save the response to the local file if (!file_exists($localfile)) { // Try to create the file, if doesn't exist fopen($localfile, 'w'); } if (is_writable($localfile)) { if ($fp = fopen($localfile, 'w')) { fwrite($fp, $response); fclose($fp); } } } ?>
The last step is that you will need to setup a cron job that runs the script above periodically. This will update your
local-ga.js
file with the latest version from Google. Here is an example using crontab with a daily update.02 4 * * * /usr/bin/php /home/USERNAME/public_html/ga-update.php 2>&1
If your web host doesn't let you setup cron jobs, you can always use an external service.
Option 2
The second option you have is to use ga-lite, which is a small, cacheable subset of Google Analytics JS client, created by Jesse Luoto. Note that this uses an API that is actually supported by Google. If you just copy the analytics.js
file to your own server, it could potentially break at any minute. This is, because Google makes updates to their code from time to time.
You can install ga-lite to your project by adding the following code to the ended of your HTML <body>
. You can download the file locally to your server or your own CDN as well.
<script src="https://cdn.jsdelivr.net/ga-lite/latest/ga-lite.min.js" async></script>
<script>
var galite = galite || {};
galite.UA = 'UA-XXXXXX'; // Insert your tracking code here
</script>
This includes the most recent version of ga-lite to your site and initializes the script with your own UA code. See more documentation on GitHub.
Fix GA leverage browser caching warning in WordPress
If you are running WordPress, there is now a great free little plugin called Complete Analytics Optimization Suite (CAOS) that can do the above for you! It was created by Daan van den Bergh and enables you to host your Google Analytics JavaScript file (analytics.js
) locally and keep it updated using wp_cron()
. To further optimize your sites' usage of Google Analytics, it allows you to optionally set Adjusted Bounce Rate and decided whether to load the analytics tracking code in the header or footer.
Download Complete Analytics Optimization Suite (CAOS) from the WordPress repository or do a search from your WordPress dashboard for "Complete Analytics Optimization Suite." Then click on "Install Now."
After you have installed and activated it, go into the plugin settings by clicking into "Settings" and "Optimize Analytics" in your WordPress dashboard.
Enter in your Google Analytics Tracking ID and position for your tracking code. We recommend placing it in your footer. The developer has also added the ability to use adjusted bounce rate (example: 30 seconds) and change the enqueue order in WordPress. The default is 0 which means the Google Analytics script will be the first script in your footer that fires. You can also enable the anonymize IP feature which is required by law in some countries.
Don't know your Google Analytics tracking ID? Here is how to find it. Log in to Google Analytics, click into your site, click on "Admin", and then into "Property Settings." Then copy your Tracking Id, paste into the plugin settings shown above, and click on "Save Changes."
And the great news is that it works perfectly with KeyCDN's WordPress CDN Enabler plugin. So the plugin above will sync across Google's analytics script and then our plugin will copy it to your CDN. Again remember to test to ensure it is working properly. A quick way to test it is to launch your site it incognito mode and view real-time analytics.
Make sure you aren't running multiple Google Analytics plugins otherwise your script might appear twice and double your reporting. If you were previously using a plugin like Monster Insights (previously Yoast Analytics) to hook up with Google Analytics, you will need to deactivate the plugin.
And now there should be no more leverage browser caching warning, at least as far as Google Analytics is concerned.
Additional browser caching recommendations
The recommendation to leverage browser caching should now disappear once you have properly configured your web server to tell browsers to cache content and have fixed the GA analytics script issue. In the event that you run your page through a site speed test tool again and see that you still do not score 100%, check which files are being flagged. If the files are being loaded from third party sources such as gravatar.com or googleapis.com, for example, these can be safely ignored as you cannot change them.
Additionally, consider using file name versioning in the event that you do make a change to a resource which is cached locally for a long period of time, this method is called cache busting. For example, if you have a file called foobar.js
and make an update to it, you can reupload the file as foobar-v1.js
. This same technique is used to allow for greater control within a KeyCDN Push Zone.
Summary
As you can see there are a couple of things to be aware of when fixing any leverage browser caching warnings. There are some great alternatives out there for local analytics if you are trying to fix the Google Analytics leverage browser caching warning in PageSpeed Insights. You might even see a small performance improvement. Let us know below how you got along with fixing the leverage browser caching warning on your site.