Set up Google Analytics in .NET the Right Way.

Are you unintentionally polluting your Google Analytics data?

If you’re using the same Google Analytics measurement ID across all your environments – production, test, and local dev – you’re mixing test traffic with real user data. This inflates metrics, skews conversions, and can completely derail decision-making.

In this guide, we’ll walk through how to keep your analytics clean using environment-specific configuration in .NET. We’ll do it with the Options Pattern – a clean, testable way to manage config in ASP.NET Core. You can also check out the YouTube video version of this tutorial.

Why This Matters

Setting up Google Analytics feels simple: create a property → paste the script tag on your site → done.

But behind the scenes, most apps have multiple environments:

  • Production: Where you’ve deployed your app for real users to access.
  • Local development: Your machine.
  • Test/UAT/Staging/Performance: Internal environments used by developers and QA.

If all of these environments report to the same GA property, your metrics become meaningless. Every local refresh, test login, and staging deployment inflates your stats.

The Fix: Use Separate Measurement IDs

To fix this:

  • Create one GA property per environment
  • Load the appropriate measurement ID via configuration
  • Inject it into your site layout dynamically

Here’s how to do it.

Step-by-Step Implementation

1. Create a GA Property Per Environment

Head over to Google Analytics and create a separate property for each environment that you have, for example:

  • Production
  • Test
  • Local Development

Each will have a unique Measurement ID (e.g., G-ABC123XYZ). Copy and save each one.

2. Configure appsettings.json Files

Start by adding a section to your root appsettings.json:

"GoogleAnalytics": {
  "MeasurementId": "",
  "Enabled": false
}

Then, override it in each environment-specific config file (e.g., appsettings.Production.json):

"GoogleAnalytics": {
  "MeasurementId": "G-ABC123XYZ",
  "Enabled": true
}

Set Enabled to false for local dev and true for test and production.

3. Create a Strongly-Typed Options Class

Create a new class to bind to the GoogleAnalytics section:

public class GoogleAnalyticsOptions
{
    public const string SectionName = "GoogleAnalytics";
    public string MeasurementId { get; set; }
    public bool Enabled { get; set; }
}

Register it in Program.cs:

builder.Services.Configure<GoogleAnalyticsOptions>(
    builder.Configuration.GetSection(GoogleAnalyticsOptions.SectionName));

4. Add the GA Script Dynamically in _Layout.cshtml

Inject your options into the layout:

@inject IOptions<GoogleAnalyticsOptions> GoogleAnalyticsOptions

Now, conditionally render the GA script:

@if (GoogleAnalyticsOptions.Value.Enabled && 
     !string.IsNullOrWhiteSpace(GoogleAnalyticsOptions.Value.MeasurementId))
{
    <script async src="https://www.googletagmanager.com/gtag/js?id=@GoogleAnalyticsOptions.Value.MeasurementId"></script>
    <script>
        window.dataLayer = window.dataLayer || [];
        function gtag() { dataLayer.push(arguments); }
        gtag('js', new Date());
        gtag('config', '@GoogleAnalyticsOptions.Value.MeasurementId');
    </script>
}

This ensures the correct tracking ID is loaded only in the environments where tracking is enabled.

5. Test It

To test your setup:

  • Launch your site locally and check the page source. You should see the development GA ID.
  • Change the ASPNETCORE_ENVIRONMENT in launchSettings.json to Test or Production.
  • Relaunch and confirm the tag updates accordingly.

Be cautious: If you run your site with production settings locally, you may pollute your real analytics.

Conclusion

With just a bit of configuration and a few lines of code, you can protect the integrity of your Google Analytics data.

By assigning a unique measurement ID to each environment and loading it using the Options Pattern, you ensure your stats reflect real user behaviour—not dev or QA activity.

Want to go further? Check out my video on automating Azure deployments using GitHub Actions.

CATEGORIES

No category

Comments are closed