TidyURLs
Go to Tools
BLOG
The TidyURLsJournal

Practical guides, tutorials, and insights about URLs, SEO, web development, and online marketing. Learn, troubleshoot, and get the most of the web.

Back to the Journal
TidyURLs Journal

Build a Chrome Extension to Clean Tracking URLs

Practical insights, clear explanations, and useful ideas for understanding the web.

October 25, 20266 min read5 topics
Build a Chrome Extension to Clean Tracking URLs
Explore the details
#Chrome Extensions#JavaScript#Manifest V3#URL API#Browser Development
October 25, 20266 min read

Tracking parameters can make URLs long, harder to share, and less tidy. In this project tutorial, you’ll build a small Manifest V3 Chrome extension that lets a user paste a URL, remove selected tracking parameters, and keep the rest of the query string intact. It uses JavaScript’s URL and URLSearchParams APIs and does not need broad site access.

Key takeaways

  • A popup with a user-pasted URL can clean links without access to the current tab or permission to read websites.
  • Removing a known list of tracking keys is more conservative than deleting every query parameter.
  • The browser’s URL APIs preserve the URL’s remaining parameters and fragment, but may normalize its serialized form.
  • Test both what gets removed and what must remain, including duplicate and unfamiliar query parameters.

What the extension will do

The extension opens a popup when its toolbar button is clicked. The user pastes a complete HTTP or HTTPS URL and submits the form. The popup displays a cleaned URL that the user can select and copy.

This design is deliberately user-triggered: the extension does not inspect the active tab, monitor browsing, or send URLs to a server. It needs no host permissions and no special permissions for this workflow.

The cleaner removes utm_ parameters and a small set of commonly used advertising or click identifiers. Unknown parameters are left alone because they may be required by the site. For a one-off cleanup without installing an extension, you can also use TidyURLs’ URL cleaner.

Create the extension files

Make a new folder, for example tracking-url-cleaner, and add these four files:

tracking-url-cleaner/
├── manifest.json
├── popup.html
├── popup.css
└── popup.js

1. Define the Manifest V3 extension

Create manifest.json:

{
  "manifest_version": 3,
  "name": "Tracking URL Cleaner",
  "version": "1.0.0",
  "description": "Remove selected tracking parameters from a pasted URL.",
  "action": {
    "default_title": "Clean a URL",
    "default_popup": "popup.html"
  }
}

The action opens the popup. There is no permissions or host_permissions entry because this extension only processes text entered into its own interface. If you later add features that read the current tab, access websites, or communicate with a service, reassess the required permissions and explain them to users before adding them.

2. Build the popup interface

Create popup.html:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Tracking URL Cleaner</title>
    <link rel="stylesheet" href="popup.css">
    <script src="popup.js" defer></script>
  </head>
  <body>
    <main>
      <h1>Clean a URL</h1>
      <form id="cleaner-form">
        <label for="input-url">Paste an HTTP or HTTPS URL</label>
        <textarea id="input-url" rows="4" required
          placeholder="https://shop.example/products?utm_source=email&color=blue"></textarea>
        <button type="submit">Remove tracking parameters</button>
      </form>
      <label for="output-url">Cleaned URL</label>
      <textarea id="output-url" rows="4" readonly
        aria-describedby="status"></textarea>
      <p id="status" role="status" aria-live="polite"></p>
    </main>
  </body>
</html>

The output is read-only to prevent accidental edits from being mistaken for the cleaner’s result. Users can still select the text and copy it themselves, so the extension does not need clipboard permissions.

3. Add simple popup styling

Create popup.css:

body {
  box-sizing: border-box;
  font: 14px/1.5 system-ui, sans-serif;
  margin: 0;
  min-width: 340px;
  padding: 16px;
}

main {
  display: grid;
  gap: 10px;
}

h1 {
  font-size: 18px;
  margin: 0;
}

textarea {
  box-sizing: border-box;
  font: inherit;
  padding: 8px;
  resize: vertical;
  width: 100%;
  overflow-wrap: anywhere;
}

button {
  cursor: pointer;
  font: inherit;
  padding: 8px 12px;
}

#status {
  margin: 0;
  min-height: 1.5em;
}

4. Parse the URL and remove selected keys

Create popup.js:

const TRACKING_KEYS = new Set([
  "gclid",
  "dclid",
  "fbclid",
  "msclkid",
  "twclid",
  "ttclid",
  "li_fat_id",
  "mc_cid",
  "mc_eid"
]);

const form = document.querySelector("#cleaner-form");
const input = document.querySelector("#input-url");
const output = document.querySelector("#output-url");
const status = document.querySelector("#status");

function cleanUrl(rawValue) {
  const url = new URL(rawValue.trim());

  if (url.protocol !== "http:" && url.protocol !== "https:") {
    throw new Error("Enter a URL that starts with http:// or https://.");
  }

  const keysToRemove = new Set();

  for (const [key] of url.searchParams) {
    const normalizedKey = key.toLowerCase();

    if (normalizedKey.startsWith("utm_") || TRACKING_KEYS.has(normalizedKey)) {
      keysToRemove.add(key);
    }
  }

  for (const key of keysToRemove) {
    url.searchParams.delete(key);
  }

  return url.href;
}

form.addEventListener("submit", (event) => {
  event.preventDefault();
  output.value = "";

  try {
    output.value = cleanUrl(input.value);
    status.textContent = "Tracking parameters removed where recognized. Select the result to copy it.";
  } catch {
    status.textContent = "Enter a valid, complete HTTP or HTTPS URL.";
  }
});

new URL() validates and parses the input. searchParams provides access to the query parameters, and delete() removes every value for a matching key. The code first collects keys to remove, then deletes them; this avoids changing the parameter list while iterating over it. Matching is case-insensitive for the selected tracking names, while unrecognized keys are retained.

Test with sample URLs

Load the extension in Chrome by opening chrome://extensions, enabling Developer mode, choosing Load unpacked, and selecting the project folder. Click the extension’s toolbar button to open its popup.

Try this input:

https://shop.example/products?utm_source=email&color=blue&size=m#details

The result should retain color, size, and the #details fragment while removing utm_source:

https://shop.example/products?color=blue&size=m#details

Also test an input with an unfamiliar parameter and repeated keys:

https://shop.example/search?q=boots&variant=wide&variant=blue&campaign_code=spring

Because none of those names are in the removal rules, they should all remain. Then test a URL with both an identifier and ordinary parameters:

https://shop.example/?gclid=sample123&category=books&utm_medium=email

The result should retain category and remove gclid and utm_medium. Finally, try malformed input and a non-HTTP scheme such as javascript:...; the popup should show an error and should not display a cleaned result.

Common mistakes and limitations

Removing the entire query string

Clearing url.search would also remove functional values such as search terms, product variants, language choices, or pagination. This example removes only recognized names and leaves unknown parameters in place.

Assuming every utm_ key is harmless to remove

A site could use a name that resembles a tracking parameter for its own behavior. The utm_ prefix rule is convenient but broad. If your audience uses sites where such parameters may be functional, replace the prefix rule with an explicit allowlist of tracking keys, or let users review and configure the keys before removal.

Expecting the exact original text back

The URL API returns a parsed and serialized URL. Its output can normalize parts of the input, including query encoding, even when the remaining parameters have not been intentionally changed. This tool preserves the meaning of ordinary parameters as represented by the parsed URL; it does not promise byte-for-byte preservation of the original string.

Treating a client-side cleaner as a privacy guarantee

The extension changes the URL text the user submits. It cannot remove tracking that happens through cookies, scripts, headers, or other mechanisms, and it cannot guarantee that a destination site will not identify a visit. Avoid pasting URLs containing private tokens or other sensitive values into tools you do not trust.

Improvements to consider

For a more polished project, add tests for cleanUrl() covering empty input, invalid URLs, duplicate tracking keys, uppercase parameter names, fragments, and URLs with no query string. You could also make the tracking-key list configurable. Keep the default behavior conservative: document exactly which keys are removed, preserve unknown parameters, and avoid requesting browser permissions the feature does not use.

Conclusion

This small Manifest V3 project demonstrates a practical way to build a Chrome extension with JavaScript: provide a user-triggered interface, parse pasted links with the URL API, and remove only explicitly recognized tracking parameters. Keeping the extension independent of tabs and websites makes its permission needs minimal while giving you a clear base for testing and further development.