Meta Pixel Setup on Umbraco | OpsBlu Docs

Meta Pixel Setup on Umbraco

Install Meta Pixel on Umbraco using Razor views, NuGet packages, or IIS integration

Implement Meta Pixel (Facebook Pixel) on your Umbraco site to track conversions, optimize ads, and build targeted audiences. This guide covers Razor view integration, NuGet packages, and IIS-level configuration for .NET environments.

Prerequisites

Before implementing Meta Pixel:

  • Meta Business Manager Account - Create at business.facebook.com
  • Meta Pixel Created - Set up Pixel in Events Manager
  • Pixel ID - Obtain your Pixel ID (format: 16-digit number)
  • Umbraco Backoffice Access - Administrator access required
  • .NET Development Environment - Visual Studio 2022+ or VS Code

Step 1: Create Meta Pixel Partial View

Location: /Views/Partials/Analytics/MetaPixel.cshtml

@using Umbraco.Cms.Core.Models.PublishedContent
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage

@{
    var pixelId = "YOUR_PIXEL_ID"; // Replace with your 16-digit Pixel ID

    // Get Pixel ID from Umbraco settings if stored in config
    if (Model?.Value<string>("metaPixelId") != null)
    {
        pixelId = Model.Value<string>("metaPixelId");
    }
}

@if (!string.IsNullOrEmpty(pixelId))
{
    <!-- Meta Pixel Code -->
    <script>
        !function(f,b,e,v,n,t,s)
        {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
        n.callMethod.apply(n,arguments):n.queue.push(arguments)};
        if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
        n.queue=[];t=b.createElement(e);t.async=!0;
        t.src=v;s=b.getElementsByTagName(e)[0];
        s.parentNode.insertBefore(t,s)}(window, document,'script',
        'https://connect.facebook.net/en_US/fbevents.js');

        fbq('init', '@pixelId');
        fbq('track', 'PageView');
    </script>
    <noscript>
        <img height="1" width="1" style="display:none"
             src="https://www.facebook.com/tr?id=@pixelId&ev=PageView&noscript=1"/>
    </noscript>
    <!-- End Meta Pixel Code -->
}

Step 2: Include in Master Layout

Location: /Views/Master.cshtml

@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage
@{
    Layout = null;
}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@Model?.Value("title") ?? Model?.Name</title>

    @* Include Meta Pixel in head *@
    @await Html.PartialAsync("~/Views/Partials/Analytics/MetaPixel.cshtml")

    @RenderSection("head", required: false)
</head>
<body>
    @RenderBody()
</body>
</html>

Step 3: Configure Pixel ID in Umbraco

Option A: Store in appsettings.json

{
  "Analytics": {
    "MetaPixel": {
      "PixelId": "YOUR_PIXEL_ID",
      "Enabled": true
    }
  }
}

Access in Razor:

@inject IConfiguration Configuration

@{
    var pixelId = Configuration["Analytics:MetaPixel:PixelId"];
    var enabled = Configuration.GetValue<bool>("Analytics:MetaPixel:Enabled");
}

@if (enabled && !string.IsNullOrEmpty(pixelId))
{
    @* Include Meta Pixel *@
}

Option B: Create Document Type Property

  1. Navigate to Settings → Document Types
  2. Select Site Settings document type
  3. Add property:
    • Name: Meta Pixel ID
    • Alias: metaPixelId
    • Editor: Textstring
  4. Save document type
  5. Navigate to Content → Site Settings
  6. Enter Pixel ID
  7. Save and publish

Step 4: Verify Installation

  1. Build and run Umbraco site
  2. Install Meta Pixel Helper Chrome extension
  3. Visit your site
  4. Click extension icon
  5. Verify Pixel fires and shows PageView event

Method 2: NuGet Package Integration

Install Community Package

Install-Package Our.Umbraco.MetaPixel

Or via .NET CLI:

dotnet add package Our.Umbraco.MetaPixel

Configure Package

In Program.cs (Umbraco 10+):

using Our.Umbraco.MetaPixel.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder.CreateUmbracoBuilder()
    .AddBackOffice()
    .AddWebsite()
    .AddDeliveryApi()
    .AddComposers()
    .AddMetaPixel(options =>
    {
        options.PixelId = builder.Configuration["Analytics:MetaPixel:PixelId"];
        options.AutoTrackPageView = true;
        options.EnableAdvancedMatching = true;
    })
    .Build();

var app = builder.Build();

// ... rest of configuration

Enable Advanced Matching

Advanced matching improves conversion attribution:

@using Umbraco.Cms.Core.Security
@inject IMemberManager MemberManager

@{
    var member = await MemberManager.GetCurrentMemberAsync();
}

<script>
    @if (member != null)
    {
        <text>
        fbq('init', 'YOUR_PIXEL_ID', {
            em: '@member.Email?.ToLowerInvariant()',
            fn: '@member.Name?.Split(' ').FirstOrDefault()?.ToLowerInvariant()',
            ln: '@member.Name?.Split(' ').LastOrDefault()?.ToLowerInvariant()'
        });
        </text>
    }
    else
    {
        <text>
        fbq('init', 'YOUR_PIXEL_ID');
        </text>
    }

    fbq('track', 'PageView');
</script>

Method 3: IIS Application-Level Integration

Create Meta Pixel HTTP Module

MetaPixelModule.cs:

using System;
using System.Web;
using System.Text;

namespace YourProject.Modules
{
    public class MetaPixelModule : IHttpModule
    {
        private const string PixelId = "YOUR_PIXEL_ID";

        public void Init(HttpApplication context)
        {
            context.PreRequestHandlerExecute += OnPreRequestHandlerExecute;
        }

        private void OnPreRequestHandlerExecute(object sender, EventArgs e)
        {
            HttpApplication app = (HttpApplication)sender;
            HttpContext context = app.Context;

            if (context.Response.ContentType == "text/html")
            {
                context.Response.Filter = new MetaPixelFilter(
                    context.Response.Filter,
                    PixelId
                );
            }
        }

        public void Dispose() { }
    }

    public class MetaPixelFilter : System.IO.Stream
    {
        private System.IO.Stream _responseStream;
        private string _pixelId;

        public MetaPixelFilter(System.IO.Stream stream, string pixelId)
        {
            _responseStream = stream;
            _pixelId = pixelId;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            string html = Encoding.UTF8.GetString(buffer, offset, count);

            string pixelScript = $@"
                <script>
                    !function(f,b,e,v,n,t,s)
                    {{if(f.fbq)return;n=f.fbq=function(){{n.callMethod?
                    n.callMethod.apply(n,arguments):n.queue.push(arguments)}};
                    if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
                    n.queue=[];t=b.createElement(e);t.async=!0;
                    t.src=v;s=b.getElementsByTagName(e)[0];
                    s.parentNode.insertBefore(t,s)}}(window, document,'script',
                    'https://connect.facebook.net/en_US/fbevents.js');
                    fbq('init', '{_pixelId}');
                    fbq('track', 'PageView');
                </script>
                <noscript>
                    <img height='1' width='1' style='display:none'
                         src='https://www.facebook.com/tr?id={_pixelId}&ev=PageView&noscript=1'/>
                </noscript>
            ";

            html = html.Replace("</head>", pixelScript + "</head>");

            byte[] data = Encoding.UTF8.GetBytes(html);
            _responseStream.Write(data, 0, data.Length);
        }

        // Required abstract members implementation
        public override bool CanRead => _responseStream.CanRead;
        public override bool CanSeek => _responseStream.CanSeek;
        public override bool CanWrite => _responseStream.CanWrite;
        public override long Length => _responseStream.Length;
        public override long Position
        {
            get => _responseStream.Position;
            set => _responseStream.Position = value;
        }
        public override void Flush() => _responseStream.Flush();
        public override int Read(byte[] buffer, int offset, int count)
            => _responseStream.Read(buffer, offset, count);
        public override long Seek(long offset, System.IO.SeekOrigin origin)
            => _responseStream.Seek(offset, origin);
        public override void SetLength(long value)
            => _responseStream.SetLength(value);
    }
}

Register in Web.config

For IIS Integrated Pipeline:

<configuration>
  <system.webServer>
    <modules>
      <add name="MetaPixelModule"
           type="YourProject.Modules.MetaPixelModule, YourProject" />
    </modules>
  </system.webServer>
</configuration>

Enhanced Umbraco Integration

Track Umbraco Member Data

@using Umbraco.Cms.Core.Security
@using Microsoft.AspNetCore.Http
@inject IMemberManager MemberManager
@inject IHttpContextAccessor HttpContextAccessor

@{
    var member = await MemberManager.GetCurrentMemberAsync();
}

<script>
    @if (member != null)
    {
        var email = member.Email;
        var firstName = member.Name?.Split(' ').FirstOrDefault();
        var lastName = member.Name?.Split(' ').Skip(1).FirstOrDefault();
        var phone = member.GetValue<string>("phone");
        var city = member.GetValue<string>("city");
        var state = member.GetValue<string>("state");
        var zip = member.GetValue<string>("zipCode");
        var country = member.GetValue<string>("country");

        <text>
        fbq('init', 'YOUR_PIXEL_ID', {
            em: '@email?.ToLowerInvariant()',
            fn: '@firstName?.ToLowerInvariant()',
            ln: '@lastName?.ToLowerInvariant()',
            ph: '@phone',
            ct: '@city?.ToLowerInvariant()',
            st: '@state?.ToLowerInvariant()',
            zp: '@zip',
            country: '@country?.ToLowerInvariant()'
        });
        </text>
    }
    else
    {
        <text>
        fbq('init', 'YOUR_PIXEL_ID');
        </text>
    }

    fbq('track', 'PageView');
</script>

Track Content Type and Template

<script>
    fbq('trackCustom', 'UmbracoPageView', {
        content_type: '@Model?.ContentType.Alias',
        content_name: '@Model?.Name',
        content_id: '@Model?.Id',
        template: '@ViewData["TemplateAlias"]'
    });
</script>

Conditional Loading by Environment

@inject Microsoft.Extensions.Hosting.IHostEnvironment HostEnvironment

@if (HostEnvironment.IsProduction())
{
    @await Html.PartialAsync("~/Views/Partials/Analytics/MetaPixel.cshtml")
}

Performance Optimization

Preconnect to Facebook Domains

<head>
    <link rel="preconnect" href="https://connect.facebook.net">
    <link rel="dns-prefetch" href="https://connect.facebook.net">

    @await Html.PartialAsync("~/Views/Partials/Analytics/MetaPixel.cshtml")
</head>

Defer Loading After Page Load

<script>
    // Defer Meta Pixel until after page load
    window.addEventListener('load', function() {
        !function(f,b,e,v,n,t,s)
        {if(f.fbq)return;n=f.fbq=function(){n.callMethod?
        n.callMethod.apply(n,arguments):n.queue.push(arguments)};
        if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
        n.queue=[];t=b.createElement(e);t.async=!0;
        t.src=v;s=b.getElementsByTagName(e)[0];
        s.parentNode.insertBefore(t,s)}(window, document,'script',
        'https://connect.facebook.net/en_US/fbevents.js');

        fbq('init', 'YOUR_PIXEL_ID');
        fbq('track', 'PageView');
    });
</script>

IIS-Specific Configuration

Enable IIS Compression

Web.config:

<configuration>
  <system.webServer>
    <urlCompression doStaticCompression="true" doDynamicCompression="true" />
    <httpCompression>
      <dynamicTypes>
        <add mimeType="application/javascript" enabled="true" />
        <add mimeType="text/javascript" enabled="true" />
      </dynamicTypes>
    </httpCompression>
  </system.webServer>
</configuration>

Set Content Security Policy

Allow Meta Pixel domains:

<configuration>
  <system.webServer>
    <httpProtocol>
      <customHeaders>
        <add name="Content-Security-Policy"
             value="script-src 'self' 'unsafe-inline' https://connect.facebook.net; img-src 'self' https://www.facebook.com;" />
      </customHeaders>
    </httpProtocol>
  </system.webServer>
</configuration>

Multi-Pixel Implementation

Track Multiple Pixels

@{
    var primaryPixelId = "PRIMARY_PIXEL_ID";
    var secondaryPixelId = "SECONDARY_PIXEL_ID";
}

<script>
    // Initialize both pixels
    fbq('init', '@primaryPixelId');
    fbq('init', '@secondaryPixelId');

    // Track PageView for both
    fbq('track', 'PageView');
</script>

Track Specific Pixels for Events

<script>
    // Track only on primary pixel
    fbq('trackSingle', '@primaryPixelId', 'Lead', {
        content_name: 'Newsletter Signup'
    });

    // Track on both pixels
    fbq('track', 'CompleteRegistration');
</script>

Validation and Testing

Use Meta Pixel Helper

  1. Install Meta Pixel Helper Chrome extension
  2. Navigate to your Umbraco site
  3. Click extension icon
  4. Verify:
    • Pixel detected
    • PageView event fires
    • No errors shown
    • Advanced matching parameters present (if configured)

Test Events Manager

  1. Navigate to Meta Events Manager
  2. Select your Pixel
  3. Click Test Events
  4. Enter your website URL
  5. Click Open Website
  6. Verify events appear in Test Events tab

Browser DevTools Testing

  1. Open DevTools → Console
  2. Type: fbq and press Enter
  3. Verify fbq function exists
  4. Type: fbq('track', 'PageView') to manually fire event
  5. Check Network tab for requests to facebook.com/tr

Common Issues

Pixel Not Firing

Cause: Incorrect Pixel ID or syntax error Solution:

Duplicate Events

Cause: Multiple Pixel implementations Solution:

  • Search codebase for duplicate fbq references
  • Remove redundant partial views
  • Check for package + manual implementation conflict

Umbraco Cache Issues

Cause: Cache not cleared after code changes Solution:

# Clear Umbraco cache
Remove-Item -Path "App_Data\TEMP\*" -Recurse -Force

IIS Module Not Working

Cause: Module not registered or wrong pipeline mode Solution:

  • Verify Web.config registration
  • Check IIS application pool mode
  • Restart IIS: iisreset

Content Security Policy Blocking

Cause: CSP header blocks Meta Pixel Solution:

<add name="Content-Security-Policy"
     value="script-src 'self' 'unsafe-inline' https://connect.facebook.net; connect-src https://www.facebook.com;" />

GDPR and Privacy Compliance

<script>
    // Disable automatic initialization
    fbq('consent', 'revoke');

    // Wait for user consent
    function enableMetaPixel() {
        fbq('consent', 'grant');
        fbq('track', 'PageView');
    }

    // Call enableMetaPixel() when user accepts cookies
    document.getElementById('accept-cookies').addEventListener('click', function() {
        enableMetaPixel();
    });
</script>

Limited Data Use (LDU)

For California privacy compliance:

<script>
    fbq('dataProcessingOptions', ['LDU'], 1, 1000); // California, USA
    fbq('init', 'YOUR_PIXEL_ID');
    fbq('track', 'PageView');
</script>

Next Steps