A case study in how to improve page load speed

We used one of Littledata's own benchmarks to identify an issue with mobile page load speed and fix the underlying problems. Here's how we did it. Page load speed benchmark Using benchmarking in the Littledata app, we compared our website with 72 other marketing services sites benchmarked in December. (Our app lets you compare web performance by sector). Paying attention to both mobile and desktop experiences, we looked at things like the average delay before page content appears and the average time before full mobile page load. Overall we are above average, but against one benchmark - delay before page content appears - we are close to the median. That means 35 sites in our sector manage faster page load speeds. As website professionals, we can't let that rest! We care because Google cares about speed. If our landing pages take a long time before first 'paint' - before the content is visible - then more visitors will bounce or pick another article to answer their questions. If we invest time into writing articles like this one on our analytics blog, we should also invest time in making sure they are fast to load, and fast for Google to crawl and index. Here's how we acted on the benchmark data. [subscribe heading="Benchmark your site" button_text="sign up"] How fast were pages loading? We used the excellent PageSpeed Insights tool to pinpoint what was going wrong with the pages loading. Our original speed on mobile For mobile browsers, where traffic increasingly comes from, we were in the bottom 10% for speed, and below average for desktop. That sucks, and was definitely penalising our organic search rankings. Fixes to improve page load speed 1. Removing unnecessary JavaScript modules As a 'web app' built using Meteor, our pages are heavy with Javascript - effectively the whole site is pre-loaded with the first page. This means a module used by only one page can slow down ALL pages. By visualising the bundles for our Meteor App we found 2 very large modules which were blocking page load for all pages: the D3 library, used to visualise our industry category coverage, and the autoform package used for a single signup form. Using the clever dynamic imports feature in Meteor were were able to cut those modules from the landing page, and half the size of the Javascript bundle loaded from 1.6Mb to 0.8Mb. 2. Using Webp images for Chrome and compatible browsers Google prefers you to use the more efficient .webp image filetype, which is typically half the file-size of older .png or .jpeg filetypes. We found our chosen image server, Cloudinary, includes a simple feature to automatically chose the best file type for the browser it detects. Very quick fix! 3. Lazy-loading of landing page images Even if the images are efficiently served, trying to fetch all the images for a long-form landing page takes time - and is wasteful for users who only read the first few paragraphs. Instead, we can wait until the user scroll down before images are loaded for that section. This results in a small flicker as the user scrolls the first time, but faster initial load time. 4. Caching pages from our blog Wordpress is a great CMS, but the PHP + MySQL stack it uses is not the fastest for generating the pages. Luckily there are some great cache plugins, including WP Fastest Cache which we set up. What this does is save a rendered HTML file on the WP server, and only refresh the cache when the post is edited or new comments are added. 5. Cleaning up unused CSS and HTML templates Like all sites that have evolved their design and content over a few years, ours had lots of redundant code that no-one had spring-cleaned. Generally it's easier with older projects just to leave code, but for Meteor apps particularly it can slow down every page (see fix 1). So a new front-end developer starting was a good chance to rewrite templates from scratch, and chuck out the trash. The page load speed improvements Those 5 improvements, plus a database upgrade, let to some pretty good speed improvements - above average for mobile and top 2% for desktop speed! Even a few days after the release we saw a boost in the visibility of our landing pages in Google Search. Looking at Littledata's benchmarks for February, included in the newly released mobile vs desktop benchmarks, we can see the delay before content appears has dropped from 2.6 to 1.8 seconds (down by 40%). And the desktop speed is now in the top 10% of all sites at 1.7 seconds. The only area we still lack is the mobile page speed, so maybe we will look at some AMP versions in the next sprint. Ready to benchmark your site? Littledata's free plans include a Google Analytics connection and free benchmarks in ecommerce, engagement, marketing and site speed.

2019-03-13

Littledata's Shopify connection is now using gtag and GTM data layer

Littledata’s Shopify app is updating to use Google’s latest tracking code library. This will simplify your setup for Google Ads and speed up your site. Google’s ‘global site tag’ or gtag has been live for a year now and is stable for Littledata to adopt. In version 5 of our tracking script we now use gtag for all the events sent to Google Analytics. The advantages of gtag are: Integrates with Google Ads out of the box – no need for separate Google Ads conversion tracker Smaller Javascript library = faster page load times Future proof for using Google Optimize In addition, we are now using the standard 'data layer' format used by Google Tag Manager. This will make it easier for all you hackers to extend Littledata's tracking and use GTM with the enhanced ecommerce data layer, and easily create tags for marketing platforms like: Facebook, Criteo, etc. [subscribe] We've also moved to using the default ecommerce event naming recommended by Google. For example, the event category 'Ecommerce' is now 'ecommerce' (lower case) and event action 'Add to cart' is now 'add_to_cart' (snake case). If you have goals or reports based on the old event names you may need to update them. One final change is that we're only sending page views to GA when the page is not hidden in the browser. Certain advertising campaigns, including SnapChat ads, preload your webpages to provide a faster experience for users, but this skews your analytics with lots of low-grade visits who didn't actually 'see' your landing page. How to update the script If your store already has our tracking script installed, just click on the in-app notification to update. Not a Littledata user yet? If you're struggling with implementing Google Ads conversion tracking or GTM for a Shopify store, check out our Google Analytics connections for Shopify and Shopify Plus stores. Let our app fix your tracking, so you can get back to business!

2019-02-12

How to stop Google Tag Manager being hacked

In two high-profile data breaches this year – at Ticketmaster and British Airways – over half a million credit cards were stolen via a compromised script inserted on the payment pages. Google Tag Manager is a powerful tool which enables you to insert any script you want onto pages of your website, but that power can used against you by hackers if you're not careful – and below we’ll look at how to stop GTM being a security risk on your payment pages. Firstly, how did the hackers get the card details from these sites? And how is it relevant to GTM on your site? Security firm RiskIQ has traced the breach to a compromised Javascript file which skimmed the card details from the payment form. So when a user entered their credit card number and security code on BritishAirways.com (or their mobile app) those details were posted to a third party server, unknown to British Airways or the customer. This is a high-scale equivalent of placing a skimming devices on an ATM, which reads one card at a time. In Ticketmaster’s hack the script was one loaded from a chatbot vendor on their site, Inbenta. Inbenta claims not even to have been aware the script was used on payment pages. The changes to the script were subtle: not breaking any functionality, and in BA’s case using a domain ‘baway.com’ which looked somewhat authentic. To protect your site against a similar attack you obviously need to lock down accounts used by your developers to change scripts in the page source code, but you also need to secure GTM – which can be used to deploy such scripts. We have a few rules at Littledata to help reduce risks in using tag management on payment pages: 1. Use pixels over custom JavaScript tags on payment pages You probably need a few standard tags, such as Google Analytics, on payment pages but try to avoid any custom scripts which could possibly skim card details. Many non-standard tags use JavaScript only to create the URL of a tracking pixel – and it is much safer (and faster) to call the tracking pixel directly. Contact the vendor to find out how. (Littledata's Shopify app even removes the need to have any script on the payment pages, by hooking into the order as it's registered on Shopify's servers) 2. Avoid loading external JavaScript files in GTM Many vendors want you to load a file from their server (e.g. myvendor.com/tracking.js) from GTM, so they can update the tracking code whenever they want. This is flexible for them, but risky for you. If the vendor gets hacked (e.g. with Inbenta above) then you get compromised. It’s less risky to embed that script directly in GTM, and control version changes from there (although a fraction slower to load the page). Of particular risk is embedding a tag manager within a tag manager – where you are giving the third party rights to publish any other scripts within the one tag. Don’t do that! [subscribe] 3. Lock down Edit and Publish rights on GTM Your organisation probably has a high turnover of contract web developers and agencies, so have you checked that only the current staff or agencies have permission to edit and publish? It's OK to have external editors use 'workspaces' for version control in GTM, but ideally someone with direct accountability to your company should check and Publish. 4. Blacklist custom JavaScript tag on the payment pages You can set a blacklist from the on-page data layer to prevent certain types of tags being deployed on the payment pages. If you have a GTM container with many users, this may be more practical that step 3. 5. Remove tags from old vendors There are many thousands of marketing tools out there, and your company has probably tried a few. Do you remove all the tags from vendors when you stop working with them? These are most at risk of being hacked. At Littledata we run a quarterly process for marketing stakeholders opt-in tags they still need for tracking or optimisation. 6. Ensure all custom JavaScript tags are reviewed by a developer before publishing It can be hard to review minimised JavaScript libraries, but worth it for payment pages if you can’t follow rules 1 and 2. If you’re still worried, you can audit the actual network requests sent from payment pages. For example, in Chrome developer tools, in the 'Network' tab, you can inspect what requests sent out by the browser and to what servers. It’s easy for malicious code to hide in the patchwork of JavaScript that powers most modern web experiences, but what is harder to hide is the network requests made from the browser to external servers (i.e. to post the stolen card information out). This request to Google Analytics is fine, but if the domain of a request is dubious, look it up or ask around the team. Good luck, and keep safe with GTM!

2018-11-24

Six challenges in developing a Shopify integration

At the start of 2017 Littledata released its first Shopify app. A year on, here are my observations on the technical challenges we’ve overcome. This week we're at Shopify Unite in Toronto, and it's no surprise that their app ecosystem continues to grow. We chose Shopify as our first platform partner due to their open APIs, quality documentation and enthusiasm from other developers. Much of that has been as expected, but to help all of you looking to build your own Shopify app I’ll share some of our learnings on the hidden challenges. Littledata's Shopify app makes it a one-click process for stores to set up for Enhanced Ecommerce tracking in Google Analytics, and then get actionable insights based on the Google Analytics data. It has to hook into Shopify orders and products, as well and modify the store's theme and process ongoing transactions. 1. Handling re-installs gracefully The great advantage of Shopify’s app store over, say, Magento marketplace, is that any store admin can install and pay for an app with a couple of clicks. The disadvantage is that stores can be as quick to uninstall as install. Store admins may start, realise they don’t have permissions, time or energy to continue and roll back to try again later in the day. Since our app inserts a snippet into the store’s theme layout (see point two below), uninstalling removes the web-hooks we set up but does not remove the inserted snippet. When a store re-installs our app has to work out what state they were in when they uninstalled (audit, test mode or live), whether the script snippet is still there and what settings have been changed in the meantime. It took us a few months to get a handle on all the possible user flows, and we’ve found automated end-to-end tests to really speed up running through the different scenarios. In our Meteor framework we use Chimp [link] to run tests through Selenium on localhost and on our staging server. We've also found it essential to track our own stats of 'installs + activations' (including the date of first install and time to finally uninstall) rather than relying on the Shopify Partner stats of uninstalls and installs, which can hide the detail in between. 2. Working with script tags The other side-effect of making apps easy to install is that you can assume the early-adopter stores who will try your app already have lots of other installs. Shopify recommends using the Script Tag API to handle scripts linked to the app, so that when a store uninstalls your app it also removes any client-side scripts from the store. Unfortunately, in early tests we found the load latency to be unacceptably high: on some stores, only 50% of the page load events were getting fired before the user moved off the page. So plan B was add a snippet to the store theme, and then load this snippet at the top of the <body> element on all the layout templates. This has worked much more predictably, except when theme editors remove the snippet reference without enquiring what the Littledata app does (see our fifth challenge). [subscribe] 3. Charge activation vs authorisation Now a very simple gotcha. In our first month we had around 60 installs at a flat price of $20/month, but apparently no revenue. After investigation we found we had not activated the recurring charges after the store admin had authorised them. Doh! We're still not sure why an app would want to have authorised charges which are not activated -- seems like over-engineering on Shopify's side -- but luckily it was easy to correct without asking for more user permissions. 4. Tracking adds-to-cart The first version of our app tried to run the script when customers got to the ‘/cart’ page of a store. The problem here is that many stores have AJAX or ‘mini’ carts where customers can checkout without every visiting the cart page. We looked to trigger the script before the user got to the cart the page, but this appeared to run too many risks of interfering with the customer actually adding the item. Our final solution has been to poll the Shopify store for the current cart, and see if products have been added (or removed) since we last polled (and stored the previous cart contents in memory). This is somewhat inefficient, as it requires continuous network activity to grab the cart JSON from Shopify, but we’ve reduced the network requests to one every 4 seconds – judging that customers are very unlikely to add a product and checkout in less than 4 seconds. This cart polling has proved more reliable across different store templates. 5. Integrating with other Shopify apps I mentioned that early-adopter stores tend to have lots of other apps: and those apps have loyal customers who push to make Littledata's app to work their chosen app (not just vanilla Shopify). The challenge is that most of these app development companies run a very Agile process, constantly changing how their app works (hopefully to improve the experience for store owners). An integration that worked a few months ago may no longer work. We've found the best solution to be open developer-to-developer communications, via a Slack guest channel. Having the developers implementing the features on each side talk to each other really cuts down the delays caused by a well-meaning project manager slightly misinterpreting the requirement. 6. Handling ongoing updates As tested improved client-side tracking scripts, we needed to update the script in the store theme (see point 2 above). This creates a small risk for the store, as there is no UAT or test environment for most stores to check before going live with the new script. The store theme may also get edited, creating new layout templates where the Littledata snippet is not loaded. In the first version of our app we tried to update and re-insert the latest Littledata snippet automatically on a weekly cycle. However, once we reached hundreds of active installs this became unmanageable and also opaque for the store admins. In the latest version we now allow store admins to UPGRADE to the latest script, and then we check all the correct Google Analytics events are being fired afterwards. Giving the end user control of updates seems a better way of maintaining trust in our brand and also removing risk: if the update goes wrong, it’s quicker for us to alert the store owner on how to fix. Conclusion I’m still sure we made the right choice with Shopify as a platform, as their APIs, partner support and commercial traction are all number one in the ecommerce world. But I hope that by sharing some of the hidden challenges in developing Shopify integrations, we can all build better apps for the community. Have you built something for the Shopify app store? Are there development problems you’ve encountered which I haven’t shared here? PS. Are you a developer interested in joining an innovative analytics company? We're hiring in multiple locations!

2018-05-07

Cross Domain tracking for Eventbrite using Google Tag Manager (GTM)

Are you using Eventbrite for event registrations? And would you like to see the marketing campaign which drove that event registration correctly attributed in Google Analytics? Then you've come to right place! Here is a simple guide to adding a Google Tag Manager tag to ensure the correct data is sent to Eventbrite to enable cross-domain tracking with your own website. Many thanks to the Lunametrics blog for their detailed solution, which we have adapted here for GTM. Before this will work you need to have: links from your site to Eventbrite (including mysite.eventbrite.com or www.eventbrite.co.uk) the Universal Analytics tracking code on both your site and your Eventbrite pages. only have one GA tracking code on your own site - or else see the Lunametrics article to cope with this 1. Create a new tag in GTM Create a new custom HTML tag in GTM and paste this script: [code language="javascript"] <script> (function(document, window) { //Uses the first GA tracker registered, which is fine for 99.9% of users. //won't work for browsers older than IE8 if (!document.querySelector) return; var gaName = window.GoogleAnalyticsObject || "ga" ; // Safely instantiate our GA queue. window[gaName]=window[gaName]||function(){(window[gaName].q=window[gaName].q||[]).push(arguments)};window[gaName].l=+new Date; window[gaName](function() { // Defer to the back of the queue if no tracker is ready if (!ga.getAll().length) { window[gaName](bindUrls); } else bindUrls(); }); function bindUrls() { var urls = document.querySelectorAll("a"); var eventbrite = /eventbrite\./ var url, i; for (i = 0; i < urls.length; i++) { url = urls[i]; if (eventbrite.test(url.hostname) === true) { //only fetches clientID if this page has Eventbrite links var clientId = getClientId(); var parameter = "_eboga=" + clientId; // If we're in debug mode and can't find a client if (!clientId) { window.console && window.console.error("GTM Eventbrite Cross Domain: Unable to detect Client ID. Verify you are using Universal Analytics."); break; return; } url.search = url.search ? url.search + "&" + parameter : "?" + parameter; } } } function getClientId() { var trackers = window[gaName].getAll(); return trackers[0].get("clientId"); } })(document, window); </script> [/code]   2. Set the tag to fire 'DOM ready' Create a new trigger (if you don't have a suitable one) to fire the tag on every page at the DOM ready stage.  We need to make sure the Google Analytics tracker has loaded first. 3. Test the marketing attribution With the script working you should see pageviews of the Eventbrite pages as a continuation of the same session. You can test this by: Opening the 'real time' reporting tag in Google Analytics, on an unfiltered view Searching for your own site in Google Navigating to the page with the Eventbrite link and clicking on it Looking under the Traffic Sources report and checking you are still listed as organic search after viewing the Eventbrite page Need more help? Comment below or get in touch!   Get Social! Follow us on LinkedIn, Twitter, and Facebook and keep up-to-date with our Google Analytics insights.

2017-02-07

Personally Identifiable Information (PII), hashing and Google Analytics

Google has a strict policy prohibiting sending Personally Identifiable Information (PII) to Google Analytics. This is necessary to provide GA reports around the world, yet comply with country regulations about storing personal information.  Even if you send personal information accidentally, Google may be forced to delete all of your analytics data for the time range affected. This policy has recently tightened to state: You may not upload any data that allows Google to personally identify an individual (such as names and email addresses), even in hashed form. A number of our clients are using a hashed email as the unique identifier for logged in users, or those coming from email campaigns.  If so, this needs be a minimum of SHA256 hashing (not MD5 hashing), with a 'salt' to improve the security - check your implementation meets the required standard. If you want to check if personal information affects your analytics, we now include checking for PII in our complete Google Analytics audit. Google's best practice for avoiding this issue is to remove the PII at the source - on the page, before it is sent to Google Analytics.  But it may be hard to hunt down all the situations where you accidentally send personal data; for example, a form which sends the user's email in the postback URL, or a marketing campaign which add the postcode as a campaign tag. We have developed a tag manager variable that does this removal for you, to avoid having to change any forms or marketing campaigns which are currency breaking the rules. Steps to setup 1. Copy the script below into a new custom Javascript variable in GTM [code language="javascript"]function() { // Modify the object below to add additional regular expressions var piiRegex = { //matches emails, postcodes and phone numbers where they start or end with a space //or a comma, ampersand, backslash or equals "email": /[\s&amp;\/,=]([a-zA-Z0-9_.+-]+\@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)($|[\s&amp;\/,])/, "postcode": /[\s&amp;\/,=]([A-Z]{1,2}[0-9][0-9A-Z]?(\s|%20)[0-9][A-Z]{2})($|[\s&amp;\/,])/, "phone number": /[\s&amp;\/,=](0[0-9]{3,5}(\s|%20)?[0-9]{5,8}|[0-9]{3}-[0-9]{4}-[0-9]{4})($|[\s&amp;\/,])/ }; // Ensure that {{Page URL}} is updated to match the Variable in your // GTM container to retrieve the full URL var dl = {{Page URL}} var dlRemoved = dl; for (key in piiRegex) { dlRemoved = dlRemoved.replace(piiRegex[key], 'REMOVED'); } return dlRemoved; }[/code]   2.Check {{Page URL}} is set up in your GTM container This is a built-in variable, but you'll need to check it under the variables tab.   3. Change the pageview tag to override the standard document location, and use the variable with PII removed   By default, Google Analytics takes the location to be whatever is in the URL bar (document.location in Javascript).  You will over-ride that with the PII-safe variable.  

2016-08-03

Get the Littledata analytics app

Complete picture of your ecommerce business. Free Google Analytics connection, audit and benchmarks.

Sign up

Insights from analytics experts

Subscribe to the Littledata blog for the latest posts and updates

No Thanks
We respect your privacy. Your information is safe and will never be shared.
Don't miss out. Subscribe today.
×
×