Server-Side Tracking Problems | Blue Frog Docs

Server-Side Tracking Problems

Fix server-side tracking issues to improve data accuracy and bypass ad blockers

Server-Side Tracking Problems

What This Means

Server-side tracking sends analytics and conversion data directly from your web server to platforms like Google Analytics, Facebook, and TikTok, rather than from the user's browser (client-side). This bypasses ad blockers, browser privacy features (ITP, ETP), and cookie restrictions, providing more accurate tracking data. When server-side tracking fails or is improperly configured, you miss conversions, can't attribute sales correctly, waste ad spend, and make decisions based on incomplete data.

Client-Side vs Server-Side Tracking

Client-Side Tracking (Traditional):

// Browser executes this code
gtag('event', 'purchase', {
    'transaction_id': '12345',
    'value': 99.99,
    'currency': 'USD'
});

// Problems:
// ❌ Ad blockers prevent request
// ❌ Safari ITP limits cookies
// ❌ iOS 14.5+ ATT blocks tracking
// ❌ User can see/modify request
// ❌ Privacy extensions block it

Server-Side Tracking (Modern):

// Browser sends data to YOUR server
fetch('/api/track-purchase', {
    method: 'POST',
    body: JSON.stringify({
        transaction_id: '12345',
        value: 99.99,
        currency: 'USD'
    })
});

// YOUR server sends to Google/Facebook
// ✅ Can't be blocked by ad blockers
// ✅ No cookie restrictions
// ✅ Secure (server-to-server)
// ✅ More accurate data
// ✅ Better attribution

Impact on Your Business

Data Accuracy:

  • 30-50% more conversions tracked - Bypass ad blockers
  • Better attribution - Know which ads actually work
  • Consistent tracking - Not affected by browser settings
  • Reduced data loss - iOS, Safari tracking protection bypassed

Privacy & Compliance:

  • First-party data - Own your tracking data
  • GDPR compliant - More control over data
  • Reduced PII exposure - Sensitive data stays on server
  • Cookie-less future - Prepared for cookiepocalypse

Ad Performance:

  • Better ROAS - Optimize with accurate data
  • Smart Bidding works - More conversions = better optimization
  • Reduced wasted spend - Stop paying for untracked conversions
  • Competitive advantage - Better data than competitors

Without Server-Side Tracking:

100 actual purchases
↓
50-70 tracked by browser (30-50% blocked)
↓
Google Ads optimizes on incomplete data
↓
Wasted budget + poor performance

With Server-Side Tracking:

100 actual purchases
↓
90-98 tracked by server (2-10% technical failures)
↓
Google Ads has accurate data
↓
Better optimization + ROI

How to Diagnose

Method 1: Compare Client vs Server Data

Check for large discrepancies:

  1. Google Analytics 4ReportsAcquisition
  2. Compare sessions/conversions
  3. Look for 20%+ difference
Expected difference: 5-15% (normal)

Red flag: 30%+ difference
- Client: 1000 conversions
- Server: 700 conversions
- Problem: Server-side tracking not working properly

Method 2: Google Tag Manager Server Container Status

  1. Open Google Tag Manager
  2. Select Server container
  3. Go to AdminContainer Settings

Check:

Working:

Server Container URL: https://tracking.yourdomain.com
Status: Active
Last request: 2 minutes ago

Not working:

Server Container URL: Not configured
Status: No recent traffic
Last request: Never

Method 3: Test Server Endpoint

Verify server is receiving requests:

# Test server-side tracking endpoint
curl -X POST https://tracking.yourdomain.com/g/collect \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "test",
    "events": [{
      "name": "page_view",
      "params": {}
    }]
  }'

# Should return:
HTTP/2 200 OK
(Server is responding)

# If error:
curl: (6) Could not resolve host
(DNS not configured)

Method 4: Check Server Logs

Review server-side tracking logs:

# Google Cloud Run logs (if using GCP)
gcloud logging read "resource.type=cloud_run_revision" \
  --project=your-project \
  --limit=50

# AWS CloudWatch (if using AWS)
aws logs tail /aws/apprunner/gtm-server

# Look for:
# - Incoming requests from website
# - Outgoing requests to Google/Facebook
# - Error messages
# - Rate limiting warnings

Method 5: Facebook Events Manager

  1. Go to Facebook Events Manager
  2. Select pixel
  3. Check Test Events

Compare:

Browser Events (client-side):
- PageView: 1,000/day
- Purchase: 20/day

Server Events (server-side):
- PageView: 1,200/day (20% more!)
- Purchase: 28/day (40% more!)

General Fixes

Fix 1: Set Up Google Tag Manager Server Container

Step-by-step setup:

  1. Create Server Container:

    • GTMAdminCreate Container
    • Container type: Server
    • Name: "Server Container"
  2. Deploy to Cloud:

Google Cloud Run (recommended):

# Install gcloud CLI
gcloud init

# Deploy GTM server
gcloud run deploy gtm-server \
  --image=gcr.io/cloud-tagging-10302018/gtm-cloud-image:stable \
  --platform=managed \
  --region=us-central1 \
  --allow-unauthenticated

# Output:
# Service URL: https://gtm-server-xxx.run.app
  1. Configure Custom Domain:
# Map custom subdomain (recommended for better tracking)
gcloud run domain-mappings create \
  --service=gtm-server \
  --domain=tracking.yourdomain.com \
  --region=us-central1

# Add DNS record:
# Type: CNAME
# Name: tracking
# Value: ghs.googlehosted.com
  1. Update Client Container:
// In web GTM container, update config
gtag('config', 'G-XXXXXXXXXX', {
    'transport_url': 'https://tracking.yourdomain.com',
    'first_party_collection': true
});

Fix 2: Configure GA4 Server-Side Tracking

Server container setup:

  1. Create GA4 Client (receives requests from website)

    • Tag type: Google Analytics: GA4
    • Client Name: GA4 Client
  2. Create GA4 Tag (sends to Google)

    • Tag type: Google Analytics: GA4
    • Measurement ID: G-XXXXXXXXXX
    • Trigger: All Events
  3. Update Web Container:

// Send events to server instead of Google directly
gtag('config', 'G-XXXXXXXXXX', {
    'server_container_url': 'https://tracking.yourdomain.com'
});

Fix 3: Implement Facebook Conversions API

Server-side Facebook tracking:

// Node.js server example
const bizSdk = require('facebook-nodejs-business-sdk');

const accessToken = 'YOUR_ACCESS_TOKEN';
const pixelId = 'YOUR_PIXEL_ID';

const ServerEvent = bizSdk.ServerEvent;
const EventRequest = bizSdk.EventRequest;
const UserData = bizSdk.UserData;
const CustomData = bizSdk.CustomData;

// API endpoint to receive conversion from your website
app.post('/api/track-purchase', async (req, res) => {
    const { email, phone, firstName, lastName, value, currency, eventId } = req.body;

    // Create user data
    const userData = new UserData()
        .setEmail(email)
        .setPhone(phone)
        .setFirstName(firstName)
        .setLastName(lastName)
        .setClientIpAddress(req.ip)
        .setClientUserAgent(req.headers['user-agent']);

    // Create custom data
    const customData = new CustomData()
        .setCurrency(currency)
        .setValue(value);

    // Create server event
    const serverEvent = new ServerEvent()
        .setEventName('Purchase')
        .setEventTime(Math.floor(Date.now() / 1000))
        .setUserData(userData)
        .setCustomData(customData)
        .setEventId(eventId) // Deduplication ID
        .setEventSourceUrl(req.body.sourceUrl)
        .setActionSource('website');

    // Send to Facebook
    const eventRequest = new EventRequest(accessToken, pixelId)
        .setEvents([serverEvent]);

    try {
        const response = await eventRequest.execute();
        console.log('Facebook CAPI response:', response);
        res.json({ success: true });
    } catch (error) {
        console.error('Facebook CAPI error:', error);
        res.status(500).json({ success: false, error: error.message });
    }
});

Client-side code to send to server:

// On purchase confirmation page
fetch('/api/track-purchase', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        email: userEmail,
        phone: userPhone,
        firstName: userFirstName,
        lastName: userLastName,
        value: orderTotal,
        currency: 'USD',
        eventId: transactionId, // Important: same ID for deduplication
        sourceUrl: window.location.href
    })
});

// ALSO send browser event with same eventId for deduplication
fbq('track', 'Purchase', {
    value: orderTotal,
    currency: 'USD'
}, {
    eventID: transactionId // Facebook deduplicates server + browser events
});

Fix 4: Setup TikTok Events API

Server-side TikTok tracking:

// Node.js TikTok Events API
const axios = require('axios');

app.post('/api/track-tiktok-purchase', async (req, res) => {
    const { email, phone, value, eventId } = req.body;

    const data = {
        pixel_code: 'YOUR_PIXEL_CODE',
        event: 'CompletePayment',
        event_id: eventId,
        timestamp: Math.floor(Date.now() / 1000),
        context: {
            user_agent: req.headers['user-agent'],
            ip: req.ip,
            url: req.body.sourceUrl
        },
        properties: {
            value: value,
            currency: 'USD'
        },
        user: {
            email: email,
            phone: phone
        }
    };

    try {
        const response = await axios.post(
            'https://business-api.tiktok.com/open_api/v1.3/event/track/',
            { data: [data] },
            {
                headers: {
                    'Access-Token': 'YOUR_ACCESS_TOKEN',
                    'Content-Type': 'application/json'
                }
            }
        );

        console.log('TikTok API response:', response.data);
        res.json({ success: true });
    } catch (error) {
        console.error('TikTok API error:', error);
        res.status(500).json({ success: false, error: error.message });
    }
});

Fix 5: Implement Measurement Protocol (GA4)

Direct GA4 server-side tracking:

// Node.js GA4 Measurement Protocol
const axios = require('axios');

app.post('/api/track-ga4-purchase', async (req, res) => {
    const { clientId, transactionId, value, items } = req.body;

    const payload = {
        client_id: clientId, // From GA cookie or generate
        events: [{
            name: 'purchase',
            params: {
                transaction_id: transactionId,
                value: value,
                currency: 'USD',
                items: items
            }
        }]
    };

    try {
        const response = await axios.post(
            `https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXXXX&api_secret=YOUR_API_SECRET`,
            payload
        );

        console.log('GA4 MP response:', response.status);
        res.json({ success: true });
    } catch (error) {
        console.error('GA4 MP error:', error);
        res.status(500).json({ success: false });
    }
});

Fix 6: Handle Event Deduplication

Prevent double-counting:

// Generate unique event ID
const eventId = 'purchase_' + Date.now() + '_' + Math.random().toString(36);

// Send browser event with ID
fbq('track', 'Purchase', {
    value: 99.99,
    currency: 'USD'
}, {
    eventID: eventId // Dedup ID
});

// Send same event server-side with SAME ID
fetch('/api/track-purchase', {
    method: 'POST',
    body: JSON.stringify({
        eventId: eventId, // SAME ID
        value: 99.99,
        currency: 'USD'
    })
});

// Facebook sees both events, deduplicates based on eventID
// Only counts once!

Fix 7: Monitor Server-Side Health

Set up monitoring:

// Health check endpoint
app.get('/health', (req, res) => {
    res.json({
        status: 'healthy',
        timestamp: Date.now(),
        uptime: process.uptime()
    });
});

// Track API errors
let errorCount = 0;
let successCount = 0;

app.post('/api/track-*', async (req, res, next) => {
    try {
        // ... tracking logic ...
        successCount++;
    } catch (error) {
        errorCount++;
        console.error('Tracking error:', error);
        // Alert if error rate > 10%
        if (errorCount / (errorCount + successCount) > 0.1) {
            sendAlert('High tracking error rate!');
        }
    }
});

Fix 8: WordPress Server-Side Tracking

Using WooCommerce:

// functions.php or custom plugin

add_action('woocommerce_thankyou', 'send_server_side_conversion', 10, 1);

function send_server_side_conversion($order_id) {
    $order = wc_get_order($order_id);

    // Prepare data
    $data = [
        'transaction_id' => $order_id,
        'value' => $order->get_total(),
        'currency' => $order->get_currency(),
        'email' => $order->get_billing_email(),
        'phone' => $order->get_billing_phone(),
        'items' => []
    ];

    foreach ($order->get_items() as $item) {
        $data['items'][] = [
            'name' => $item->get_name(),
            'quantity' => $item->get_quantity(),
            'price' => $item->get_total()
        ];
    }

    // Send to your server endpoint
    wp_remote_post('https://yoursite.com/api/track-purchase', [
        'body' => json_encode($data),
        'headers' => ['Content-Type' => 'application/json']
    ]);
}

Platform-Specific Guides

Detailed implementation instructions for your specific platform:

Platform Troubleshooting Guide
Shopify Shopify Server-Side Tracking Guide
WordPress WordPress Server-Side Tracking Guide
Wix Wix Server-Side Tracking Guide
Squarespace Squarespace Server-Side Tracking Guide
Webflow Webflow Server-Side Tracking Guide

Verification

After implementing server-side tracking:

Test 1: Send Test Event

  1. Trigger conversion on your site
  2. Check server logs for incoming request
  3. Check platform (GA4, Facebook) for event

Test 2: Compare Match Rates

  1. Wait 24-48 hours
  2. Compare client vs server events
  3. Server should track 20-40% more

Test 3: Ad Blocker Test

  1. Enable uBlock Origin
  2. Complete purchase
  3. Conversion should still track server-side

Test 4: Platform Verification

  • GA4: Real-Time report shows events
  • Facebook: Events Manager shows server events
  • Google Ads: Conversions appear

Common Mistakes

  1. Not using event deduplication - Counts events twice
  2. Wrong server URL - Use first-party domain
  3. Missing client_id - Can't tie to sessions
  4. No error handling - Silent failures
  5. Forgetting to hash PII - Some APIs require hashing
  6. Not monitoring - Don't know when tracking breaks
  7. Cold start delays - Cloud Run containers spin down
  8. Missing consent mode - GDPR compliance issues

Further Reading

// SYS.FOOTER