Fix Leverage Browser Caching Warning

local analytics

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. And we are referring to the one originating from Google’s analytics.js script. In this post today we are going to show you a couple options on how to easily setup local analytics to fix this warning. Depending upon your environment you might also see a slight performance improvement. We have also included a new way to more easily do this for those of you running WordPress.

Google Analytics Leverage Browser Caching Warning

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.

There are some warnings which can be a little frustrating, such as with the leverage browser caching warning, because it is coming from Google’s own analytics.js script. Kind of ironic that their own optimization tools gives 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 2 HTTP requests to Google, you now only have 1.
  • When you host locally, you now have full control over browser caching, expires headers, cache-control, etc.
  • 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.576s
Fully loaded: 1.740s

google analytics load time before

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.426s
Fully loaded: 1.526s

google analytics load time after

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 to 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 3rd 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.

Step 1

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.

Step 2

Upload the local-ga.js file to your web server where it is accessible. Note: Permissions on this file need to be writable for everything to work correctly.

Step 3

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.

Step 4

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';
$localfile = 'ENTER YOUR ABSOLUTE PATH TO THE FILE HERE';
//For Cpanel it will be /home/USERNAME/public_html/local-ga.js

// 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 exist)
 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);
 }
 }
}
?>

Step 5

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. Note: 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.

complete analytics optimization suite banner

Step 1

Download Complete Analytics Optimization Suite (CAOS) from the WordPress repository or do a search from your WordPress dashboard for “host analytics.” Then click on “Install Now.”

caos plugin

Step 2

After you have installed and activated it, go into the plugin settings by clicking into “Settings” and “Optimize Analytics” in your WordPress dashboard.

Step 3

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.

caos analytics settings

Don’t know your Google Analytics tracking ID? Here is how to find it. Login 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.”

optimus get analytics tracking id

 

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.

Important Note: 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.

Summary

As you can see 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. Have you experimented with hosting Google Analytics locally? If so we would love to hear your thoughts below.

Related Articles

Fix Leverage Browser Caching Warning was last modified: June 6th, 2017 by Brian Jackson
  • Venezuelasite com

    More simple whith crontab (daily update)

    02 4 * * * /usr/bin/wget -c https://www.google-analytics.com/analytics.js > /YOUR/LOCAL/HOME/DIR/analytics.js 2>&1

    • Thanks for your comment. I have added your example to the post above.

      • tudoutou

        wget would save the result but not the content of analytics.js

  • Christoffer

    Great read! Rocking 95% myself. But I could get an additional 2-3% by making keycdn handle the request from the last external sources. Currently I don’t have the funds. But I’ll definitely do it in the future.

    • 95% is great! Most people never achieve that. Also, you can calculate KeyCDN pricing more accurately with our pricing calculator: https://www.keycdn.com/pricing Hope to see you on board in the future.

  • Yo. Senior Jackson: Either I’m too lazy to write it up myself, or too busy to explain all the details, but… After passing your article around a bit, can you add a few steps to your tutorial when folks are using MonsterInsights (formerly known as Yoast), or some other type of GA Plugin in their WordPress site?

    It’s a bit difficult for folks to ‘see’ where/how to modify their code when they’ve used a myriad of plugins that simply prompts for the GA ID. They don’t really see ‘how’ to modify their since… well…. let’s be honest here: all they’ve done is copy / paste their ID into (one of many) Google Analytics Plugins.

    Any chance you can take a quick run back through this article w/ a few more screenshots and steps for those using 2 or 3 of the most popular plugins?

    Thanks Brian!

    • Hey Brad! Thanks for sharing the article around.

      I have gone through and updated the WordPress section of the tutorial a bit. Let me know if you have any other suggestions. Thanks.

      • I’m sorry I’m just now circling back to this @brianleejackson:disqus
        More specifically… when a site owner is using Yoast / Monster Insights, there is no way for them to actually modify their script. So where you mention, “…you need to update the Google Analytics tracking code on your website…”, that part can’t happen.

        Uh…. why Brad?

        Well because there is NOT a WAY for the average user to actually modify the way that Yoast / Monster Insights inserts the whole script.

        There’s only this screen http://prntscr.com/bkl96s which allows the end user to either verify via Google’s API, or they can manually enter in their Analytics ID. When the average user is using the most popular plugin, this tutorial doesn’t & can’t actually help them because it hasn’t shown them how to use this _with_ the Yoast / Monster Insights Plugin.

        • Hey Brad, thanks for the update. If you look under the “WordPress” section of the tutorial above you will see I added information about how unfortunately you can’t use Yoast / Monster Insights with this. You have to disable that plugin and use the plugin in the tutorial. No coding is required.

          If you are more technical you can of course modify the Monster Insights plugin, but that is not a viable option for most people. If you are trying to figure out a way for basic users to use this with the Monster Insights plugin I recommend forwarding this tutorial to their developers and asking them to add the option.

          Hopefully the helps!

          • Well, I guess that works ;-) I was looking for a tut for people on how to use both.

            a.k.a. On lines xyz of file pdq.php of Yoast’s plugin, change lines 123 to unhook and rehook ….yadda yadda… something along those lines.

  • Gulf State Software

    YES! Wonderful, I am always thought about this, but didn’t get solution. You rock.

    • Glad to hear it! It seems to be one of those warnings people just ignore because they might not think there is a solution, but there is :)

  • Really like these type of tips from you guys! Another option, which we are using on our site, is called “ga-lite” https://github.com/jehna/ga-lite it’s a small cacheable Google Analytics JS client that is super easy to get up and running with.

  • Michael Oeser

    Nice plugin but as very often it simply cannot be used in Germany because it does not have an option to anonymize the IP which is required to cover German data-protection laws.

    • Thanks for the heads up Michael.

    • Daan van den Bergh

      Hi! The author here.

      You’re not the first one to request this. I actually am planning to implement this option into the next release.

      • Michael Oeser

        That´s awesome

        • Daan van den Bergh

          I just released a new version, mostly containing bugfixes. This clears the path for me to add Anonymize ip option very soon. Stay tuned ?

        • Daan van den Bergh

          @michael_oeser:disqus Check out version 1.35 of the plugin. It’ll make you very happy! :D

          • Michael Oeser

            Yessss…I´m happy now ;-)

    • Walter Otsyula

      Have you tried to replace cookie tracking with fingerprinting instead. I don’t think the laws have gone there yet. You will get duplicates however. You could try to use window.Localstorage for browsers that support it and fall back to browser fingerprinting for those that don’t. You will still have to inform about your use of window.localstorage but at least you won’t have the word cookie on your website. https://github.com/Foture/cookieless-google-analytics

  • Good idea, Does this mean that Google don’t have good servers to host analytics.js file? This file is definitely a headache when you try to optimize it for http requests.

    • Hey Rohan. No, Google has good servers and a very large infrastructure. The issue is that whenever you start introducing external 3rd party requests, especially to analytics.js which is quite large, it can slightly increase download times. It can be faster to sometimes include the bulk of the weight in your current CDN. And if you are running over HTTPS with HTTP/2, having one single connection along with parallelism can be a great solution.

  • FYI maybe one day GA could consider the issue https://code.google.com/p/analytics-issues/issues/detail?id=861 and cdnjs.com could host it https://github.com/cdnjs/cdnjs/issues/1670.

    • That would be amazing if the GA team fixed the issue at least with PageSpeed Insight reporting. However, I have low expectations since this has been ongoing for years. Fingers crossed.

  • Hi Brian,

    are using the third solution, renamed in “Complete Analytics Optimization Suite (CAOS)”? I noted that on your site, the javascript “local-ga.js” it’s loaded from CDN; on my site, where I used KeyCDN, the file continued to load from my domain, and not from CDN.

    Did you modified anything in the plugin?

    • Nothing was modified in the plugin. It does work with our CDN Enabler plugin. I select “footer” for location and then “Change enqueue order?” is left blank (or rather default of 0). Do you have any other optimization plugins running that might be changing it?

      • So, I understand the problem.

        If I use WP Rocket to integrate che CDN, two images file (background and logo) and the “local-ga.js” are NOT load from the CDN and are served from my server. Always automatically, files JS and CSS are minimized in a single file, created from WP Rocket in a folder “cache”.

        If I use CDN Enabler plugin, all my file are load from the CDN, but file JS and CSS NOT are automatically minimzed in a single file, but they are loaded one at a time, and they are a little bit more big of first solution.

        As performance, they are both very good, but I am continuing to monitor them.

        Which do you think is the best strategy?

        • Jonathan Buttigieg

          Hi Alessandro,

          WP Rocket and CDN Enabler are 100% compatible. So if you want you can disable the CDN feature from WP Rocket, keep turned on the minification options and use CDN Enabler to serve them from your CDN.

  • Hi. I was wondering if you could elaborate on the bit where we need to set up a cron job. I’ve looked all over the internet and can’t seem to find a definitive way to set up a cron job without cPanel. And I have read that if I do this incorrectly I could really screw up the website. I use WPengine and thus have no access to cPanel. How would I go about setting up this cron job without cPanel? Do I write a function in functions.php to do something? I’ve never used the terminal to code. I open up SFTP and edit my files through a text editor called Sublime. How would I know what to run and how to run this if I need to use a terminal? Is it possible to get a step by step run through? Thanks for the great article btw. All was straightforward except this part.

    • Eugene Kalashnikov

      Please make sure that WPengine didn’t not disable cron task for you, the easiest way is to ask them about it directly. I think they can also suggest you the way how to add cron task in your case. But in general if you have FTP access to the server – you may try to put the file (file name should be your FTP account name) that contains cron task under the /var/spool/cron.
      You may find here how does cron task should look like:
      http://www.cyberciti.biz/faq/how-do-i-add-jobs-to-cron-under-linux-or-unix-oses/

  • rotrif

    Hi, I need a similar solution but for Google Map not for Google Analytics. Can you provide a plugin or a soution for Maps also?

  • The Plugin works perfectly fine. You’ve provided the best solution so far comparing others.

  • Book Book

    Thank you, I check from 83 to 93 mark.

  • John C

    I’m a little confused by the instructions in option one. In step 4 we create a php script that looks like it sources and copies the analytics.js file from google to a local file. Step 5 sets up a cron job that looks like it is doing the same thing with Unix command line tools.

    Where does the php script get called?

    Please help me sort through my ignorance.

  • Suhaib

    what for are we creating a php file and if created where are we executing it ?

    • The PHP file should have been included in the cronjob example. This has now been updated to reflect that.

  • Suhaib

    Status: 404 Not Found
    X-Powered-By: PHP/5.6.28
    Content-type: text/html; charset=UTF-8

    No input file specified.

    i am facing this error.

    i have given the absolute address properly

    like:- ‘/home/sultanventures/public_html/analytics.js’

  • Walter Otsyula

    You can improove javascript performance by using async / defer and adding preconnect link tag to your cdn provider.

    http://devdocs.io/html/attributes#async-attribute
    http://devdocs.io/html/attributes#defer-attribute
    http://devdocs.io/html/link_types#preconnect

  • Rob Fish

    Thanks for the clear explanation of the problem! I do my own web site, and between online research and modifying the site, I’ve spent over 20 hours this week improving my home page’s Google PageSpeed score. The score started at 63/100, and I invested about 14 hours into moving the score up to 98/100 for both mobile and desktop views. And then I spent about 6 hours trying to figure out a way to leverage browser caching for Facebook’s sdk.js and Google’s analytics.js.

    I still don’t clearly understand much of the technology and many of the terms I’ve been reading about. But after reading this article, I do finally have a clear understanding of why Google’s PageSpeed Insights is giving me a 98/100 score, why none of the stuff I learned about and tried in my web.config file had any affect on the score, what I can do to get from 98/100 to 99/100, and, most importantly, that I should probably stop worrying about getting those last 2 points and move on to updating the rest of my web site.

    I sure wish I’d found this article during the first hour I tried to leverage browser caching for http://connect.facebook.net/en_US/sdk.js (20 minutes) and https://www.google-analytics.com/analytics.js (2 hours) – it would have saved me a lot of time and needless worry.

    Thanks for the great article!

  • Ilya Heifetz

    I did everything exactly as described in the Option 1. My script works safely locally and I can view real-time analytics in my Google Analytics account. But I still get https://www.google-analytics.com/analytics.js Leverage Browser Caching Warning on the Google PageSpeed. What am I doing wrong?

  • It sounds like you are still loading the script directly from Google. Did you ensure you removed the original script from your website and that you are only using the local copy?

    • Ilya Heifetz

      Yes I did. I changed the GA script link from global https://www.google-analytics.com/analytics.js to my local path. The local path is correct, because the Google Analytics data in my Google Analytics updates in the real-time. Also I tried to specify the wrong path to the local analytics.js script file. In this case, I was getting an error in the console. Do you have more versions, why this happens?

  • On another note, will these work for Google Adsense too?

  • Hi Brian, I’m glad I found your article. The CAOS plugin works fine – I use KeyCDN / WP Rocket. The local gs file is now cached in my CDN. It also solved the GTMetrix / YSlow “Add expire headers” for the analytics.js. Cool stuff!

Share This