Meta Pixel Setup on Craft CMS | OpsBlu Docs

Meta Pixel Setup on Craft CMS

Complete guide to implementing Meta (Facebook) Pixel on Craft CMS using Twig templates, environment configuration, and best practices.

Learn how to implement Meta Pixel (Facebook Pixel) on your Craft CMS website using Twig templates, plugins, and environment-aware configuration.

Prerequisites

  • Active Meta Business Manager account
  • Meta Pixel ID (format: 123456789012345)
  • Craft CMS 4.x or 5.x installation
  • Basic understanding of Twig templating

Method 1: Direct Twig Template Integration

Step 1: Configure Environment Variables

Add your Meta Pixel ID to .env:

# .env
META_PIXEL_ID="123456789012345"
ENVIRONMENT="production"

Step 2: Create Meta Pixel Partial Template

Create a reusable partial at templates/_analytics/meta-pixel.twig:

{# templates/_analytics/meta-pixel.twig #}

{% set pixelId = getenv('META_PIXEL_ID') %}
{% set environment = craft.app.config.general.environment %}

{# Only load Meta Pixel in production and not during Live Preview #}
{% if pixelId and environment == 'production' and not craft.app.request.isLivePreview %}

<!-- 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 -->

{% elseif craft.app.config.general.devMode %}
<!-- Meta Pixel disabled in dev mode -->
{% endif %}

Step 3: Include in Base Layout

Add Meta Pixel to your main layout template:

{# templates/_layouts/base.twig #}
<!DOCTYPE html>
<html lang="{{ currentSite.language }}">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ title ?? siteName }}</title>

    {# Meta Pixel - Load in <head> #}
    {{ include('_analytics/meta-pixel') }}

    {# Other head content #}
    {{ head() }}
</head>
<body>
    {{ beginBody() }}

    {# Main content #}
    {% block content %}{% endblock %}

    {{ endBody() }}
</body>
</html>

Method 2: Using SEOmatic Plugin

Step 1: Install SEOmatic

cd /path/to/craft-project
composer require nystudio107/craft-seomatic

Step 2: Configure in Control Panel

  1. Navigate to SEOmatic → Tracking Scripts
  2. Click Facebook Pixel
  3. Enter your Pixel ID in the Facebook Pixel ID field
  4. Configure environment settings:
    • Environment: Set to production only
    • Send Page View: Enabled by default

Step 3: Environment Configuration

Create or update config/seomatic.php:

<?php

use craft\helpers\App;

return [
    '*' => [
        'pluginName' => 'SEOmatic',
        'renderEnabled' => true,
        'environment' => App::env('ENVIRONMENT') ?: 'production',
    ],
    'production' => [
        'renderEnabled' => true,
        'facebookPixel' => [
            'id' => App::env('META_PIXEL_ID'),
            'sendPageView' => true,
        ],
    ],
    'staging' => [
        'renderEnabled' => false,
    ],
    'dev' => [
        'renderEnabled' => false,
    ],
];

Method 3: Custom Module Implementation

For advanced control and server-side events:

Step 1: Create Meta Pixel Module

mkdir -p modules/metapixel

Create modules/metapixel/Module.php:

<?php

namespace modules\metapixel;

use Craft;
use craft\events\TemplateEvent;
use craft\web\View;
use yii\base\Event;
use yii\base\Module as BaseModule;

class Module extends BaseModule
{
    public static $instance;

    public function init()
    {
        parent::init();
        self::$instance = $this;

        // Inject Meta Pixel script
        Event::on(
            View::class,
            View::EVENT_END_HEAD,
            [$this, 'injectMetaPixel']
        );
    }

    public function injectMetaPixel(TemplateEvent $event)
    {
        // Only inject in production
        if (Craft::$app->config->general->environment !== 'production') {
            return;
        }

        // Skip during Live Preview
        if (Craft::$app->request->isLivePreview) {
            return;
        }

        $pixelId = getenv('META_PIXEL_ID');
        if (!$pixelId) {
            return;
        }

        $script = $this->renderMetaPixelScript($pixelId);
        $event->output = str_replace('</head>', $script . '</head>', $event->output);
    }

    private function renderMetaPixelScript(string $pixelId): string
    {
        return <<<HTML
<!-- 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 -->
HTML;
    }
}

Step 2: Bootstrap the Module

Edit config/app.php:

<?php

use craft\helpers\App;

return [
    'modules' => [
        'metapixel' => [
            'class' => \modules\metapixel\Module::class,
        ],
    ],
    'bootstrap' => ['metapixel'],
];

Multi-Site Configuration

For Craft multi-site installations with different pixels:

{# templates/_analytics/meta-pixel.twig #}

{% set pixelIds = {
    'siteHandleOne': getenv('META_PIXEL_SITE_ONE'),
    'siteHandleTwo': getenv('META_PIXEL_SITE_TWO'),
    'siteHandleThree': getenv('META_PIXEL_SITE_THREE'),
} %}

{% set pixelId = pixelIds[currentSite.handle] ?? null %}

{% if pixelId and craft.app.config.general.environment == 'production' %}
<!-- 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 -->
{% endif %}

Environment variables:

# .env
META_PIXEL_SITE_ONE="111111111111111"
META_PIXEL_SITE_TWO="222222222222222"
META_PIXEL_SITE_THREE="333333333333333"

Advanced Event Matching

Track user data for improved attribution (GDPR-compliant):

{# templates/_analytics/meta-pixel.twig - With Advanced Matching #}

{% set pixelId = getenv('META_PIXEL_ID') %}
{% set currentUser = currentUser ?? null %}

{% if pixelId and craft.app.config.general.environment == 'production' %}
<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');

{% if currentUser %}
// Advanced matching with hashed user data
fbq('init', '{{ pixelId }}', {
  em: '{{ currentUser.email|hash('sha256') }}',
  fn: '{{ currentUser.firstName|hash('sha256') }}',
  ln: '{{ currentUser.lastName|hash('sha256') }}'
});
{% else %}
fbq('init', '{{ pixelId }}');
{% endif %}

fbq('track', 'PageView');
</script>
{% endif %}

Content Security Policy (CSP)

Configure CSP headers to allow Meta Pixel:

// config/general.php
return [
    '*' => [
        'securityHeaders' => [
            'Content-Security-Policy' => implode('; ', [
                "default-src 'self'",
                "script-src 'self' 'unsafe-inline' https://connect.facebook.net",
                "img-src 'self' data: https://www.facebook.com",
                "connect-src 'self' https://www.facebook.com",
            ]),
        ],
    ],
];

Implement GDPR-compliant loading based on consent:

{# templates/_analytics/meta-pixel.twig - With Cookie Consent #}

{% set cookieConsent = craft.cookies.get('cookie_consent') %}
{% set pixelId = getenv('META_PIXEL_ID') %}

{% if cookieConsent == 'all' or cookieConsent == 'marketing' %}
  {% if pixelId and craft.app.config.general.environment == 'production' %}
  <!-- 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>
  {% endif %}
{% else %}
  {# Show consent banner #}
  {{ include('_components/cookie-consent-banner') }}
{% endif %}

Performance Optimization

DNS Prefetch and Preconnect

Add resource hints to improve loading performance:

{# In <head> section #}
<link rel="dns-prefetch" href="//connect.facebook.net">
<link rel="preconnect" href="https://connect.facebook.net" crossorigin>
<link rel="preconnect" href="https://www.facebook.com" crossorigin>

Lazy Loading

Delay Meta Pixel loading for non-critical pages:

<script>
  // Load Meta Pixel 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', '{{ pixelId }}');
    fbq('track', 'PageView');
  });
</script>

Testing and Validation

Verify Pixel Installation

  1. Browser Console: Check for fbq function
  2. Network Tab: Verify requests to facebook.com
  3. Meta Pixel Helper: Install Meta Pixel Helper Chrome extension
  4. Events Manager: Check Test Events in Meta Events Manager

Debug Mode in Development

Enable debug output for local testing:

{% if craft.app.config.general.devMode %}
<script>
  // Mock fbq for development
  window.fbq = window.fbq || function() {
    console.log('Meta Pixel Event:', arguments);
  };

  // Initialize mock pixel
  fbq('init', '{{ pixelId ?? 'DEV_MODE' }}');
  fbq('track', 'PageView');

  console.log('Meta Pixel running in debug mode');
</script>
{% endif %}

Test Events Tool

Use Meta's Test Events feature:

{% set testEventCode = craft.app.request.getParam('test_event_code') %}

{% if testEventCode %}
<script>
  fbq('init', '{{ pixelId }}', {}, {
    testEventCode: '{{ testEventCode }}'
  });
</script>
{% endif %}

Access with: https://yoursite.com?test_event_code=TEST12345

Common Issues and Solutions

Pixel Not Loading

Add debug output:

{% if craft.app.config.general.devMode %}
<!-- Meta Pixel Debug Info -->
{% set pixelId = getenv('META_PIXEL_ID') %}
<!-- Pixel ID: {{ pixelId ? pixelId : 'NOT SET' }} -->
<!-- Environment: {{ craft.app.config.general.environment }} -->
<!-- Live Preview: {{ craft.app.request.isLivePreview ? 'Yes' : 'No' }} -->
{% endif %}

AdBlockers Blocking Pixel

AdBlockers commonly block Meta Pixel. Solutions:

  1. Use Conversions API (server-side tracking)
  2. Inform users about content personalization
  3. Test with AdBlocker disabled

CSP Header Blocking

Ensure CSP headers allow Facebook domains:

'script-src' => "'self' 'unsafe-inline' https://connect.facebook.net",
'img-src' => "'self' data: https://www.facebook.com",
'connect-src' => "'self' https://www.facebook.com",

User Privacy Considerations

Limited Data Use

For California residents (CCPA compliance):

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

Disable Automatic Configuration

For better privacy control:

<script>
  fbq('init', '{{ pixelId }}', {}, {
    autoConfig: false,
    debug: {{ craft.app.config.general.devMode ? 'true' : 'false' }}
  });
  fbq('track', 'PageView');
</script>

User Role Exclusion

Exclude admin users from tracking:

{% set currentUser = currentUser ?? null %}
{% set shouldLoadPixel = not currentUser or not currentUser.isInGroup('admins') %}

{% if shouldLoadPixel %}
    {# Load Meta Pixel #}
{% endif %}

GraphQL API Integration

For headless Craft CMS, expose pixel configuration:

query {
  globalSet(handle: "siteSettings") {
    ... on siteSettings_GlobalSet {
      metaPixelId
      enableMetaPixel
    }
  }
}

Implement in frontend:

// Frontend JavaScript
function loadMetaPixel(pixelId) {
  !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');
}

Next Steps

Resources