How to Fix Spaces in UTM Parameters: Complete 2024 Guide

UTMGuard Team
9 min readURL Syntax

"We had spaces in our UTM parameters for eight months. When I finally fixed them, our conversion attribution improved by 43%. I wish someone had told me this on day one."

Lisa Chen, Growth Marketing Manager at a SaaS startup, discovered this after a frustrating audit revealed 278 fragmented campaign entries—all from a single issue: spaces in UTM parameters.

This comprehensive guide will show you exactly how to identify, fix, and prevent space-related UTM issues that are fragmenting your analytics data.

Quick Diagnosis: Do You Have Space Issues?

Check #1: Your Current URLs

Look at your tracking links. Do you see ANY of these?

❌ PROBLEM URLS:
?utm_campaign=Black Friday Sale
?utm_source=email newsletter
?utm_medium=social media
?utm_content=hero banner
?utm_term=running shoes

✅ CORRECT URLS:
?utm_campaign=black-friday-sale
?utm_source=email-newsletter
?utm_medium=social-media
?utm_content=hero-banner
?utm_term=running-shoes

If you see spaces, you have a problem.

Check #2: Your Analytics Data

Google Analytics 4 fragmentation symptoms:

  1. Open Reports → Acquisition → Traffic Acquisition
  2. Look at "Session campaign" dimension
  3. Do you see entries like:
    • "Black Friday Sale"
    • "Black%20Friday%20Sale"
    • "Black+Friday+Sale"
    • "Black"

If yes, you have space-induced fragmentation.

🚨 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 Complete Fix: 5-Step Process

Step 1: Audit All Current Campaigns

Create a comprehensive inventory:

// JavaScript audit script for your analytics export
function auditCampaignNames(data) {
  const problemCampaigns = [];
 
  data.forEach(row => {
    const campaign = row.campaign;
 
    // Check for spaces or encoded spaces
    if (campaign.includes(' ') ||
        campaign.includes('%20') ||
        campaign.includes('+')) {
      problemCampaigns.push({
        original: campaign,
        sessions: row.sessions,
        revenue: row.revenue,
        severity: calculateSeverity(row.sessions)
      });
    }
  });
 
  return problemCampaigns.sort((a, b) => b.sessions - a.sessions);
}
 
function calculateSeverity(sessions) {
  if (sessions > 1000) return 'CRITICAL';
  if (sessions > 100) return 'HIGH';
  if (sessions > 10) return 'MEDIUM';
  return 'LOW';
}

Export from Google Analytics:

  1. Reports → Acquisition → Traffic Acquisition
  2. Add dimensions: Campaign, Source, Medium, Content, Term
  3. Date range: Last 90 days
  4. Export to CSV
  5. Run audit script on exported data

Expected output:

CRITICAL Issues (>1000 sessions):
- "Summer Sale 2024" (1,245 sessions, $18,900 revenue)
- "Email Newsletter" (1,120 sessions, $8,450 revenue)

HIGH Issues (100-1000 sessions):
- "Facebook Campaign" (456 sessions, $3,200 revenue)
- "Product Launch" (234 sessions, $5,100 revenue)

... (full list)

Step 2: Create Corrected Versions

For each problematic entry, create the fixed version:

function createFixedVersion(originalName) {
  return originalName
    .toLowerCase()                    // Convert to lowercase
    .trim()                          // Remove leading/trailing spaces
    .replace(/\s+/g, '-')           // Replace spaces with hyphens
    .replace(/[^a-z0-9-_]/g, '')    // Remove special characters
    .replace(/-+/g, '-')            // Replace multiple hyphens with single
    .replace(/^-|-$/g, '');         // Remove leading/trailing hyphens
}
 
// Examples
console.log(createFixedVersion('Black Friday Sale 2024'));
// Output: black-friday-sale-2024
 
console.log(createFixedVersion('Email Newsletter Campaign'));
// Output: email-newsletter-campaign
 
console.log(createFixedVersion('SUMMER  SALE!!'));
// Output: summer-sale

Create a mapping document:

Original (with spaces)Fixed VersionSessionsRevenuePriority
Summer Sale 2024summer-sale-20241,245$18,9001
Email Newsletteremail-newsletter1,120$8,4502
Facebook Campaignfacebook-campaign456$3,2003

Step 3: Update Active Campaigns

For each active campaign, replace URLs systematically:

😰 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

Email Marketing Platforms

Mailchimp:

  1. Go to Campaigns → All campaigns
  2. Search for campaigns with spaces in UTM parameters
  3. Clone campaign
  4. Update all tracking links with fixed versions
  5. Test with seed list
  6. Schedule/send new version
  7. Archive old campaign

HubSpot:

  1. Marketing → Email
  2. Filter by "Active" campaigns
  3. Edit each email
  4. Use Find & Replace for UTM parameters
  5. Update all instances
  6. Review in preview mode
  7. Re-test and republish

Social Media Platforms

Facebook Ads:

1. Ads Manager → All Campaigns
2. Click into Ad Set
3. Edit → Ad Creative
4. Update "Website URL"
5. Replace:
   OLD: ?utm_campaign=Black Friday Sale
   NEW: ?utm_campaign=black-friday-sale
6. Review and publish changes

LinkedIn Campaign Manager:

1. Campaign Manager → Campaigns
2. Select campaign → Ads
3. Edit each ad
4. Update destination URL
5. Test link in preview
6. Activate updated ads

Using Ads Editor (bulk update):

  1. Download Google Ads Editor
  2. Get Latest → Select account
  3. Keywords → Add/update tracking templates
  4. Find: utm_campaign=Black Friday Sale
  5. Replace: utm_campaign=black-friday-sale
  6. Review changes
  7. Post changes

Manual update:

1. Google Ads → Campaigns
2. Settings → Campaign URL options
3. Update tracking template:

OLD: `{"{"}{"{"}lpurl{"}"}{"}"}}`?utm_source=google&utm_medium=paid search&utm_campaign=Black Friday
NEW: `{"{"}{"{"}lpurl{"}"}{"}"}}`?utm_source=google&utm_medium=paid-search&utm_campaign=black-friday

4. Save and apply to all ad groups

Step 4: Fix UTM Generation Process

Implement a proper URL builder:

<!DOCTYPE html>
<html>
<head>
  <title>UTM URL Builder (Space-Free)</title>
  <style>
    .error { color: red; }
    .success { color: green; }
    .url-output {
      background: #f5f5f5;
      padding: 15px;
      margin: 15px 0;
      border-radius: 5px;
      word-break: break-all;
    }
  </style>
</head>
<body>
  <h1>UTM URL Builder</h1>
 
  <form id="utmForm">
    <label>Website URL *</label>
    <input type="url" id="baseUrl" required placeholder="https://example.com">
 
    <label>Campaign Source *</label>
    <input type="text" id="source" required placeholder="facebook">
 
    <label>Campaign Medium *</label>
    <input type="text" id="medium" required placeholder="paid-social">
 
    <label>Campaign Name *</label>
    <input type="text" id="campaign" required placeholder="summer-sale-2024">
 
    <label>Campaign Content (optional)</label>
    <input type="text" id="content" placeholder="header-banner">
 
    <label>Campaign Term (optional)</label>
    <input type="text" id="term" placeholder="running-shoes">
 
    <button type="submit">Generate URL</button>
  </form>
 
  <div id="validation"></div>
  <div id="output"></div>
 
  <script>
    document.getElementById('utmForm').addEventListener('submit', function(e) {
      e.preventDefault();
 
      const baseUrl = document.getElementById('baseUrl').value;
      const params = {
        utm_source: document.getElementById('source').value,
        utm_medium: document.getElementById('medium').value,
        utm_campaign: document.getElementById('campaign').value,
        utm_content: document.getElementById('content').value,
        utm_term: document.getElementById('term').value
      };
 
      // Validate for spaces
      const validation = validateParams(params);
      displayValidation(validation);
 
      if (validation.hasErrors) {
        return; // Stop if errors found
      }
 
      // Build URL
      const finalUrl = buildURL(baseUrl, params);
      displayResult(finalUrl);
    });
 
    function validateParams(params) {
      const errors = [];
      const warnings = [];
 
      Object.keys(params).forEach(key => {
        const value = params[key];
        if (!value) return; // Skip empty values
 
        // Check for spaces
        if (value.includes(' ')) {
          errors.push(`${"{"}{"{"}key{"}"}{"}"}} contains spaces: "${"{"}{"{"}value{"}"}{"}"}}"`);
        }
 
        // Check for uppercase
        if (value !== value.toLowerCase()) {
          warnings.push(`${"{"}{"{"}key{"}"}{"}"}} contains uppercase: "${"{"}{"{"}value{"}"}{"}"}}"`);
        }
 
        // Check for special characters
        if (!/^[a-z0-9-_]*$/i.test(value)) {
          warnings.push(`${"{"}{"{"}key{"}"}{"}"}} contains special characters: "${"{"}{"{"}value{"}"}{"}"}}"`);
        }
      });
 
      return {
        hasErrors: errors.length > 0,
        errors,
        warnings
      };
    }
 
    function buildURL(baseUrl, params) {
      const url = new URL(baseUrl);
 
      Object.keys(params).forEach(key => {
        let value = params[key];
        if (!value) return;
 
        // Clean the value
        value = value
          .toLowerCase()
          .trim()
          .replace(/\s+/g, '-')
          .replace(/[^a-z0-9-_]/g, '')
          .replace(/-+/g, '-')
          .replace(/^-|-$/g, '');
 
        url.searchParams.set(key, value);
      });
 
      return url.toString();
    }
 
    function displayValidation(validation) {
      const div = document.getElementById('validation');
 
      if (validation.hasErrors) {
        div.innerHTML = '<div class="error"><strong>Errors Found:</strong><ul>' +
          validation.errors.map(e => '<li>' + e + '</li>').join('') +
          '</ul><p>URL will be automatically corrected when generated.</p></div>';
      } else if (validation.warnings.length > 0) {
        div.innerHTML = '<div class="warning"><strong>Warnings:</strong><ul>' +
          validation.warnings.map(w => '<li>' + w + '</li>').join('') +
          '</ul><p>Values will be normalized.</p></div>';
      } else {
        div.innerHTML = '<div class="success">✓ All parameters valid!</div>';
      }
    }
 
    function displayResult(url) {
      document.getElementById('output').innerHTML =
        '<h2>Generated URL:</h2>' +
        '<div class="url-output">' + url + '</div>' +
        '<button onclick="copyToClipboard(\'' + url + '\')">Copy to Clipboard</button>';
    }
 
    function copyToClipboard(text) {
      navigator.clipboard.writeText(text);
      alert('URL copied to clipboard!');
    }
  </script>
</body>
</html>

Save this as utm-builder.html and use it for all campaign URL generation.

Step 5: Implement Validation Checks

Pre-launch validation script:

// Add to your campaign deployment checklist
function validateCampaignURLs(urls) {
  const report = {
    passed: [],
    failed: [],
    total: urls.length
  };
 
  urls.forEach(url => {
    const issues = [];
 
    try {
      const urlObj = new URL(url);
      const params = urlObj.searchParams;
 
      // Check each UTM parameter
      ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'].forEach(param => {
        const value = params.get(param);
        if (!value) return;
 
        // Space check
        if (value.includes(' ')) {
          issues.push(`${"{"}{"{"}param{"}"}{"}"}} has spaces: "${"{"}{"{"}value{"}"}{"}"}}"`);
        }
 
        // Encoding check
        if (value.includes('%20') || value.includes('+')) {
          issues.push(`${"{"}{"{"}param{"}"}{"}"}} has encoded spaces: "${"{"}{"{"}value{"}"}{"}"}}"`);
        }
 
        // Case check
        if (value !== value.toLowerCase()) {
          issues.push(`${"{"}{"{"}param{"}"}{"}"}} not lowercase: "${"{"}{"{"}value{"}"}{"}"}}"`);
        }
      });
 
      if (issues.length > 0) {
        report.failed.push({ url, issues });
      } else {
        report.passed.push(url);
      }
    } catch (e) {
      report.failed.push({ url, issues: ['Invalid URL format'] });
    }
  });
 
  return report;
}
 
// Usage
const campaignURLs = [
  'https://example.com?utm_campaign=Black Friday',
  'https://example.com?utm_campaign=black-friday',
  'https://example.com?utm_source=Email'
];
 
const report = validateCampaignURLs(campaignURLs);
 
console.log(`Total URLs: ${report.total}`);
console.log(`Passed: ${report.passed.length}`);
console.log(`Failed: ${report.failed.length}`);
 
if (report.failed.length > 0) {
  console.log('\nFailed URLs:');
  report.failed.forEach(item => {
    console.log(`\nURL: ${item.url}`);
    console.log('Issues:');
    item.issues.forEach(issue => console.log(`  - ${"{"}{"{"}issue{"}"}{"}"}}`));
  });
}

Prevention: Never Have Spaces Again

1. Style Guide

Create and enforce this standard:

# UTM Parameter Style Guide
 
## Rules
1. All lowercase
2. No spaces ever
3. Use hyphens for word separation
4. No special characters except hyphens and underscores
 
## Examples
 
✅ CORRECT:
- utm_source=facebook
- utm_medium=paid-social
- utm_campaign=summer-sale-2024
- utm_content=hero-banner-v2
- utm_term=running-shoes-men
 
❌ WRONG:
- utm_source=Facebook (uppercase)
- utm_medium=paid social (space)
- utm_campaign=Summer Sale! (space + special char)
- utm_content=hero banner (space)
- utm_term=running shoes (space)
 
## Approved Sources
Copy these exactly:
- facebook
- instagram
- twitter
- linkedin
- email
- google
- tiktok
- pinterest
 
## Approved Mediums
Copy these exactly:
- paid-social
- organic-social
- email
- newsletter
- paid-search
- organic-search
- display
- referral
- affiliate

2. Team Training

30-minute training module:

  1. Why spaces matter (10 min)

    • Show real fragmented data example
    • Calculate cost of fragmentation
    • Demonstrate impact on decisions
  2. How to fix (10 min)

    • Walk through URL builder tool
    • Practice creating clean URLs
    • Review common mistakes
  3. Prevention (10 min)

    • Introduce style guide
    • Set up validation checklist
    • Assign accountability

3. Monthly Audits

Recurring audit schedule:

Week 1: Export GA4 data, run audit script
Week 2: Review new campaigns for issues
Week 3: Update style guide if needed
Week 4: Team refresher training

Audit script output to track:

MonthProblem URLsSessions AffectedRevenue AffectedStatus
Jan234,567$8,900Fixed
Feb71,234$2,100Fixed
Mar2345$650Fixed
Apr00$0Clean ✓

✅ 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

Q: Can I use a find-and-replace to fix spaces in Google Analytics?

A: No, you can't edit historical data in GA4. You can only fix URLs going forward and create custom dimensions to normalize data in reports.

Q: What if I have 100+ campaigns with spaces?

A: Use the bulk update methods for each platform (Google Ads Editor, Facebook Ads Manager bulk edit, etc.). Prioritize by traffic volume using the audit script.

A: The old links will still work but track separately. This is desired—you want clean new data going forward. Keep historical data for reference.

Q: Should I use underscores or hyphens instead of spaces?

A: Hyphens are the industry standard and more readable. Choose one and be consistent. Both work equally well technically.

Q: How long does it take to fix spaces in UTM parameters?

A: For a medium-sized operation (50 active campaigns): 4-8 hours for initial fix, 1 hour/month for ongoing maintenance.

Q: Do spaces in URL fragments (after #) matter?

A: UTM parameters should be in the query string (after ?), not the fragment (after #). Fragments aren't sent to the server and won't be tracked.

Q: What about spaces in the utm_content for A/B testing?

A: Same rules apply. Use hyphens: utm_content=header-banner-v1 and utm_content=header-banner-v2 instead of spaces.

Q: Can I automate space detection in our workflow?

A: Yes! Add the validation script to your campaign deployment process. Make it a required check before launch, like spell-check for emails.


Ready to eliminate spaces from your UTM parameters once and for all? UTMGuard automatically detects, flags, and helps you fix space-related issues before they fragment your data. Start your free audit today.