Most broken UTM data comes from characters that have a special meaning in URLs: &, #, ?, + and spaces, plus inconsistent capital letters. Use lowercase values with hyphens instead of spaces, percent-encode anything else once, and put the UTM parameters in the query string before any #. The examples below show what each mistake does to the parameters your analytics actually receives.
Google's help page for campaign URLs in Google Analytics 4 recommends always setting utm_source, utm_medium and utm_campaign, and notes that missing UTM parameters result in (not set) values in reports. Several encoding mistakes cause exactly that.
The parameters GA4 reads
| Parameter | Purpose (from Google's help page) |
|---|---|
utm_source |
Where the traffic comes from, such as a newsletter or site |
utm_medium |
The marketing medium, such as email or cpc |
utm_campaign |
The product, promotion or campaign name |
utm_id |
Campaign ID that matches uploaded campaign data |
utm_term |
Paid keyword |
utm_content |
Distinguishes links or creatives with the same destination |
utm_source_platform |
Platform that directs traffic to the property |
Eight mistakes and what they do
Each "parsed as" column below was produced by the standard URL parser that browsers use (URL and URLSearchParams).
1. A raw & inside a value
?utm_campaign=Q&A-webinar&utm_source=linkedin
Parsed as: utm_campaign = Q, a stray parameter A-webinar, and utm_source = linkedin. The & ends the value. Encode it as %26 or, better, write qa-webinar.
2. A raw # inside a value
?utm_campaign=launch #1&utm_source=x
Parsed as: utm_campaign = launch and nothing else. Everything from # onward is the fragment, so utm_source is lost. RFC 3986 defines the query as ending at the first #. Encode # as %23 or avoid it.
3. A second ? instead of &
https://example.com/?id=5?utm_source=newsletter&utm_medium=email
Parsed as: id = 5?utm_source=newsletter and utm_medium = email. Only the first ? starts the query; later ones are ordinary characters. When a URL already has a query string, add UTM parameters with &.
4. UTM parameters after the
https://example.com/page#pricing?utm_source=newsletter
Parsed as: no query parameters at all; the hash is #pricing?utm_source=newsletter. The fragment is never sent to the server. Put the query first: /page?utm_source=newsletter#pricing.
5. & copied from HTML
?utm_source=newsletter&utm_medium=email&utm_campaign=spring-sale
Parsed as: utm_source = newsletter, then parameters named amp;utm_medium and amp;utm_campaign. Analytics no longer sees a medium or campaign, so they report as (not set). & is correct only inside HTML source, where the browser converts it back; see HTML entities vs URL encoding.
6. Encoding twice
?utm_campaign=spring%2520sale
Parsed as: utm_campaign = spring%20sale, with a literal %20 in your reports. This happens when a link builder, ad platform or redirect encodes a URL that was already encoded. See double URL encoding (%2520).
7. Mixed capitalization
utm_source=Newsletter, utm_source=newsletter and utm_source=NEWSLETTER are three different sources. Google's help page states that parameter values are case sensitive and recommends a strict naming convention with lowercase as standard practice.
8. Spaces and trailing spaces
A space must be sent as %20 or +. Which one you get depends on the tool that built the link, and a value pasted from a spreadsheet may carry an invisible trailing space that becomes %20 at the end. Form-style parsers read + as a space, but plain percent-decoders do not (see %20 vs + in URLs). Hyphens remove the question entirely.
Summary table
| Mistake | Example | What analytics gets | Fix |
|---|---|---|---|
Raw & |
utm_campaign=Q&A |
Campaign Q |
qa or Q%26A |
Raw # |
utm_campaign=launch #1 |
Campaign launch; later params lost |
launch-1 or %23 |
Second ? |
?id=5?utm_source=x |
No utm_source |
Use & |
UTMs after # |
#pricing?utm_source=x |
No UTMs | Query before fragment |
& |
&utm_medium=email |
(not set) medium |
Plain & outside HTML |
| Double encoding | spring%2520sale |
spring%20sale |
Encode once |
| Mixed case | Newsletter / newsletter |
Two sources | Lowercase |
| Spaces | spring sale |
%20, + or a split value |
spring-sale |
Build UTM links safely
Let the URL API handle the encoding and normalize values first:
const slug = v => v.trim().toLowerCase().replace(/\s+/g, "-");
function buildUtmUrl(base, params) {
const u = new URL(base); // keeps existing query and #fragment
for (const [k, v] of Object.entries(params)) {
if (v) u.searchParams.set("utm_" + k, slug(v));
}
return u.toString();
}
buildUtmUrl("https://example.com/pricing#plans", {
source: "Newsletter", medium: "Email", campaign: " Spring Sale 2026 ", content: "Q&A block"
});
// https://example.com/pricing?utm_source=newsletter&utm_medium=email
// &utm_campaign=spring-sale-2026&utm_content=q%26a-block#plans
The function adds parameters with & when a query already exists, keeps the fragment at the end and encodes & and # inside values. Building links in a spreadsheet instead? The Google Sheets and Excel guide shows the ENCODEURL formulas.
A naming convention that avoids encoding
- Lowercase only.
- Letters, digits and hyphens only; no spaces,
&,#,?,+or/. - A fixed list of
utm_mediumvalues (for exampleemail,cpc,social). - Dates in one format, such as
2026-11. - One owner for the convention, and a shared sheet of approved values.
With values like spring-sale-2026, the encoded and decoded forms are identical, so nothing can be encoded twice or decoded wrongly.
Auditing campaigns that already ran
You cannot change data that has already been collected, but you can find the broken links and stop new damage:
- Look for odd values. In your acquisition reports, sort source, medium and campaign values alphabetically. Variants that differ only in case, values ending in a space or
%20, and values containing%25are encoding problems. - Look for
(not set). A sudden rise in(not set)medium or campaign for one source often means a template started producing&or a misplaced#. - Trace the link. Find the email template, ad, social post or QR code that produced the URL and check its exact text.
- Fix at the source, then re-test by clicking the live link and checking the landing URL in the address bar.
- Document the convention so the next campaign uses the same values. Consistent future data is easier than cleaning old reports.
Check a link before you send it
Paste the finished URL into the URL parser. It lists every parameter as analytics will read it, highlights utm_ parameters, and warns about uppercase values, spaces, & and double encoding. For one-off strings, the URL encode and decode operations on the universal encoder decoder show exactly what a value turns into.
Frequently asked questions
Are UTM parameters case-sensitive?
Yes, in Google Analytics 4. Google's help page says parameter values are case sensitive, so utm_source=google and utm_source=Google are reported as different sources. It recommends using lowercase as a standard practice.
Should I use spaces in utm_campaign?
Avoid them. A space has to be encoded as %20 or +, and links that are copied, shortened or rebuilt by other tools can end up with different encodings. Hyphens or underscores, such as spring-sale-2026, need no encoding at all.
Why does my report show (not set) for medium or campaign?
Google Analytics shows (not set) when a UTM parameter is missing. With encoding problems this usually means the parameter name was damaged, for example by & turning utm_medium into amp;utm_medium, or by a # or ? placed before it.
Split a URL into scheme, host, path, query and fragment, and decode every parameter.
Open the URL Parser