UTM and other tracking parameters

I was reading an article, it contained some references to another page. I opened the link in a new tab, and noticed how "messy" it looked: https://www.website-B.com/page?utm_source=website-A.com&utm_medium=referral`.

To no surprise, a cleaner form of the URL is more readable, and points to the same resource: https://www.website-B.com/page.

This made me particularly angry, as

  • there is no affiliation between website-A and website-B

  • website does not inform its user it will hand additional metadata to an external website

  • website B could (currently) not care less of this metadata

It is a form of tracking, without consent, that also leads to unreadable URLs. A nice example of enshittification 🗄️, where no one wins. Not the end-user, which has unreadable URLs and needs to pay attention not to share some private information when sharing them, not the owner of website A, as there is no affiliation, and not the owners of website B, as it could not care less about this data, and has to handle potentially broken URLs.

I noticed that all URLs to external sites are appended with UTM parameters for tracking purposes, so this is probably done automatically by some framework used for managing the website.

Since I do not want to be tracked unnecessarily, and most importantly wanted to save some pointed resources as references for the future, but did not want to clean them manually, I wrote a new bookmarklet:

clean-urls
javascript: (() => {
  const p = /^(utm_[^=]+|fbclid|gclid|dclid|msclkid|mc_cid|mc_eid|ref|ref_|_ga|_gl)$/i;
  function cleanurl(url) {
    [...url.searchParams.keys()].forEach(k => p.test(k) && url.searchParams.delete(k));
    return url;
  }
  document.querySelectorAll('a[href]').forEach(a => {
      try {a.href = cleanurl(new URL(a.href)).href;} catch {}
  });
  try {history.replaceState(null, '', cleanurl(new URL(location.href)).href);} catch {}
})()

This bookmarklet strips all utm_ parameters and, a couple of others I found in my bookmarks, in the current webpage, and in browser navigation bar.

As usual, the disadvantage of the bookmarklet is that it cannot be triggered automatically.

For Firefox there are a couple of alternatives, with a different set of advantages and disadvantages.

An add-on

The first is relying on an add-on, for example clearurls.

The main advantage is that it acts on your behalf automatically when clicking on a link, and also works on Android. The disadvantage is that it has access to all webpages, so a very broad permission for such a simple task.

Another disadvantage is that in its current form, it does not "clean" the website. It "just" removes the tracking parameters when clicking a link. Thus, if you copy the URL, you’ll copy the tracking parameters too.

If you are already using uBlock Origin, which is more or less a requirement for security, note that it also provides a similar functionality. Under Filter lists, enable "AdGuard/uBO - URL Tracking Protection". Similarly to clearurls, it does not clean the HTML page.

Other content managers might provide a similar feature.

Built-in functionality

Currently, Firefox has a built-in functionality for cleaning URLs, as part of the "antitracking" component 🗄️.

From the settings it is not possible to enlist directly which query parameters to strip, but it can be done from about:config:

  • set privacy.query_stripping.enabled to true

  • if you want to enable it for private mode too, set privacy.query_stripping.enabled.pbmode to true

  • set privacy.query_stripping.strip_list to a space-separated-list of parameters; for example utm_source utm_medium utm_campaign utm_term utm_content fbclid gclid

The disadvantage of the built-in functionality is that it is not particularly flexible. There is no easy way to quickly toggle this setting without opening about:config, or enforce a different list on a particular website.

Other browser might have a similar feature, like Brave 🗄️.

Are there any downsides?

Yes. In fact, some URLs could break.

Normally https://example.com/?utm_medium=referral and https://example.com/ point to the same resource, but the server is free to do something with the query parameter and give a different response.

So, for the same, reason, also blindly adding (like more and more websites seems to do) query parameters can have unintended side effects.

Given the current practice of blindly "poisoning" every URL with those parameters for no good reason, chances of actually breaking something are very low, so I’m not concerned about it.

Best practices

At least, from my point of view.

As a visitor of a website

Clean the URLs.

There is no advantage in keeping them dirty, having readable URLs make it easier to understand where they are pointing too, and easier to share.

Unfortunately it is not always easy to understand which query parameters are for some functionality and which one are for tracking, so on some websites multiple attempts might be necessary.

Fortunately the browser itself or an extension can help to automate the task.

I wonder if I should start to add random UTM parameters on internal links when visiting webpages that triggered me to write this notes instead of only removing them from external webpages…​

As website owner that does not use query strings

My first approach was to block them.

Someone is adding some query parameter that I might want use in the future, and since I do not want valid URLs to turn 404 or to point something different, I prefer to return 404 right now, like for any other invalid URL.

Once I noticed around 2023 some requests with the query parameter mdrv I tried to find out who was adding it without success. Like this user on stackoverflow 🗄️, I ended blocking all request with that particular query parameter.

Block mdrv query parameter
RewriteEngine On
RewriteCond %{QUERY_STRING} ^mdrv=
RewriteRule ^ - [F,L]

Then I realized that more and more visitors had those utm query parameters. Blocking them would have been a disservice for the users, who are not directly at fault.

At this point I decided to simply clear the URLs and remove the query parameters.

If currently there are no query parameters with a special meaning, one could strip them all

Strip all query parameters
RewriteEngine On

RewriteCond %{QUERY_STRING} .
RewriteRule ^ %{REQUEST_URI} [R=301,L,QSD]

But once some parameters are supported, one needs to handle some parameter differently.

For example, one could remove only the utm parameters with the following rule:

Strip only utm parameters
RewriteEngine On

RewriteCond %{QUERY_STRING} (.*)(?:^|&)utm_[^&]*(&.*)? [NC]
RewriteRule ^ %{REQUEST_URI}?%1%2 [R=301,L]

The major downside of this rewrite rule, is that it causes a redirect for every utm parameter, thus https://example.come/?utm_source=foo&utm_campaign=baz&utm_medium=mail causes three 301 redirects.

While I am using something similar right now, I cannot say I’m happy with this approach, it feels like hiding the dirt under the carpet.

Why are other people adding irrelevant things to my URLs? Why should I repair them?

Another approach, is to inform the user that whoever game them the URL should feel ashamed and since the user is not at fault, give him what is probably the correct URL.

I like the idea, it was also implemented on this blog 🗄️. I suppose that most user would probably be annoyed by it, or even not understand what is going on. Plus it makes the website more complex compared to blocking or strip the parameters. I’m torn if I should implement something similar, I have been toying with the idea multiple times since I’ve blocked the mdrv query parameter.

As a website owner who wants to track users

It’s not me, but I since I find some use-cases for all those query parameters confusing and questionable, here are my two cents.

For example, there is the referrer header 🗄️, why not use that instead of utm_source?

And in case the answer is "because the user can block it", then the response to that decision should not be to ignore the desire of the user and track it with other tools.

Compared to a query parameter, the referrer header has the main advantage that the link stays clean the link stays clean.

As replacement for utm_campaign, use a dedicated link.

Instead of https://example.com/?utm_campaign=sale-2026, create a dedicated page: https://example.com/sale-2026

It reads better, and you can extract exactly the same information.

As replacement for utm_medium you could also make separate pages; instead of https://example.com/?utm_medium=mail and https://example.com/?utm_medium=feed have https://example.com/mail and https://example.com/feed

Granted, those alternatives do not scale well or work well in many situations.

You do not want to duplicate every page in order to add a separate mail page for tracking those who clicked a link on the mail. And creating those pages on the fly require a more complex setup, while adding a query parameter comes for free.

And I’m surely to naive thinking that those use-cases are not that relevant.

So while using more or less standardized query flags is useful and easy, and can be used to provide benefits (mostly to the site owner) they have been misused to the point I want to block them all.

At least remove those query flags once the site is loaded, especially if I gave the content to track me on the webpage. Once I copy the URL and give it to someone else, the information it is carrying is probably wrong.

https://example.com/?utm_medium=mail might not be the link one clicks from the mail. It might be a link I saved in the browser, or shared on a chat.

While it is true that it shows the mail campaign was successful, I also might have the link https://example.com/?utm_medium=feed stored somewhere, and it is just by sheer luck that I shared https://example.com/?utm_medium=mail instead of https://example.com/?utm_medium=feed.

Look for example at this extract from Wikipedia:

<li id=cite_note-31><span class=mw-cite-backlink><b><a href=#cite_ref-31 aria-label="Jump up" title="Jump up">^</a></b></span> <span class=reference-text><link rel=mw-deduplicated-inline-style href=mw-data:TemplateStyles:r1238218222><cite class="citation web cs1"><a rel=nofollow class="external text" href="https://www.reddit.com/r/Twitter/comments/1l0ft16/comment/mvd7pfr/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">"<span class=cs1-kern-left></span>"Why are accounts being permanently suspended without explanation?" – Reddit"</a>. <i>Reddit</i><span class=reference-accessdate>. Retrieved <span class=nowrap>27 June</span> 2025</span>.</cite><span title="ctx_ver=Z39.88-2004&amp;rft_val_fmt=info%3Aofi%2Ffmt%3Akev%3Amtx%3Ajournal&amp;rft.genre=unknown&amp;rft.jtitle=Reddit&amp;rft.atitle=%22Why+are+accounts+being+permanently+suspended+without+explanation%3F%22+%E2%80%93+Reddit&amp;rft_id=https%3A%2F%2Fwww.reddit.com%2Fr%2FTwitter%2Fcomments%2F1l0ft16%2Fcomment%2Fmvd7pfr%2F%3Futm_source%3Dshare%26utm_medium%3Dweb3x%26utm_name%3Dweb3xcss%26utm_term%3D1%26utm_content%3Dshare_button&amp;rfr_id=info%3Asid%2Fen.wikipedia.org%3ATwitter+suspensions" class=Z3988></span></span>
</li>

Someone added a link before cleaning it. Now everyone clicking on the link from Wikipedia will have those utm query flags that look completely wrong.

So even if you want to use query parameters

  • do not add them to external links unless you have a reason to

  • do not change links to external websites, most of the time you should prefer the canonical URL

  • use those utm query parameters for internal links, but remove them once they are not needed anymore / are tracking users with other tools


If you have questions, comments, or found typos, the notes are not clear, or there are some errors; then just contact me.