Fix Unencoded Ampersand in UTM: 3-Step Quick Guide

UTMGuard Team
5 min readtroubleshooting

Your UTM campaign name has an &. GA4 shows truncated data.

Here's the 3-step fix to encode ampersands and restore complete tracking.

🚨 Not sure what's breaking your tracking?

Run a free 60-second audit to check all 40+ ways UTM tracking can fail.

Scan Your Campaigns Free

✓ No credit card ✓ See results instantly

The Problem

❌ BROKEN:
?utm_campaign=save&win

Parsed as:
utm_campaign = "save"
win = "" (interpreted as separate parameter)

GA4 shows: "save"
Missing: "&win"

Quick Fix (3 Steps - Under 2 Minutes)

Step 1: Find the Ampersand (30 seconds)

Look at your URL and find the & that's INSIDE a parameter value:

?utm_campaign=save&win&utm_medium=cpc
              ↑   ↑    ↑
          Part of   Should be
          value     separator

Find this & (in value) → needs encoding

Step 2: Replace with %26 (60 seconds)

❌ BEFORE:
?utm_campaign=save&win&utm_medium=cpc

✅ AFTER:
?utm_campaign=save%26win&utm_medium=cpc
              ↑      ↑
         Encoded as %26

Rule: Replace & INSIDE parameter values with %26. Leave & BETWEEN parameters.

Step 3: Test (30 seconds)

1. Visit fixed URL
2. Open browser console
3. Run: new URL(location.href).searchParams.get('utm_campaign')
4. Should output: "save&win" (decoded)

Done.

😰 Is this your only tracking issue?

This is just 1 of 40+ ways UTM tracking breaks. Most marketing teams have 8-12 critical issues they don't know about.

• 94% of sites have UTM errors

• Average: $8,400/month in wasted ad spend

• Fix time: 15 minutes with our report

✓ Connects directly to GA4 (read-only, secure)

✓ Scans 90 days of data in 2 minutes

✓ Prioritizes issues by revenue impact

✓ Shows exact sessions affected

Get Your Free Audit Report

Automated Fix

JavaScript One-Liner

// Fix unencoded & in UTM parameters
function fixAmpersands(url) {
    const urlObj = new URL(url);
 
    // Re-encode all parameter values
    ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'].forEach(key => {
        const value = urlObj.searchParams.get(key);
        if (value) {
            urlObj.searchParams.set(key, value); // .set() with plain value auto-encodes
        }
    });
 
    return urlObj.toString();
}
 
// Usage
const broken = 'https://site.com?utm_campaign=save&win';
const fixed = fixAmpersands(broken);
console.log(fixed);
// https://site.com?utm_campaign=save%26win

Browser Console Quick Fix

// Paste this in console when on the broken URL
(function() {
    const url = new URL(window.location.href);
    const campaign = prompt('Enter correct campaign value (with &):', 'save&win');
 
    if (campaign) {
        url.searchParams.set('utm_campaign', campaign); // Auto-encodes
        window.location.href = url.toString();
    }
})();

Bulk Fix Script

// Fix multiple URLs at once
function bulkFixAmpersands(urls) {
    return urls.map(url => {
        try {
            const urlObj = new URL(url);
            const params = new URLSearchParams();
 
            // Re-encode all parameters
            for (const [key, value] of urlObj.searchParams.entries()) {
                params.set(key, value); // Properly encodes
            }
 
            urlObj.search = params.toString();
            return urlObj.toString();
        } catch (error) {
            console.error(`Failed to fix: ${"{"}{"{"}url{"}"}{"}"}}`, error);
            return url;
        }
    });
}
 
// Usage
const broken = [
    'site.com?utm_campaign=save&win',
    'site.com?utm_source=johnson&johnson',
    'site.com?utm_content=shoes&boots'
];
 
const fixed = bulkFixAmpersands(broken);
console.log(fixed);
// All & in values encoded as %26

Common Patterns & Fixes

Pattern 1: Company Name

❌ WRONG:
utm_source=procter&gamble

✅ RIGHT:
utm_source=procter%26gamble

GA4 shows: "procter&gamble"

Pattern 2: Promotional Text

❌ WRONG:
utm_campaign=buy-now&save

✅ RIGHT:
utm_campaign=buy-now%26save

GA4 shows: "buy-now&save"

Pattern 3: Product Lists

❌ WRONG:
utm_content=shoes&boots&sandals

✅ RIGHT:
utm_content=shoes%26boots%26sandals

GA4 shows: "shoes&boots&sandals"

Pattern 4: Multiple Words with &

❌ WRONG:
utm_campaign=rock&roll&music

✅ RIGHT:
utm_campaign=rock%26roll%26music

GA4 shows: "rock&roll&music"

Visual Guide: Which & to Encode

URL: ?utm_source=facebook&utm_campaign=buy&save&utm_medium=cpc
                        ↑               ↑     ↑             ↑
                     Separator      In value  Separator  Separator

Encode this one only:   ↑
Result: ?utm_source=facebook&utm_campaign=buy%26save&utm_medium=cpc

Rule: Encode & when it's part of a parameter VALUE. Leave & between parameters.

Platform-Specific Application

Tracking Template:
`{"{"}{"{"}lpurl{"}"}{"}"}}`?utm_campaign=buy%26save&gclid=`{"{"}{"{"}gclid{"}"}{"}"}}`
                         ↑
                    Pre-encoded in template

Facebook Ads

URL Parameters:
utm_source=facebook&utm_campaign=save%26win
                                  ↑
                             Encoded & in value

Email HTML

<a href="https://site.com?utm_campaign=buy%26save&utm_source=email">
    Click Here
</a>
 
<!-- Encoded & in HTML is preserved -->

Validation Script

function validateAmpersands(url) {
    const errors = [];
 
    try {
        const urlObj = new URL(url);
        const rawQuery = url.split('?')[1] || '';
 
        // Check each UTM parameter
        ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'].forEach(key => {
            const value = urlObj.searchParams.get(key);
 
            if (value) {
                // Check if raw query has unencoded & in this parameter's value
                const paramPattern = new RegExp(`${"{"}{"{"}key{"}"}{"}"}}=([^&]*)`);
                const match = rawQuery.match(paramPattern);
 
                if (match) {
                    const rawValue = match[1];
 
                    // If contains & but not %26, it's unencoded
                    if (rawValue.includes('&') && !rawValue.includes('%26')) {
                        errors.push({
                            parameter: key,
                            issue: 'Contains unencoded &',
                            current: value,
                            fix: encodeURIComponent(value)
                        });
                    }
                }
            }
        });
 
    } catch (error) {
        errors.push({ issue: 'Invalid URL', message: error.message });
    }
 
    return {
        valid: errors.length === 0,
        errors
    };
}
 
// Usage
const url = 'site.com?utm_campaign=save&win';
const result = validateAmpersands(url);
 
if (!result.valid) {
    console.log('❌ Issues found:');
    result.errors.forEach(err => {
        console.log(`  ${err.parameter}: "${err.current}" → "${err.fix}"`);
    });
}

Alternative Solutions

Option 1: Replace with "and"

Instead of encoding, replace & with word "and":

❌ BEFORE:
utm_campaign=buy&save

✅ AFTER:
utm_campaign=buy-and-save

Easier to read, no encoding needed

Option 2: Remove Ampersand

❌ BEFORE:
utm_campaign=buy&save

✅ AFTER:
utm_campaign=buysave

Shortest option, but less readable

Option 3: Use Different Separator

❌ BEFORE:
utm_campaign=shoes&boots

✅ AFTER:
utm_campaign=shoes-boots
utm_campaign=shoes_boots
utm_campaign=shoes.boots

All valid, no encoding needed

Testing Checklist

After fixing:

✅ Visual check
   Look at URL - & in values should be %26

✅ Browser console test
   new URL('your-url').searchParams.get('utm_campaign')
   Should show decoded value with &

✅ GA4 Real-Time test
   Visit URL → Check GA4 Realtime
   Campaign should show complete value

✅ Cross-platform test
   Test on desktop & mobile
   Different browsers
   Verify consistent results

✅ Fixed this issue? Great! Now check the other 39...

You just fixed one tracking issue. But are your Google Ads doubling sessions? Is Facebook attribution broken? Are internal links overwriting campaigns?

Connects to GA4 (read-only, OAuth secured)

Scans 90 days of traffic in 2 minutes

Prioritizes by revenue impact

Free forever for monthly audits

Run Complete UTM Audit (Free Forever)

Join 2,847 marketers fixing their tracking daily

FAQ

When should I encode & and when should I leave it?

Encode & when it's PART OF a parameter value. Leave & between parameters as-is.

What if I have multiple & symbols in one value?

Encode all of them:

buy&save&win → buy%26save%26win

Does encodeURIComponent() handle this automatically?

Yes. encodeURIComponent('save&win') returns 'save%26win'.

What if my platform shows %26 literally in ads?

The platform displays encoded URL. Users will see decoded version after clicking. Test by actually clicking the link.

Conclusion

Fix unencoded & in UTM parameters in 3 steps:

  1. Find & INSIDE parameter values
  2. Replace with %26
  3. Test in browser and GA4
❌ utm_campaign=save&win
✅ utm_campaign=save%26win

Use encodeURIComponent() to automatically encode all special characters.


Technical Reference: Ampersand Not Encoded Validation Rule