Avoid Sending GA4 Events on Page Refresh via Google Tag Manager

Measuring event counts in Google Analytics on page refreshes is like trying to count how many times someone enters a revolving door: You see them go in, but did they actually come out and re-enter, or are they just spinning around in circles for fun?

Identifying a page refresh in Google Tag Manager (GTM) can be a bit tricky since a standard page view or page load in analytics tools doesn’t differentiate between an initial page load and a refresh. And the problem is aggravated when you are relying on thank you page loads to measure your conversions. I’ve been there when a lot of times GA4 conversions do not match the actual sign ups we see in the CRM.

However, there’s a method you can use to identify page refreshes using the browser’s performance object. The performance object contains information about the current page’s navigation, and the type property of this object can indicate the type of navigation (e.g., navigated to the page, reloaded the page, etc.).

Note that this doesn’t come without caveats that I’ll list at the bottom of the article.

Here’s how to set this up in GTM:

  1. Create a Custom JavaScript Variable in GTM:
    • Navigate to the “Variables” section of your GTM container
    • Click “New” to create a new variable
    • Name the variable something like “JS – Page Navigation Type”
    • Choose “Custom JavaScript” for the variable type.
    • Enter the following code into the code editor:
      function() {
      if (performance && performance.navigation) {
      return performance.navigation.type;
      return undefined;

      This script returns the type property of the performance.navigation object, which can have one of the following values:
      0: The page was navigated to (e.g., by clicking a link).
      1: The page was reloaded (refreshed).
      2: The page was navigated to via the browser’s back or forward buttons.

    • Save the variable.
  1. Add this variable to your Event Trigger:
    • Go to your event “Triggers”
    • Edit the Trigger configuration
    • Choose “Some forms/ Some clicks” under “This trigger fires on”. This depends on whether you have no condition or already have a condition in place. If you already have a condition added, you’ll just click the + icon right next to it under “Fire this trigger when an Event occurs and all of these conditions are true”. Choose the variable we created in the first step and select “does not equal” 1 (for page refresh identification).
    • if you’d like to also exclude forward or backward browser button, then add another condition. Choose the variable, select “does not equal” 2.
    • Save your trigger.
  1. Test and Publish
    • Before publishing any changes, use GTM’s “Preview” mode to test and ensure the setup works as intended. Once everything is working correctly, publish the changes.


Ok, now the hate-able part. These are the cases you should be aware of when this trigger will not work.

  1. Browser Support: Older browsers or certain mobile browsers might not support the performance or performance.navigation APIs. While most modern browsers support these APIs, it’s always a good idea to check for compatibility, especially if your audience uses older or less common browsers.
  2. BFCache (Back-Forward Cache): On some browsers, when a user navigates away from a page and then returns via the back or forward buttons, the page might be loaded from the BFCache, which means it’s not re-fetched from the server or re-executed. This can cause the performance.navigation.type value to be 2 (indicating navigation via back/forward buttons) even if it seems like a fresh page load to the user.
  3. Single Page Applications (SPAs): In SPAs, only the first load of the application is a full page load. Subsequent “page views” are handled client-side without full page reloads. This means that the provided approach will not detect refreshes on inner “pages” of the SPA, because those aren’t full browser page loads.
  4. Multiple Tabs: If a user opens a link in a new tab (or duplicates the tab), the performance.navigation.type value will be 0 (indicating a new navigation) in the new tab. If they then refresh the new tab, the value will correctly be 1. However, from a user experience perspective, this might be their first “real” view of the page, even though it’s technically a refresh.
  5. Hard vs. Soft Refresh: There’s a distinction between a soft refresh (e.g., pressing F5 or the browser’s refresh button) and a hard refresh (e.g., Ctrl + F5), which bypasses the cache and fetches all resources anew. The method provided doesn’t differentiate between these two types of refreshes.
  6. Server-Side Redirects: If there’s a server-side redirect in place, it might affect the values you see in performance.navigation. A quick client-side redirection (e.g., via meta-refresh or JavaScript) might also be seen as a navigation rather than a reload.
  7. Clearing or Modifying Browser History: If there’s any client-side code that manipulates the browser history (e.g., using the History API), it might interfere with how performance.navigation categorizes the navigation type.

1 Comment

    Leave a Reply