Setup Google Tag Manager for Analytics

Support for Google Tag Manager was added today! This howto will show you how to use Google Universal Analytics through Google Tag Manager. After you’re done, the Google Tag Manager API will be running on your Discourse site, so you could in theory send arbitrary events to any service that integrates with Tag Manager. Analytics is only the beginning.

:warning: For security reasons, all up to date Discourse installs have a Content Security Policy that blocks all non-Discourse scripts. This means that if you add external scripts to your GTM integration, you might have to allowlist their domains in order for the scripts to be allowed to run.

For example, if you have enabled Advertising Features or AdWords integration, you would need to add


to the content security policy script src site setting.

If you have your own custom scripts in Custom HTML tags, you can follow this guide to allow them to run in Discourse.

Universal Analytics

The first thing you’ll need is to create an Analytics account. If you’re signed in with a Google account, then simply visiting will guide you through creating an account.

Once you have a new account, create a property.

Choose “Website” as the type of app, and fill in the rest of the fields for your Discourse site. At the end, you’ll get a tracking id, which looks like UA-12345678-1. Keep it handy because you’ll need to give it to Tag Manager.

Tag Manager

Visit and let Google set you up. Once you’re in, you’ll be prompted to create your first “Account” and “Container”. When asked where the container will be used, choose “Web”.

Once your container is created, we need to hook up some events from the tag manager api to Google Analytics.


Go to the Variables tab on the left. Click the “New” button in the User-Defined Variables section at the bottom. We’ll be creating two variables.

Title: DL - page title
Type: Data Layer Variable
Data Layer Variable Name: page.title
Data Layer Version: 2

Title: DL - page url
Type: Data Layer Variable
Data Layer Variable Name: page.url
Data Layer Version: 2

Here’s an example:


Now create a trigger that does something when those variables arrive from the API. Click the “Triggers” tab on the left and click New. Create one named virtualPageView (or whatever you want). Choose “Custom Event” as its type. The event name must be virtualPageView. Finally, click Save Trigger.


Finally, we’re ready to add Analytics. Click the Tags tab on the left and create a new tag.

  • Title: enter a title of your choice at the top
  • Product: choose Google Analytics
  • Tag Type: choose Universal Analytics
  • Configure Tag:
    • enter your tracking id from Universal Analytics (UA-xxxxxxxx-x)
    • Track Type: choose Page View
    • Expand “More Settings”, then “Fields to set” and add these three fields exactly as shown:
      • name: cookieDomain, value: auto
      • name: title, value: {{DL - page title}} (can be chosen from a dropdown)
      • name: page, value: {{DL - page url}}
  • Fire On: choose our custom trigger “virtualPageView”.
  • Save Tag


None of this work is live until you click the Publish button on the top right of the page. It will be red if there are changes that haven’t been published.

:warning: If your Discourse site is reporting 404 errors from, it’s probably because you didn’t publish your changes.


GTM is waiting for data, so let’s add it to our Discourse site. Go to the Settings tab of admin and search for gtm container id and enter your tag manager id. It should always be displayed on the top navigation bar of the tag manager UI.

:warning: Also remove your Analytics tracking code from the ga universal tracking code and ga tracking code settings.

Reload the page and data should be flowing through GTM to Analytics. Watch the real-time content view to see traffic and url’s of your site visitors.


Now that you have pageview data going into tag manager, you could add more Tags that receive the events. Google DFP, AdWords, LinkedIn, and others are in the list.


Quick question - Why would you fire on all pages and the custom event?

Not seen the code yet but, I’d think that the custom event will trigger on all - forced load and frontend transitions.

Also, I’m curious - why did you setup additional datalayer variables for url and title?

Page URL is a built in variable and the analytics code figures out the title.

I didn’t find anything about built-in data layer variables for it. What would I use in our page-tracking.js?

1 Like

This is my variable screen for the container I am using.

Is that different from what you see? If I were to scroll down, I’d find the area to add custom variables.

I see that too, but I still can’t find info about how to wire it up to analytics. What would our page-tracking code need to send?

onPageChange((url, title) => {
    'event': '??????',
    '?????': url,
    /* no title needs to be sent? how does it figure it out? */

I found specific examples for Ember apps, which is where I got the solution with the custom variables and page tracking code. I’ll admit that my eyes glaze over while reading these google product docs, but every solution is pretty complicated. See this simple button click example. Ouch that’s a lot of steps.

1 Like

Hope to get back to it and contribute later this week.

I need to get ssl and lets encrypt working with multisite and perhaps figure out how to replace one box with a more versatile approach first. :smile: (famous last words)


Hi Neil:

Jumping in here to add my 2 cents. I think the above methodology goes off the rails a bit. Hopefully, I can help clear up some issues.

Generally, when you setup a google tag manager account, the basic thing is to create a universal analytics tag with your tracking id ( just like you did above) and create a trigger for that tag to fire, then you publish your tag. Thats it. Google automatically takes care of tracking the parameters of the page like page title, url, referrer , time on page etc . It’s already “wired” up to your analytics account when you set the tracking id.

You only need to go the extra step to custom configure a few things , if required. A few scenarios:

  1. You want the tracking code to fire only on certain pages - then you create a trigger to identify just those pages. If you want the tag to fire on all pages you simply use the inbuilt “All Pages” trigger, like you have above - minus the virtual pageview tag you have. No need to mess with datalayer variables at this point.

  2. if you want to track certain events on your site - like clicks on external links, social sharing etc, then you can configure event tracking with GTM to track those too using their own tag.

  3. You want to capture specific info about your site visitor - log in status, membership, shopping cart details, items purchased etc … then you configure that via the datalayer.

There are a ton of other things that can be done directly within GTM - its main purpose is to try and manage most of the scripts on your site from one central repository - DFP, comscore etc. But the methodology is simply to configure the main universal analytics tag, set the trigger, publish and place the container code on your site. I will try to find a link to some good tutorial on the web and place it here later.


I read some more tutorials again today, but still don’t get what the code should look like. Will wait for more people to chime in.

1 Like

A question though:

That’s what we’re doing with the dataLayer:

dataLayer = [{"cookieDomain":"auto","userId":712}];

Can GTM - UA really detect user sessions without the app telling it how to identify users?

In the simplest implementation, the code is just the container code.

Google Analytics by default, uses built in algorithms to attempt to identify users - even across different devices ( cross device tracking). They use a combination of IP, device id etc. . Hence they can count number of visitors to the site, repeat visitors etc ( visitor centric metrics). This has some downsides - like if you clear your browser cache.

Starting with Universal Analytics, which was rolled out awhile back, they provided the ability for businesses to specifically identify users with custom user ids - like you show in the datalayer screenshot. But that is when you want to have that level of info - knowing who specifically is doing what on your site. You may be able to tie your assigned user id to user records into your own backend systems to get names, addresses etc. So that is a really specific advanced requirement.

But Google Analytics doesn’t need that to count users.

Also, setting cookie domain to auto can be set in the google tag manager universal analytics tag - without specifying it in the datalayer.

Here is what a typical Universal Analytics implementation looks like ( just drop only the container code on your site and you are good to go)

I’m investigating double pageview tracking today. I fixed it in Universal Analytics without GTM, and have reproduced it with GTM. If I follow your suggestions @victor2 then I get a single pageview on initial load but then no tracking of other pages. As I suspected, we need this code:

onPageChange((url, title) => {
    'event': '??????',
    '?????': url,
    /* no title needs to be sent? how does it figure it out? */

The GTM trigger “All Pages” is misleading because it means initial page loads, not all pages as single-page apps think of them.

All Pages == First Page

So as others pointed out to me earlier, we need the window.dataLayer.push calls in order to track all the pageviews.

I think what’s wrong in my instructions is that there are two triggers for Universal Analytics:

That is not a correct use of “All Pages”, which is triggering two page views to be sent to UA.

So there are two possible solutions:

  • Only use All Pages. But if this is possible (it might not be), then I need to fill in the blanks here:
onPageChange((url, title) => {
    'event': '??????',
    '?????': url,
    /* no title needs to be sent? how does it figure it out? */
  • Only use our custom trigger (virtualPageView). This is easy and will work, but it sounds like it is not the correct solution, which no one will define for me. :confounded:

That doesn’t seem to be true for single-page apps like Discourse because only the first pageview is being tracked when I do as you say. We definitely need to call the GTM api for each pageview. Can we use only the custom trigger and not the “All Pages” one?

I think @shri was right all along with his solution (only use the custom trigger, not All Pages).


I updated the instructions to fix the double pageviews. The changes:

  • Removed the All Pages trigger from the tag of type Universal Analytics.
  • Added the cookieDomain field to the UA tag with value auto.

I also removed the gtm_ua_domain_name setting because it can be controlled in GTM.


Actually, I think there is a problem!
Using this extension: Tag Assistant (by Google) - Chrome Web Store you can visualize the number of gtm tags in a page, if you open the first page or if you reload a page the tag will be only 2: GTM and Universal analytics
If you go switching pages between the forum, you will see multiple tags appearing.
3 or 4.
And the plugin will tell you that multiple istance of GTM are running.

I think we should try to fix this bug, probably it depends from the async loading of Ember


I installed it too and see that warning for each time I visit the /categories page. From the help:

We suggest you place only 1 instance of the GTM snippet on a webpage. Multiple GTM snippets don"t work well with each other
Multiple GTM snippets don"t work well with each other because of which the tag added via GTM may not always fire correctly.
Place only 1 instance of the GTM snippet on a webpage

We aren’t adding GTM code to the page on the fly as far as I know… I’ll look into it. @broz Are you seeing problems in your analytics data? Using the real-time view, do you see double pageviews or anything wrong?

This tool doesn’t work with Angular sites (1, 2), so I’m going to guess that it doesn’t work for Ember sites either. Also it seems to totally break some sites now that I’ve installed it in my browser. :angry:

More importantly, are you seeing errors in tracking? The Real Time > Content view in Universal Analytics is useful.

FYI, the google tag manager tags are added in the static html here:

I can’t see how it could be rendered again after the initial page load. The Tag Assistant is probably complaining that more than one pageview has been tracked, but that’s how single-page apps are supposed to work (according to Google).

1 Like

Understood your answer.
I don’t see problems in tracking but will do further testing, i have a beta-stage web site and for this reason with not so much visits i cannot compare the previous data with the one acquired from gtm.
I will let you know if i find anything, but i think it can be only a problem of the addon for chrome as you said.
Thank you for the quick answer.

1 Like

@broz Thanks, please report back. I have been told that the Universal Analytics real-time content view is not correct (even though it looks exactly correct to me). There are no tools that can be trusted to figure out if this is working apparently.


@neil so I’m a bit confused. Is there a reason we must create the custom variables and triggers? Is it so you can track a user journey instead of initial pageview?

I’m assuming that if we only want to measure total pageviews and standard metrics, then a standard implementation without custom variables and triggers, and set to fire on all pageviews would suffice?