PageSpeed Insights 99/100 because of Google Analytics - How can I cache GA?
Asked Answered
P

20

261

I'm on a quest to reach 100/100 on PageSpeed and i'm almost there. I'm trying to find a good solution to cache Google Analytics.

Here is the message I get:

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 for the following cacheable resources: http://www.google-analytics.com/analytics.js (2 hours)

The only solution i've found was from 2012 and I do not think it is a good solution. Essentially you copy the GA code and host it yourself. You then run a cron job to recheck Google once a day to grab the latest GA code and replace it.

http://diywpblog.com/leverage-browser-cache-optimize-google-analytics/

What else can I do to reach 100/100 while also using Google Analytics?

Thank you.

Padauk answered 20/3, 2015 at 9:5 Comment(3)
I used the cron method, Without cron usage ( loads and caches onload. i can share php code if you want ). And i got fixed my GA fixing suggestion. But little problem left there: I left "Cache-Control: max-age=604800" header. Which is much higher then 5 minutes cache.Hollander
Is that really a good idea, though? Caching this file on your server means the browser will have to re-download it instead of re-using the one it has already cached by visiting other sites using Google Analytics. So it may actually slightly slow down your visitors.Longwise
Possible duplicate of Leverage browser caching for 3rd party JSVolsung
B
259

Well, if Google is cheating on you, you can cheat Google back:

This is the user-agent for pageSpeed:

“Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.8 (KHTML, like Gecko; Google Page Speed Insights) Chrome/19.0.1084.36 Safari/536.8”

You can insert a conditional to avoid serving the analytics script to PageSpeed:

<?php if (!isset($_SERVER['HTTP_USER_AGENT']) || stripos($_SERVER['HTTP_USER_AGENT'], 'Speed Insights') === false): ?>
// your analytics code here
<?php endif; ?>

Obviously, it won't make any real improvement, but if your only concern is getting a 100/100 score this will do it.

Bouillon answered 21/3, 2016 at 17:7 Comment(24)
Clever......pity I use edge caching because this script will only work if requests reach your origin for every request :(Keene
Load it via JS then :) if(navigator.userAgent.indexOf("Speed Insights") == -1) { /* analytics here */ }Oneal
Thank you, Half Crazed, that's a great approach for statically cached pages!Bouillon
@Jim See #10735468 -- you would use this method inside of the { } in my example, along with any other JS that GA uses (such as ga('create', 'UA-XXXXX-Y', 'auto'); ga('send', 'pageview'); or whatever)Oneal
@Jim I've added an answer which covers this.Oneal
Any way to extend this to other tests? E.g. Google Page Speed and gtmetrixPolecat
With all due respect, that's cheating! Think that you may apply that rule for many other things, like improving speed by not loading content. Sooner or later, Google might start to penalise sites that use this rule, by comparing HTML from different user agents.Jennee
@JoãoPimentelFerreira Yup, that is named cloacking and it is already penalized by google. You can serve different content for google, but should keep your changes to secondary non-visible elementsBouillon
@Polecat I've made some test with other services. It worked with services like pingdom who identify themshelves with a unique user agent. On the other hand, GTMetrix uses a computer farm with real browsers that keep their user-agent (chrome, firefox, edge), it would be more difficult to target that.Bouillon
What a hack! There's literally no point in doing this, just ignore the warning in PageSpeed Insights if you don't care.Clemmie
@MattiVirkkunen the problem is that a lower insights score negatively affects your seo placement... even if that lower score is caused by "the google" :(Oneal
@HalfCrazed Your time is probably better spent coming up with better content if your search ratings are low, instead of trying to play the system.Clemmie
@MattiVirkkunen agreed. This "hack" isn't meant to improve SERP, only insights scores.Oneal
How about if Pingdom (pingdom.com) or GT metrics (gtmetrix.com) is used?Ipomoea
Just a helpful heads up - Google cuts out the https: from it's new snippet of analytics code - Placing this in will make this trick work - Otherwise it will still show up unsolved.Riddance
I think this has to be one of the most over rated answers I have seen on SO for a while now. PageSpeed Insights is a tool that lets you test your site for speed optimization so that when Google bot fetches it, the website will load faster. They don't say anywhere that SpeedInsight score is one of the indicators they use for ranking.Hayner
It's not used for ranking, but it's one of the factors that affect the quality score of Adwords campaigns. And yes, my answer is totally overrated :DBouillon
Warning: This does not work anymore. Page Speed Insights powered by Lighthouse uses a default userAgent, that cannot be detected anymore.Extramural
@DavidVielhuber is there any other good solution for this problem then?Stephanestephani
Wow that was a really good one :)Tobietobin
@Afsanefda, is it working for you? I have tried but not working for me.Steamtight
@Steamtight I didn't try this but the whole idea was nice :)Tobietobin
@Afsanefda, It's not working for me.Steamtight
But be aware, CrUX metrics become not better with this hint!Ambidexter
B
42

There's a subset of Google Analytics js library called ga-lite that you can cache however you want.

The library uses Google Analytics' public REST API to send the user tracking data to Google. You can read more from the blog post about ga-lite.

Disclaimer: I am the author of this library. I struggled with this specific problem and the best result I found was to implement this solution.

Bloodroot answered 28/12, 2015 at 15:9 Comment(0)
O
23

Here is a really simple solution using JS, for basic GA tracking, which will also work for edge caches/proxies (this was converted from a comment):

if(navigator.userAgent.indexOf("Speed Insights") == -1) {
  (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://www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-XXXXXXXXX-X', 'auto');
  ga('send', 'pageview');
}

Note: This is the default GA script. You may have other ga() calls, and if so, you would need to always check the user agent before calling ga(), otherwise it may error out.

Oneal answered 9/12, 2016 at 16:46 Comment(3)
Reacting to the "Note:" section, You can declare ga as ga = function(){}; before the snippet to fail silently when executed as ga(); so you don't have to check the existence of this function everywhere in your code.Puppis
How to add this in script <script async src="googletagmanager.com/gtag/js?id=UA-xx6600xx-1"></…>Nesline
Note: This likely no longer works. Speed Scores DO contribute as to search signals (see developers.google.com/search/docs/guides/page-experience) - to what extent is unknown.Oneal
A
16

I wouldn't worry about it. Don't put it on your own server, it sounds like this is an issue with Google, but as good as it gets. Putting the file on your own server will create many new problems.

They probably need the file to get called every time rather than getting it from the client's cache, since that way you wouldn't count the visits.

If you have a problem to feel fine with that, run the Google insights URL on Google insights itself, have a laugh, relax and get on with your work.

Abott answered 8/10, 2015 at 7:1 Comment(5)
He wants to know how can he reach 100, not if 99 is ok.Pannikin
This answer is not true, where the Analytics.js file is downloaded from does not impact whether or not analytics tracks. The issue of hosting your own analytics file is that you always have to manually update to the latest version (a few times a year).Picoline
Thanks Matthew for pointing that out. Apparently I was wrong, which is good, but still I don't think it is a good idea to host this file on your own server because I can imagine it will create a lot of new problems. The OP question was how to get to 100 on pagespeed and my answer is not to worry about getting to that 100. That may be a really annoying answer, but that's me.Abott
good answer for people that got lost by thinking 99 is not good enough. better dedicate your time on real problems.Caldron
@ErickEngelhardt You are correct, but if people ask a question where you think they are not aiming for the best goal, you should give them a heads up which solution might serve them better.Mathematical
T
10

In the Google docs, they've identified a pagespeed filter that will load the script asynchronously:

ModPagespeedEnableFilters make_google_analytics_async

You can find the documentation here: https://developers.google.com/speed/pagespeed/module/filter-make-google-analytics-async

One thing to highlight is that the filter is considered high risk. From the docs:

The make_google_analytics_async filter is experimental and has not had extensive real-world testing. One case where a rewrite would cause errors is if the filter misses calls to Google Analytics methods that return values. If such methods are found, the rewrite is skipped. However, the disqualifying methods will be missed if they come before the load, are in attributes such as "onclick", or if they are in external resources. Those cases are expected to be rare.

Toponym answered 26/2, 2016 at 17:34 Comment(0)
I
7

You can try to host the analytics.js locally and update it's contents with a caching script or manually.

The js file is updated only few times a year and if you don't need any new tracking features update it manually.

https://developers.google.com/analytics/devguides/collection/analyticsjs/changelog

Indoxyl answered 23/4, 2015 at 13:26 Comment(1)
Be warned this is explicitly not supported by Google: support.google.com/analytics/answer/1032389?hl=enPaleolithic
A
7

store localy analytics.js, but it is not recommended by google: https://support.google.com/analytics/answer/1032389?hl=en

it is not recommended cause google can update script when they want, so just do a script that download analytics javascript each week and you will not have trouble !

By the way this solution prevent adblock from blocking google analytics scripts

Altercate answered 25/9, 2015 at 9:59 Comment(1)
It doesn't bypass Adblock completely (it still blocks ajax calls), but at least you get sessions and page viewsBouillon
F
7

varvy.com (100/100 Google page speed insight) loads google analitycs code only if user make a scroll of the page:

var fired = false;

window.addEventListener("scroll", function(){
    if ((document.documentElement.scrollTop != 0 && fired === false) || (document.body.scrollTop != 0 && fired === false)) {

        (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','//www.google-analytics.com/analytics.js','ga');

        ga('create', 'UA-XXXXXXXX-X', 'auto');
        ga('send', 'pageview');

        fired = true;
    }
}, true);
Freestyle answered 8/11, 2016 at 14:17 Comment(3)
What if visitor does not scroll but just click a link. He will not be counted in analytics.Aftereffect
@RossIvantsiv you can handle also the click!Freestyle
I would not recommend this solution. You can never asure all data being tracked, since some touch devices wont perform events initially. Take purchase tracking, where you return from third party window. No GTM or analytics tag loaded, the purchase is never tracked. Just my thoughts..Consequently
R
6

You can proxy the google analytics script via your own server, save it locally and auto update the file every hour to make sure it's always latest version from google.

I've done this on a couple of sites now and all is working fine.

Google Analytics Proxy Route in NodeJS / MEAN Stack

This is how I implemented it on my blog that's built with the MEAN stack.

router.get('/analytics.js', function (req, res, next) {
    var fileUrl = 'http://www.google-analytics.com/analytics.js';
    var filePath = path.resolve('/content/analytics.js');

    // ensure file exists and is less than 1 hour old
    fs.stat(filePath, function (err, stats) {
        if (err) {
            // file doesn't exist so download and create it
            updateFileAndReturn();
        } else {
            // file exists so ensure it's not stale
            if (moment().diff(stats.mtime, 'minutes') > 60) {
                updateFileAndReturn();
            } else {
                returnFile();
            }
        }
    });

    // update file from remote url then send to client
    function updateFileAndReturn() {
        request(fileUrl, function (error, response, body) {
            fs.writeFileSync(filePath, body);
            returnFile();
        });
    }

    // send file to client
    function returnFile() {
        res.set('Cache-Control', 'public, max-age=' + oneWeekSeconds);
        res.sendFile(filePath);
    }
});

Google Analytics Proxy Action Method in ASP.NET MVC

This is how I implemented it on other sites built with ASP.NET MVC.

public class ProxyController : BaseController
{
    [Compress]
    public ActionResult GoogleAnalytics()
    {
        var fileUrl = "https://ssl.google-analytics.com/ga.js";
        var filePath = Server.MapPath("~/scripts/analytics.js");

        // ensure file exists 
        if (!System.IO.File.Exists(filePath))
            UpdateFile(fileUrl, filePath);

        // ensure file is less than 1 hour old
        var lastModified = System.IO.File.GetLastWriteTime(filePath);
        if((DateTime.Now - lastModified).TotalMinutes > 60)
            UpdateFile(fileUrl, filePath);

        // enable caching for 1 week for page speed score
        Response.AddHeader("Cache-Control", "max-age=604800");

        return JavaScript(System.IO.File.ReadAllText(filePath));
    }

    private void UpdateFile(string fileUrl, string filePath)
    {
        using (var response = WebRequest.Create(fileUrl).GetResponse())
        using (var dataStream = response.GetResponseStream())
        using (var reader = new StreamReader(dataStream))
        {
            var body = reader.ReadToEnd();
            System.IO.File.WriteAllText(filePath, body);
        }
    }
}

This is the CompressAttribute used by the MVC ProxyController for Gzip compression

public class CompressAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {

        var encodingsAccepted = filterContext.HttpContext.Request.Headers["Accept-Encoding"];
        if (string.IsNullOrEmpty(encodingsAccepted)) return;

        encodingsAccepted = encodingsAccepted.ToLowerInvariant();
        var response = filterContext.HttpContext.Response;

        if (encodingsAccepted.Contains("gzip"))
        {
            response.AppendHeader("Content-encoding", "gzip");
            response.Filter = new GZipStream(response.Filter, CompressionMode.Compress);
        }
        else if (encodingsAccepted.Contains("deflate"))
        {
            response.AppendHeader("Content-encoding", "deflate");
            response.Filter = new DeflateStream(response.Filter, CompressionMode.Compress);
        }
    }
}

Updated Google Analytics Script

On the client side I append the analytics path with the current date up to the hour so the browser won't use a cached version more than an hour old.

<!-- analytics -->
<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', '/analytics.js?d=' + new Date().toISOString().slice(0, 13), 'ga');
</script>
Rags answered 22/9, 2016 at 2:19 Comment(0)
C
5

For Nginx:

location ~ /analytics.js {
        proxy_pass https://www.google-analytics.com;
        expires 31536000s;
        proxy_set_header Pragma "public";
        proxy_set_header Cache-Control "max-age=31536000, public";
    }

Then change path https://www.google-analytics.com/analytics.js to https://yoursite.com/analytics.js

Chili answered 8/9, 2017 at 13:49 Comment(1)
I've made this but "yoursite.com/analytics.js" return 404 (of course i have replaced by my own site).Blitzkrieg
T
5

PHP

Add this in your HTML or PHP code:

<?php if (!isset($_SERVER['HTTP_USER_AGENT']) || stripos($_SERVER['HTTP_USER_AGENT'], 'Speed Insights') === false): ?>
  <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://www.google-analytics.com/analytics.js','ga');

    ga('create', 'UA-PUT YOUR GOOGLE ANALYTICS ID HERE', 'auto');
    ga('send', 'pageview');
  </script>
<?php endif; ?>

JavaScript

This works fine with JavaScript:

  <script>
  if(navigator.userAgent.indexOf("Speed Insights") == -1) {
    (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://www.google-analytics.com/analytics.js','ga');

    ga('create', 'UA-<PUT YOUR GOOGLE ANALYTICS ID HERE>', 'auto');
    ga('send', 'pageview');
  }
  </script>

NiloVelez already said: Obviously, it won't make any real improvement, but if your only concern is getting a 100/100 score this will do it.

Testosterone answered 23/9, 2017 at 12:44 Comment(0)
N
1

try this just insert before

<script async='async' src='https://cdn.jsdelivr.net/ga-lite/latest/ga-lite.min.js'></script> <script>var galite=galite||{};galite.UA="xx-xxxxxxx-x";</script>

Please change xx-xxxxxxx-x to your code, please check to implementation here http://www.gee.web.id/2016/11/how-to-leverage-browser-caching-for-google-analitycs.html

Nolde answered 11/11, 2016 at 20:36 Comment(1)
Be aware that this is for ga-lite rather than the standard analyticsEtherealize
R
1

In 2020 Page Speed Insights user agents are: "Chrome-Lighthouse" for mobile and "Google Page Speed Insights" for desktop.

<?php if (!isset($_SERVER['HTTP_USER_AGENT']) || stripos($_SERVER['HTTP_USER_AGENT'], 'Chrome-Lighthouse') === false  || stripos($_SERVER['HTTP_USER_AGENT'], 'Google Page Speed Insights') === false): ?>
// your google analytics code and other external script you want to hide from PageSpeed Insights here
<?php endif; ?>
Relly answered 23/4, 2020 at 14:8 Comment(0)
L
0

Google cautions against using local copies of the analtics scripts. However if you are doing it, you will probably want to use local copies of the plugins & the debug script.

A second concern with agressive caching is that you will be getting hits from cached pages - which may have changed or have been removed from the site.

Longfellow answered 27/7, 2015 at 14:35 Comment(0)
C
0

To fix this issue you would have to download the file locally and run a cron job to keep updating. Note: this doesn't make your website any faster at all so its best to just ignore it.

For demonstration purposes however, follow this guide: http://diywpblog.com/leverage-browser-cache-optimize-google-analytics/

Comprehensive answered 27/11, 2015 at 13:18 Comment(1)
"this doesn't make your website any faster" that's not necessarily true. As in theory, gzipping a non critical concatenated JS a file with analytics included should compress slightly smaller than a separated analytics file because of the shared dictionary. Perhaps more trouble than it's worth.Impressible
Z
0

This may do the trick :)

<script>
  $.ajax({
  type: "GET",
  url: "https://www.google-analytics.com/analytics.js",
  success: function(){},
  dataType: "script",
  cache: true
  });
</script>
Zippel answered 7/8, 2017 at 12:28 Comment(0)
Q
0

Depending on your use of Google Analytics data, if you want basic information (such as visits, UI interactions) you might be able to not include analytics.js at all, yet still collect data in GA.

One option may be to instead use the measurement protocol in a cached script. Google Analytics: Measurement Protocol Overview

When you set the transport method explicitly to image, you can see how GA constructs its own image beacons.

ga('set', 'transport', 'image');

https://www.google-analytics.com/r/collect
  ?v={protocol-version}
  &tid={tracking-id}
  &cid={client-id}
  &t={hit-type}
  &dl={location}

You could create your own GET or POST requests with the required payload.

However, if you require a greater level of detail it probably won't be worth your effort.

Querida answered 19/12, 2017 at 14:1 Comment(7)
Where's the connection to Pagespeed?Nihil
By not loading analytics.js you avoid the pagespeed penalty.Querida
Yeah. And by skipping all that CSS, JS and imagery out of your page, it will load even faster. Skipping Google Analytics is not an option according to the OPNihil
Except that data is still recorded in Google Analytics I think my answer is valid, and clearly stated that depending on the level of detail required from Google Analytics it may be an option worth considering which importantly would still record visits, UI interactions and potentially other metrics. If the OP is looking to optimise for the final 1%, it might be an optimisation worth considering.Querida
@NicoHaase I've edited my comment to hopefully make my point clearer. Interested to hear your thoughts.Querida
I personally do not like the way of implementing your own logic to collect data for GA. Even if it works for now, you have to adopt your own implementation of doing it if Google changes their code, and if you miss the proper point of doing it, you fail to collect the data. For some companies, this is crucial for business and not something that can stop working until IT fixes their codeNihil
I agree, but that same logic applies to strategies suggesting taking a copy of analytics.js and hosting it with different caching policies. Looking at the measurement protocol changelog and the frequency of change (even non-breaking change) would make this a valid solution. developers.google.com/analytics/devguides/collection/protocol/…Querida
B
0

You can set up a cloudfront distribution that has www.google-analytics.com as its origin server and set a longer expiry header in the cloudfront distribution settings. Then modify that domain in the Google snippet. This prevents the load on your own server and the need to keep updating the file in a cron job.

This is setup & forget. So you may want to add a billing alert to cloudfront in case someone "copies" your snippet and steals your bandwidth ;-)

Edit: I tried it and it's not that easy, Cloudfront passes through the Cache-Control header with no easy way to remove it

Bethune answered 10/1, 2018 at 9:30 Comment(0)
R
0

Open https://www.google-analytics.com/analytics.js file in a new tab, copy all the code.

Now create a folder in your web directory, rename it to google-analytics.

Create a text file in the same folder and paste all the code you copied above.

Rename the file ga-local.js

Now change the URL to call your locally hosted Analytics Script file in your Google Analytics Code. It will look something like this i.e. https://domain.xyz/google-analytics/ga.js

Finally, place your NEW google analytics code into the footer of your webpage.

You are good to go. Now check your website of Google PageSpeed Insights. It will not show the warning for Leverage Browser Caching Google Analytics. And the only problem with this solution is, to regularly update the Analytics Script manually.

Requisition answered 21/4, 2018 at 11:7 Comment(0)
P
-13

You can minify all your scripts in page, including analytics.js using:

Remember to minify the files before using it. Otherwise it will consume more processing time.

Pannikin answered 17/11, 2015 at 12:4 Comment(1)
analytics.js is already minified from the sourceJennee

© 2022 - 2024 — McMap. All rights reserved.