Our own 301 redirect stripped UTM parameters and gclid for seven months

Screenshot: header of the SHIFT64 article on stripping UTM parameters at Cloudflare, with the correction dated 7 October 2026

Our own 301 redirect stripped UTM parameters and gclid for seven months

Last verified: October 14, 2026
13 min read
Case study
Technical SEO

For seven months, anyone who reached wppoland.com through a campaign link was 301-redirected to the same URL without its tracking parameters. From 7 March 2026 to 10 October 2026 my Cloudflare Pages middleware treated utm_*, gclid, fbclid and msclkid as duplicate-content parameters and removed them before the browser ran a single script. Analytics never saw a campaign and Google Ads auto-tagging lost its gclid. From 13 July, when the contact form gained hidden lead-source fields, the form lost the source of every campaign lead as well.

I do not know how many leads this affected. There is no measurement I could count it from, so I give no number. What I do know is that campaign and lead-source data from that period is incomplete and cannot support channel conclusions.

#How to test if a redirect strips UTM parameters

Start with the test, because it takes one line. A unique parameter value bypasses any cache, so the answer comes from the live logic:

curl -sI "https://wppoland.com/pl/?utm_source=t$(date +%s)"

After the fix this returns HTTP/2 200, and so does the same request with ?gclid=. As a control, a parameter that is still meant to redirect:

curl -sI "https://wppoland.com/pl/?lang=en"

That one returns 301. I checked all three on production on 11 October 2026.

On your own site, swap the domain and the parameter. Test utm_source, gclid, fbclid and msclkid separately, because rules often treat them differently. If you get a 301 or 302, read the Location header: the parameter has to be there. Then repeat the test on URLs that redirect for some other reason: a missing trailing slash, the other host (with and without www), an old slug. A page that answers 200 can pass the test while the redirect next to it still drops the parameters.

#Why UTM parameters disappear after a redirect

A 301 is a server response with a Location header. The browser adds nothing of its own: it goes exactly to the address the server built. If the rule that builds that address drops the query string or part of it, the campaign parameters are gone before the page loads.

That matters because almost all campaign attribution happens in the browser. The analytics tag reads utm_source from location.href. A form that stores the lead source in hidden fields fills them with JavaScript from location.search. Google Ads auto-tagging appends gclid to the landing page URL and expects that identifier to reach the page. Each of these mechanisms only sees the URL the browser finally landed on.

So every redirect between an ad and a page is a place where attribution can die. Not only redirects you wrote by hand. Also host normalisation, trailing slashes, old slugs and query-parameter deduplication.

#Why Cloudflare Pages middleware removes gclid and utm_source

A commit on 7 March 2026, described as improving URL handling in middleware and headers, added a list of duplicate-content parameters to functions/_middleware.ts. The function hasDuplicateContentQuery checked whether a URL carried any of them. If it did, filteredQueryString built a query string without those parameters and the middleware answered with a 301 to the result.

The list held parameters that really do create pointless variants, such as lang, amp, nonamp and s. It also held utm_source, utm_medium, utm_campaign, utm_content, utm_term, gclid, fbclid and msclkid. The goal sounded reasonable: one URL per page. The result was that a link to /en/?utm_source=newsletter ended at /en/ before any browser code saw the word “newsletter”.

Middleware in Pages Functions runs on every request, ahead of the rest of the routing. That makes it a convenient place for URL normalisation, and an equally convenient place for a bug that touches every campaign visit.

#Why hidden UTM fields in a contact form come through empty

Since 13 July 2026 my contact form has hidden utm_source, utm_medium, utm_campaign and utm_term fields. Before that date it recorded no lead source at all. JavaScript fills the fields from location.search when the page loads. After the redirect location.search was empty, so the fields were too, for every visit from a campaign link between 13 July and 10 October.

The damage landed in three places:

  • The form (from 13 July). Leads reached the inbox with no source. A visit from a newsletter link and a visit with no tagging at all looked identical.
  • Analytics. The analytics tool never received campaign parameters, so traffic from UTM-tagged links fell into generic channels or into direct.
  • Google Ads. Auto-tagging appends gclid and my 301 removed it. Without gclid on the landing page, Google Ads has nothing to tie the click to a later conversion.

None of these symptoms looks like a redirect bug. It looks like a campaign that does not work, or a channel that brings nothing.

#Strip UTM parameters at Cloudflare without losing attribution

SHIFT64 described the same symptom through a different path. Their article on stripping tracking parameters at Cloudflare was published on 31 August 2026 and corrected on 7 October 2026. The original version recommended a Transform Rule that rewrote the URL at the edge (no redirect), so the cache saw one URL per page. The browser kept the full address, so attribution was supposed to survive.

It survived only when the origin answered 200. When the origin answered with a redirect (apex to www, a missing trailing slash, a WordPress canonical redirect), it built the new address from what it received: the already-stripped URL. The browser followed it and gclid, fbclid and gad_source vanished from the address bar. The correction also lists a regex problem:

“Non-adjacent tracking parameters were only partly removed, because Cloudflare’s regex_replace() replaces only the first match.”

Mateusz Zadorożny, SHIFT64, Strip UTM Parameters at Cloudflare Without Losing Attribution (Corrected), correction dated 7 October 2026

And a URL like ?fbclid=x&color=red reached the server as ?&color=red, which WordPress answered with a 301. According to SHIFT64, on one store running Google Ads the bug showed in the logs as 21 paid landings over about three weeks, plus landings on the non-canonical host that the logs cannot count. They replaced the rule with a Worker that re-appends the stripped parameters to same-site redirects. SHIFT64 also advises that if the Super Page Cache plugin created a [DO NOT EDIT] rule with the same regex, you switch off its “strip tracking parameters” option. I have not tested that plugin ourselves.

The dates: SHIFT64’s correction on 7 October, my fix on 10 October. The difference is the mechanism. SHIFT64 lost the parameters through an origin redirect that followed an edge rewrite. I lost them through a deliberate deduplication redirect of my own.

#Does AI chatbot traffic show up as direct in GA4

It can. My reading of two reports published on 1 October 2026 is that a redirect like mine costs more now than it did in March.

Search Engine Journal (Roger Montti) reported that Gemini appears to add UTM parameters to some links to websites. The report rests on one Reddit user, who called the behaviour “very new, 24 hours or so”. Google has not documented it. Nobody has published which parameters or values Gemini adds, to which links, or under what conditions. John Mueller replied only “I’m happy to forward that to the team”, which is not a confirmation. The same article notes that AI chatbot traffic can show up in GA4 as direct, because direct is the fallback when a visit carries neither a referrer nor UTM data.

DemandSphere (Ray Grieselhuber) tracked the share of branded keywords that return an AI Overview: 26.12% on 1 September, a peak of 90.48% on 27 September and 82.06% on 29 September, their last data point. The numbers come from their own platform, DemandMetrics, across all markets and devices. The sample size is not published, and an AI Overview counts whether or not it cites the brand. Google has announced no change.

The link to my bug is my inference; neither source makes it. If AI assistants start tagging their links, the tags arrive in the same query string my middleware was cutting. A redirect that strips utm_* strips them too, and the visit falls back to direct. If more branded clicks pass through an AI Overview first, the clicks that still arrive tagged carry more of the attribution you have left. I have no evidence that Gemini traffic reached wppoland.com during the bug window, and I do not claim I lost any. My own event tracking does not record the referrer, so for search I take source data from Google Search Console.

#Do UTM parameters cause duplicate content

Not here, and the redirect protected against nothing. Every page on wppoland.com declares a canonical without parameters, so Google already knew which URL was the right one. From 12 April 2026, public/_headers also sent this header:

No-Vary-Search: key-order, params=("utm_source" "utm_medium" "utm_campaign" "utm_content" "utm_term" "ref" "fbclid" "gclid" "msclkid")

No-Vary-Search tells the browser that the listed parameters do not change the response, so the version with them and the version without can share one cache entry. From 7 March to 11 April the canonical was the only protection, and it was enough. From 12 April there were two layers that solved duplication without touching the URL in the address bar. At no point did the redirect add protection, and the whole time it took attribution away.

The fix of 10 October 2026 lets tracking parameters through with no redirect. lang, amp, nonamp and s still get a 301, because those are the ones that create real variants. The diff left this comment in the code:

// Tracking params (utm_*, gclid, fbclid, msclkid) pass through: the page
// canonical is already clean, and stripping them killed lead attribution.

#Which redirect rules strip UTM parameters and gclid

The lesson is not specific to Cloudflare Pages. Any rule that “tidies” the query string and answers with a redirect has the same risk profile:

  • middleware in Pages Functions or in a Worker,
  • Cloudflare rules: Transform Rules, Redirect Rules, Page Rules,
  • rewrite and return 301 in an nginx config,
  • WordPress redirect plugins that normalise URLs or drop “unnecessary” parameters,
  • WordPress’s own canonical redirects, when something upstream has already cut the URL.

The rule I adopted: parameter deduplication leaves tracking parameters alone. Canonical handles duplicate content, and No-Vary-Search or a cache key without those parameters handles the cache. A redirect is not needed for either. The second piece of advice comes from SHIFT64’s own correction: test redirects, not only pages.

If your site sits behind Cloudflare and you want someone to review the edge rules with this in mind, that is part of my Cloudflare edge service.

#Do Cloudflare redirect rules keep the query string

Not by default. Cloudflare documents the default for each redirect product, and they point the same way:

  • Single Redirects. The settings page describes Preserve query string as disabled by default, for static and dynamic redirects alike. In the API the field is preserve_query_string.
  • Bulk Redirects. The parameter reference lists preserve_query_string with a default of false, while preserve_path_suffix defaults to true. A bulk list of old slugs therefore keeps the rest of the path and drops ?gclid= until someone switches the option on.
  • Pages _redirects. The Pages redirects documentation marks matching on query parameters as unsupported and does not say whether the incoming query string is carried to the destination. It also states that _redirects rules are not applied to requests served by Pages Functions, so on a site with middleware the redirect logic for those routes lives in the Function code.
  • Workers. The redirect example shows both outcomes. The variant that sends every request to one URL drops the path and the query string. The domain-to-domain variant builds the destination from pathname and search, so the parameters survive.

Where the documentation is silent, the curl test above answers the question for your own configuration. My middleware bug was the Workers case written by hand: the code rebuilt search and left the tracking parameters out of it.

#How to keep UTM parameters out of the Cloudflare cache key without a redirect

The usual reason to strip tracking parameters at the edge is the cache, not SEO. The default Cloudflare cache key includes the full URL with its query string, so /?utm_source=a and /?utm_source=b are two separate cache entries. Cloudflare offers several ways to change that without touching the address the visitor sees:

  • Ignore query string. This Cache Level builds the key from every default element except the query string. It is available on all plans. It also ignores parameters that do change the response, such as a search term in ?s=, so it fits only pages where no parameter changes the content.
  • Sort query string. Also available on all plans. The same parameters in a different order land in one cache entry, which is the job key-order does in my No-Vary-Search header.
  • Query string include or exclude. A Cache Rule can remove named parameters such as utm_source and gclid from the key and keep the rest. According to the same page, this field is available on Enterprise only.
  • URL rewrite. A Transform Rule rewrite changes the path or query string the origin receives, while the address in the browser stays as it was. That is the mechanism from the SHIFT64 case: the browser keeps gclid, but any redirect the origin builds starts from the rewritten URL.

None of these options is a redirect, so none of them changes what the analytics tag reads from location.search. On wppoland.com the browser-side counterpart is the No-Vary-Search header described above.

#How to treat analytics data from a period of stripped UTM parameters

Campaign data in analytics and Google Ads between 7 March and 10 October 2026 is incomplete, and so is the form lead source between 13 July and 10 October. That does not make all of it wrong: visits without campaign parameters, such as clicks from organic search results, never went through this redirect. It means that everything arriving from tagged links was credited somewhere else or nowhere.

In practice:

  • I do not compare UTM-dependent channels in that period with the period after 10 October,
  • I do not switch off campaigns based on zero attribution from those months,
  • the first reliable campaign and lead-source data starts on 10 October 2026.

The routing layer on this site has broken something quietly before, while everything else looked healthy: Cloudflare Pages dropped _redirects rules past 100KB. The wider context of this architecture is in my write-up of twelve months migrating from WordPress to Astro on Cloudflare Pages. Clean canonicals for links that carry parameters are covered in the technical guide to affiliate SEO.

Next step

Turn the article into an actual implementation

This block strengthens internal linking and gives readers the most relevant next move instead of leaving them at a dead end.

Want this implemented on your site?

If visibility in Google and AI systems matters, I can build the content architecture, FAQ, schema, and internal linking needed for SEO, GEO, and AEO.

Frequently asked questions

Practical answers to apply the topic in real execution.

Why do UTM parameters disappear after a redirect?#
A 301 sends the browser to a new address built by the server. If the rule that builds it drops the query string or part of it, the browser lands on a URL without utm_source, utm_medium, utm_campaign or gclid. Analytics tags and hidden form fields only run on the destination page, so they read an empty location.search.
How do I check whether a redirect strips gclid or utm_source?#
Request the page with a unique parameter and read the headers, for example curl -sI "https://your-domain/?utm_source=t$(date +%s)". A 200 means the parameter passes through. A 301 or 302 whose Location header lacks the parameter means the redirect drops it. Also test URLs that redirect anyway: missing trailing slash, the other host, an old slug.
Do UTM parameters in the URL cause duplicate content in Google?#
Not when the page declares a clean canonical without parameters, which wppoland.com did for the whole period of the bug. From 12 April 2026 public/_headers also sent a No-Vary-Search header listing the tracking parameters. The 301 added no protection and destroyed attribution.
How many leads did the redirect cost?#
We do not know and we do not estimate. There is no measurement to count it from. What is certain is that campaign data in analytics and Google Ads from 7 March to 10 October 2026 is incomplete, and so is the lead source in the contact form from 13 July (when the form started recording it) to 10 October. We draw no channel conclusions from that period.
Why does traffic from AI chatbots show up as direct?#
GA4 files a visit as direct when it carries neither a referrer nor UTM data, and according to Search Engine Journal that is where AI chatbot traffic often lands. Search Engine Journal also reported on 1 October 2026 that Gemini appears to add UTM parameters to some links, which Google has not documented. Any such tags only help if no redirect on your site strips them.

Need an FAQ tailored to your industry and market? I can build one aligned with your business goals.

Let’s discuss

Related Articles