Microsoft Advertising Cross-Domain Tracking | OpsBlu Docs

Microsoft Advertising Cross-Domain Tracking

Configure cross-domain and subdomain tracking for Microsoft Advertising UET tag to maintain attribution across multiple domains.

Overview

Cross-domain tracking maintains user session and conversion attribution when users navigate between multiple domains or subdomains. Without proper configuration, each domain transition creates a new session, breaking the attribution chain from ad click to conversion.

When Cross-Domain Tracking is Needed

Common Scenarios

  1. Multi-domain checkout: User browses on www.example.com, checks out on checkout.example.com
  2. Third-party payment processor: User redirects to secure-payments.processor.com during purchase
  3. Separate domains: Main site on example.com, blog on blog.example.net
  4. Subdomain separation: Marketing site on www.example.com, app on app.example.com
  5. Regional domains: US on example.com, UK on example.co.uk

Signs You Need Cross-Domain Tracking

  • Conversions attributed to "(direct)" instead of ad campaign
  • Session breaks between browsing and checkout
  • Lost MSCLKID parameter during domain transitions
  • Lower conversion rates than expected
  • Attribution mismatches between platforms

How Cross-Domain Tracking Works

MSCLKID Parameter Preservation

Microsoft Advertising uses the msclkid (Microsoft Click ID) parameter for attribution:

  1. User clicks ad on Microsoft Advertising
  2. Landing URL includes msclkid parameter: example.com?msclkid=abc123xyz
  3. UET tag stores msclkid in first-party cookie
  4. When user navigates to different domain, msclkid must be passed
  5. UET tag on second domain reads msclkid and maintains attribution
  6. Conversion properly attributed to original ad click

Cookies don't work across different domains due to browser security:

  • Cookie set on example.com cannot be read by checkout.example.net
  • Cookie set on www.example.com can be read by app.example.com (same root domain)
  • Solution: Pass msclkid as URL parameter during transitions

Subdomain Tracking

Same Root Domain

For subdomains under the same root domain (e.g., www.example.com, app.example.com, checkout.example.com):

No special configuration needed. UET automatically sets cookies for the root domain:

// UET automatically sets cookie for .example.com
// Readable by all subdomains: www.example.com, app.example.com, etc.

Step 2: Deploy UET Tag on All Subdomains

Ensure the same UET tag ID is deployed on all subdomains:

<!-- On www.example.com -->
<script>
(function(w,d,t,r,u){
  var f,n,i;
  w[u]=w[u]||[],f=function(){
    var o={ti:"12345678"}; // Same tag ID
    // ... rest of UET tag
  }
  // ...
})(window,document,"script","//bat.bing.com/bat.js","uetq");
</script>

<!-- On checkout.example.com -->
<script>
(function(w,d,t,r,u){
  var f,n,i;
  w[u]=w[u]||[],f=function(){
    var o={ti:"12345678"}; // Same tag ID
    // ... rest of UET tag
  }
  // ...
})(window,document,"script","//bat.bing.com/bat.js","uetq");
</script>

Step 3: Test

  1. Click ad and land on www.example.com with msclkid parameter
  2. Navigate to checkout.example.com
  3. Complete conversion
  4. Verify conversion attributes to ad campaign (not direct)

Different Root Domains

For completely different domains (e.g., example.com and example.net), additional configuration is required.

Cross-Domain Tracking Implementation

Step 1: Enable Auto-Tagging

In Microsoft Advertising:

  1. Navigate to Accounts > Account settings
  2. Scroll to Tracking template section
  3. Ensure Auto-tagging is enabled
  4. Save settings

Auto-tagging automatically appends msclkid to all destination URLs.

Add JavaScript to preserve msclkid when linking between domains:

// Add to all pages on source domain (example.com)
(function() {
  // Get MSCLKID from URL
  function getMSCLKID() {
    var urlParams = new URLSearchParams(window.location.search);
    return urlParams.get('msclkid');
  }

  // Append MSCLKID to cross-domain links
  function appendMSCLKIDToLinks() {
    var msclkid = getMSCLKID();

    // Also check for MSCLKID in cookie (set by UET)
    if (!msclkid) {
      var cookies = document.cookie.split(';');
      for (var i = 0; i < cookies.length; i++) {
        var cookie = cookies[i].trim();
        if (cookie.indexOf('_uetmsclkid=') === 0) {
          msclkid = cookie.substring('_uetmsclkid='.length);
          break;
        }
      }
    }

    if (msclkid) {
      // Find all links to checkout domain
      var links = document.querySelectorAll('a[href*="checkout.example.com"]');
      links.forEach(function(link) {
        var url = new URL(link.href);
        url.searchParams.set('msclkid', msclkid);
        link.href = url.toString();
      });
    }
  }

  // Run on page load
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', appendMSCLKIDToLinks);
  } else {
    appendMSCLKIDToLinks();
  }
})();

Step 3: Preserve Through Redirects

If using server-side redirects:

<?php
// PHP example - preserve msclkid through redirect
$msclkid = isset($_GET['msclkid']) ? $_GET['msclkid'] : '';
$redirect_url = 'https://checkout.example.com/cart';

if ($msclkid) {
  $redirect_url .= '?msclkid=' . urlencode($msclkid);
}

header('Location: ' . $redirect_url);
exit;
?>
// Node.js example
app.get('/checkout', (req, res) => {
  const msclkid = req.query.msclkid;
  let redirectUrl = 'https://checkout.example.com/cart';

  if (msclkid) {
    redirectUrl += `?msclkid=${encodeURIComponent(msclkid)}`;
  }

  res.redirect(redirectUrl);
});

Step 4: Deploy UET Tag on Second Domain

Ensure UET tag with same tag ID is on the destination domain:

<!-- On checkout.example.com -->
<script>
(function(w,d,t,r,u){
  var f,n,i;
  w[u]=w[u]||[],f=function(){
    var o={ti:"12345678"}; // Same tag ID as example.com
    // ... rest of UET tag
  }
  // ...
})(window,document,"script","//bat.bing.com/bat.js","uetq");
</script>

UET will automatically read msclkid from URL parameter and restore attribution.

For more control, manually store and retrieve msclkid:

On Source Domain (example.com)

// Capture and store MSCLKID
function getMSCLKIDFromURL() {
  var urlParams = new URLSearchParams(window.location.search);
  return urlParams.get('msclkid');
}

function storeMSCLKID() {
  var msclkid = getMSCLKIDFromURL();
  if (msclkid) {
    // Store in localStorage (works cross-domain if manually transferred)
    localStorage.setItem('msclkid', msclkid);

    // Store in cookie for 90 days
    var expires = new Date();
    expires.setTime(expires.getTime() + (90 * 24 * 60 * 60 * 1000));
    document.cookie = 'msclkid=' + msclkid + ';expires=' + expires.toUTCString() + ';path=/';
  }
}

storeMSCLKID();

On Destination Domain (checkout.example.com)

// Retrieve MSCLKID and add to conversion
function getMSCLKIDForConversion() {
  // Check URL first
  var urlParams = new URLSearchParams(window.location.search);
  var msclkid = urlParams.get('msclkid');

  // If not in URL, check localStorage (manual transfer needed)
  if (!msclkid) {
    msclkid = localStorage.getItem('msclkid');
  }

  return msclkid;
}

// When conversion occurs
var msclkid = getMSCLKIDForConversion();
if (msclkid) {
  // Add to page URL so UET can pick it up
  if (!window.location.search.includes('msclkid')) {
    history.replaceState(null, '', window.location.pathname + '?msclkid=' + msclkid);
  }
}

// Fire conversion
window.uetq = window.uetq || [];
window.uetq.push('event', 'purchase', {
  'revenue_value': 99.99,
  'currency': 'USD'
});

Payment Processor Integration

Third-Party Checkout (Stripe, PayPal, etc.)

When users redirect to payment processor and return:

Step 1: Pass MSCLKID to Payment Processor

// Before redirecting to payment processor
function redirectToCheckout() {
  var msclkid = getMSCLKID(); // From URL or cookie
  var returnUrl = 'https://example.com/thank-you';

  // Append MSCLKID to return URL
  if (msclkid) {
    returnUrl += '?msclkid=' + encodeURIComponent(msclkid);
  }

  // Redirect to payment processor with return URL
  window.location = 'https://checkout.stripe.com/pay?return_url=' + encodeURIComponent(returnUrl);
}

Step 2: Configure Return URL

Most payment processors allow custom return URLs. Include msclkid:

Stripe Example:

stripe.redirectToCheckout({
  sessionId: 'sess_123',
  successUrl: 'https://example.com/thank-you?msclkid={{MSCLKID}}&session_id={CHECKOUT_SESSION_ID}',
  cancelUrl: 'https://example.com/cancel?msclkid={{MSCLKID}}'
});

PayPal Example:

<input type="hidden" name="return" value="https://example.com/thank-you?msclkid=abc123xyz">

Step 3: Fire Conversion on Return

When user returns from payment processor with msclkid:

// On thank-you page
var urlParams = new URLSearchParams(window.location.search);
var msclkid = urlParams.get('msclkid');

// UET tag will automatically read msclkid from URL
window.uetq = window.uetq || [];
window.uetq.push('event', 'purchase', {
  'revenue_value': orderTotal,
  'currency': 'USD'
});

Google Tag Manager Cross-Domain Configuration

GTM Method

Step 1: Create Utility Variables

Variable: Get MSCLKID from URL

  • Variable Type: URL
  • Component Type: Query
  • Query Key: msclkid
  • Variable Name: URL - MSCLKID

Variable: Get MSCLKID from Cookie

  • Variable Type: 1st Party Cookie
  • Cookie Name: _uetmsclkid
  • Variable Name: Cookie - MSCLKID

Tag Type: Custom HTML

<script>
(function() {
  var msclkid = {{URL - MSCLKID}} || {{Cookie - MSCLKID}};

  if (msclkid) {
    // Decorate cross-domain links
    var links = document.querySelectorAll('a[href*="checkout.example.com"], a[href*="otherdomain.com"]');
    links.forEach(function(link) {
      link.addEventListener('click', function() {
        var url = new URL(this.href);
        if (!url.searchParams.has('msclkid')) {
          url.searchParams.set('msclkid', msclkid);
          this.href = url.toString();
        }
      });
    });
  }
})();
</script>

Trigger: All Pages

Step 3: Test in GTM Preview Mode

  1. Click Preview in GTM
  2. Click ad with msclkid parameter
  3. Click link to cross-domain site
  4. Verify msclkid is appended to URL
  5. Complete conversion and verify attribution

Testing Cross-Domain Tracking

Test Plan

  1. Generate test click:

    • Use Microsoft Advertising ad preview tool
    • Click ad to generate msclkid parameter
    • Note the msclkid value
  2. Navigate through funnel:

    • Land on first domain with msclkid
    • Navigate to second domain
    • Check URL includes msclkid parameter
    • Check browser DevTools > Application > Cookies for UET cookie
  3. Complete conversion:

    • Fire conversion event
    • Use UET Tag Helper to verify
    • Check Network tab for bat.bing.com request with msclkid
  4. Verify in Microsoft Advertising:

    • Wait 2-4 hours for processing
    • Check conversion appears in reporting
    • Verify attributed to correct campaign (not "(direct)")

Debugging Checklist

  • Auto-tagging enabled in Microsoft Advertising account
  • Same UET tag ID on all domains
  • Links between domains include msclkid parameter
  • Redirects preserve msclkid parameter
  • Payment processor return URLs include msclkid
  • UET Tag Helper shows tag firing on all domains
  • Network requests show msclkid in bat.bing.com calls
  • Conversions attributed to ad campaigns, not "(direct)"

Common Issues & Solutions

MSCLKID Lost During Navigation

Symptom: Parameter missing when navigating between domains

Solutions:

  1. Check links include msclkid in href
  2. Verify JavaScript decorator runs before clicks
  3. Ensure redirects preserve query parameters
  4. Check form submissions include msclkid as hidden field

Conversion Attributed to "(direct)"

Symptom: Conversions show as direct traffic instead of ad campaign

Solutions:

  1. Verify msclkid parameter present on conversion page URL
  2. Check UET tag is same ID on all domains
  3. Ensure conversion fires within 90-day click window
  4. Verify auto-tagging is enabled

Multiple Domains Don't Share Cookies

Symptom: Cookie set on domain A not readable on domain B

Expected behavior: Cookies can't be shared across different root domains

Solution: Use URL parameter method to pass msclkid between domains

Payment Processor Strips Parameters

Symptom: msclkid removed during payment redirect

Solutions:

  1. Store msclkid in payment metadata/custom fields
  2. Return msclkid in webhook/callback
  3. Use server-side session to preserve value
  4. Configure payment processor return URL with msclkid

Advanced Scenarios

Multi-Step Checkout Across Domains

For complex funnels spanning multiple domains:

example.com (product browse)
  ↓
cart.example.com (shopping cart)
  ↓
checkout.example.net (checkout form)
  ↓
secure.processor.com (payment)
  ↓
example.com/thank-you (confirmation)

Solution: Pass msclkid at each transition:

  1. example.com → cart.example.com: Append to "Add to Cart" link
  2. cart.example.com → checkout.example.net: Append to "Checkout" button
  3. checkout.example.net → secure.processor.com: Include in payment return URL
  4. secure.processor.com → example.com/thank-you: Return in redirect

Regional Domain Tracking

For businesses with regional domains (example.com, example.co.uk, example.de):

Option 1: Separate UET Tags

  • Create separate UET tag for each region
  • Create separate conversion goals
  • Report shows conversions by region

Option 2: Shared UET Tag

  • Use same UET tag across all regional domains
  • Implement cross-domain tracking
  • Use event labels to differentiate regions:
window.uetq = window.uetq || [];
window.uetq.push('event', 'purchase', {
  'revenue_value': 99.99,
  'currency': 'GBP',
  'event_label': 'uk' // Regional identifier
});

Single Sign-On (SSO) Flows

When users authenticate on separate domain:

app.example.com (main app)
  ↓
sso.example.com (authentication)
  ↓
app.example.com (return after login)

Solution: SSO redirect should preserve msclkid:

// On app.example.com
function redirectToSSO() {
  var msclkid = getMSCLKID();
  var returnUrl = 'https://app.example.com/dashboard';

  if (msclkid) {
    returnUrl += '?msclkid=' + encodeURIComponent(msclkid);
  }

  window.location = 'https://sso.example.com/login?return=' + encodeURIComponent(returnUrl);
}

Best Practices

  1. Use same UET tag ID: All domains must use identical tag ID
  2. Preserve parameters: Always append msclkid when linking between domains
  3. Test thoroughly: Verify attribution before launching campaigns
  4. Document flows: Map out all domain transitions in user journey
  5. Monitor attribution: Regularly check for "(direct)" conversions that should be attributed
  6. Server-side backup: Store msclkid server-side for critical conversions
  7. 90-day window: Conversions must occur within 90 days of click
  8. HTTPS everywhere: Use HTTPS on all domains for security

Cross-Domain Tracking Checklist

  • Auto-tagging enabled in Microsoft Advertising
  • Same UET tag ID deployed on all domains
  • Links between domains include msclkid parameter
  • Form submissions preserve msclkid (hidden field or URL)
  • Redirects maintain msclkid in query string
  • Payment processor return URLs include msclkid
  • JavaScript decorator runs on all cross-domain links
  • GTM cross-domain configuration tested (if using GTM)
  • Test conversions attribute correctly (not to direct)
  • Documentation created for all domain transitions

Next Steps