best-practicesUpdated 2025

URL Encoding: Space vs Plus Sign - Which Should You Use?

%20 or + for spaces in UTM parameters? Learn the difference, browser compatibility, and best practices for reliable space encoding.

7 min readbest-practices

You need to encode a space in your UTM campaign name. Should you use %20 or +?

Both are valid, but one is more reliable. Here's the complete guide.

🚨 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 Difference

%20 (Percent-Encoded Space)

Code
Example: spring%20sale
Meaning: Space character (explicit)
Context: Works everywhere (URLs, forms, APIs)

+ (Plus as Space)

Code
Example: spring+sale
Meaning: Space character (legacy encoding)
Context: Valid in query strings only

Quick Recommendation

Use %20 for UTM parameters.

Why:

  • Works in ALL URL contexts
  • Explicitly represents space
  • No ambiguity
  • Consistent across platforms

Historical Context

application/x-www-form-urlencoded

The + as space convention comes from HTML form encoding:

Html
<form method="GET" action="/search">
    <input name="q" value="hello world">
</form>
 
Submits as:
/search?q=hello+world
 
Browser automatically converts space → +

This is called application/x-www-form-urlencoded format.

Modern URL Encoding (RFC 3986)

Modern URL standard (RFC 3986) says:

  • Use %20 for space
  • + is a regular character (not special)
  • Use %2B if you need literal +

Recommendation: Follow modern standard, use %20.

😰 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

Browser Behavior

Javascript
// Test both encodings
const test1 = 'site.com?utm_campaign=spring%20sale';
const test2 = 'site.com?utm_campaign=spring+sale';
 
// Both decode to "spring sale" in most browsers
new URL(test1).searchParams.get('utm_campaign');
// Output: "spring sale"
 
new URL(test2).searchParams.get('utm_campaign');
// Output: "spring sale"
 
// HOWEVER, behavior varies in edge cases and older systems

When + Works

Code
✅ Query string context (form data):
?search=hello+world → "hello world"

✅ URLSearchParams API:
new URLSearchParams({q: 'hello world'}).toString()
→ q=hello+world

✅ Most modern browsers:
Decode + as space in query strings

When + Fails

Code
❌ Path segments:
/path/hello+world → "hello+world" (+ kept as +, not space)

❌ Fragment identifiers:
#section+name → "section+name" (+ kept as +)

❌ Some older systems:
May not decode + as space

❌ When literal + needed:
product=C++ → "C " (space, not "+")

Platform Compatibility

Platform%20 Support+ SupportRecommendation
GA4✅ Perfect⚠️ Usually worksUse %20
Google Ads✅ Perfect⚠️ Usually worksUse %20
Facebook✅ Perfect⚠️ Usually worksUse %20
Email clients✅ Perfect⚠️ InconsistentUse %20
URL shorteners✅ Perfect⚠️ VariesUse %20
API calls✅ Perfect⚠️ Depends on APIUse %20

Universal choice: %20

Encoding Methods

JavaScript: encodeURIComponent() → %20

Javascript
const value = 'spring sale';
const encoded = encodeURIComponent(value);
console.log(encoded);
// Output: spring%20sale (uses %20)
 
// Build URL
const url = `site.com?utm_campaign=${"{"}{"{"}encoded{"}"}{"}"}}`;
// site.com?utm_campaign=spring%20sale

JavaScript: URLSearchParams → +

Javascript
const params = new URLSearchParams();
params.set('utm_campaign', 'spring sale');
const url = `site.com?${params.toString()}`;
console.log(url);
// site.com?utm_campaign=spring+sale (uses +)
 
// Both are valid, but %20 is more explicit

When Each Method Produces What

Javascript
// encodeURIComponent: Always %20
encodeURIComponent('hello world')
'hello%20world'
 
// URLSearchParams: Always +
new URLSearchParams({q: 'hello world'}).toString()
'q=hello+world'
 
// Both decode to same value:
new URL('site.com?q=hello%20world').searchParams.get('q')
'hello world'
 
new URL('site.com?q=hello+world').searchParams.get('q')
'hello world'

Best Practices

Rule 1: Use %20 for UTM Parameters

Javascript
// ✅ RECOMMENDED
function buildUtmUrl(base, campaign) {
    const encoded = encodeURIComponent(campaign); // Uses %20
    return `${"{"}{"{"}base{"}"}{"}"}}?utm_campaign=${"{"}{"{"}encoded{"}"}{"}"}}`;
}
 
buildUtmUrl('site.com', 'spring sale');
// site.com?utm_campaign=spring%20sale

Rule 2: Or Use Underscores/Hyphens

Code
Alternatives that need no encoding:

spring_sale (underscore)
spring-sale (hyphen)
springsale (remove space)

All are:
- URL-safe
- No encoding needed
- Readable
- Consistent across platforms

Rule 3: Never Mix

Code
❌ WRONG (mixed encoding):
utm_campaign=spring%20sale+2024

Pick one format:
✅ utm_campaign=spring%20sale%202024 (all %20)
✅ utm_campaign=spring_sale_2024 (no spaces)

Rule 4: Document Your Choice

Code
// Add to team documentation:

UTM Encoding Standard:
- Spaces: Use %20 (via encodeURIComponent)
- Alternative: Use underscores (spring_sale)
- Never use + for spaces
- Encode literal + as %2B

Examples:
✅ spring%20sale
✅ spring_sale
❌ spring+sale (ambiguous)

Migration Guide

If Currently Using +

Javascript
// Convert existing + to %20
function migratePlusToPercent(url) {
    const urlObj = new URL(url);
 
    // Re-encode all UTM parameters
    ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'].forEach(key => {
        const value = urlObj.searchParams.get(key);
        if (value) {
            // Remove and re-add to trigger proper encoding
            urlObj.searchParams.delete(key);
            const encoded = encodeURIComponent(value); // Uses %20
            urlObj.search += `${"{"}{"{"}key{"}"}{"}"}}=${"{"}{"{"}encoded{"}"}{"}"}}&`;
        }
    });
 
    // Clean up trailing &
    urlObj.search = urlObj.search.replace(/&$/, '');
 
    return urlObj.toString();
}
 
// Usage
const old = 'site.com?utm_campaign=spring+sale';
const new = migratePlusToPercent(old);
console.log(new);
// site.com?utm_campaign=spring%20sale

Testing Both Formats

Javascript
function testSpaceEncoding() {
    const testCases = [
        {
            name: '%20 encoding',
            url: 'https://site.com?utm_campaign=spring%20sale',
            expected: 'spring sale'
        },
        {
            name: '+ encoding',
            url: 'https://site.com?utm_campaign=spring+sale',
            expected: 'spring sale'
        },
        {
            name: 'underscore (no encoding)',
            url: 'https://site.com?utm_campaign=spring_sale',
            expected: 'spring_sale'
        }
    ];
 
    testCases.forEach(test => {
        const url = new URL(test.url);
        const actual = url.searchParams.get('utm_campaign');
        const match = actual === test.expected;
 
        console.log(`${match ? '✅' : '❌'} ${test.name}`);
        console.log(`  Expected: "${test.expected}"`);
        console.log(`  Actual: "${"{"}{"{"}actual{"}"}{"}"}}"`);
    });
}
 
testSpaceEncoding();

✅ 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

Is + deprecated for spaces?

Not officially, but %20 is the modern standard (RFC 3986). Use %20 for new implementations.

Why does URLSearchParams use +?

It follows HTML form encoding convention (application/x-www-form-urlencoded). Still valid, but %20 is more explicit.

Can I use both in the same URL?

Technically yes, but don't. Choose one format for consistency.

What if my platform requires +?

Follow platform requirements, but prefer %20 when you have a choice.

Should I convert existing URLs from + to %20?

Not urgently, but standardize on %20 for new campaigns.

Conclusion

For UTM parameters, use %20 for spaces.

Why %20?

  • ✅ Works everywhere (query strings, paths, fragments)
  • ✅ Explicit and unambiguous
  • ✅ Modern standard (RFC 3986)
  • ✅ Consistent across platforms
  • ✅ No conflict with literal +

Alternative: Use underscores or hyphens (no encoding needed).

Code
Best:      spring%20sale
Also good: spring_sale
Also good: spring-sale
Avoid:     spring+sale (ambiguous)

Use encodeURIComponent() in JavaScript - it automatically uses %20.


Technical Reference: Plus Sign in Value Validation Rule

UTM

Get Your Free Audit in 60 Seconds

Connect GA4, run the scan, and see exactly where tracking is leaking budget. No credit card required.

Trusted by growth teams and agencies to keep attribution clean.