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.
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.
Table of contents
- The Difference
- %20 (Percent-Encoded Space)
- + (Plus as Space)
- Quick Recommendation
- Historical Context
- application/x-www-form-urlencoded
- Modern URL Encoding (RFC 3986)
- Browser Behavior
- When + Works
- When + Fails
- Platform Compatibility
- Encoding Methods
- JavaScript: encodeURIComponent() → %20
- JavaScript: URLSearchParams → +
- When Each Method Produces What
- Best Practices
- Rule 1: Use %20 for UTM Parameters
- Rule 2: Or Use Underscores/Hyphens
- Rule 3: Never Mix
- Rule 4: Document Your Choice
- Migration Guide
- If Currently Using +
- Testing Both Formats
- FAQ
- Is + deprecated for spaces?
- Why does URLSearchParams use +?
- Can I use both in the same URL?
- What if my platform requires +?
- Should I convert existing URLs from + to %20?
- Conclusion
🚨 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)
Example: spring%20sale
Meaning: Space character (explicit)
Context: Works everywhere (URLs, forms, APIs)
+ (Plus as Space)
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:
<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
%20for space +is a regular character (not special)- Use
%2Bif 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
Browser Behavior
// 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 systemsWhen + Works
✅ 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
❌ 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 | + Support | Recommendation |
|---|---|---|---|
| GA4 | ✅ Perfect | ⚠️ Usually works | Use %20 |
| Google Ads | ✅ Perfect | ⚠️ Usually works | Use %20 |
| ✅ Perfect | ⚠️ Usually works | Use %20 | |
| Email clients | ✅ Perfect | ⚠️ Inconsistent | Use %20 |
| URL shorteners | ✅ Perfect | ⚠️ Varies | Use %20 |
| API calls | ✅ Perfect | ⚠️ Depends on API | Use %20 |
Universal choice: %20
Encoding Methods
JavaScript: encodeURIComponent() → %20
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%20saleJavaScript: URLSearchParams → +
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 explicitWhen Each Method Produces What
// 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
// ✅ 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%20saleRule 2: Or Use Underscores/Hyphens
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
❌ 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
// 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 +
// 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%20saleTesting Both Formats
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
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).
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