Skip to content

Repository files navigation

Clear Trail

Every URL you click is carrying baggage you didn't ask for. utm_source, fbclid, gclid, Amazon affiliate tags stuffed into paths, session tokens encoded in Base64, Facebook tracking buried in URL fragments. URL shorteners hide where the link actually goes. Some domains register with Cyrillic characters that look identical to paypal.com in your browser bar.

Clear Trail is a Chrome extension that deals with all of it. It runs in the background, cleans every URL as you navigate, and doesn't need you to click anything or paste anything into a popup. Install it and forget about it.

No content scripts. No clipboard access. No popup. Just clean URLs.

The Pipeline

Your URLs pass through two layers. Both are driven by JSON rulesets, not hardcoded logic.

Layer 1: declarativeNetRequest

This is the fast path. Chrome's native declarativeNetRequest API strips known tracking parameters before the request leaves your browser. The server never sees utm_source=newsletter or fbclid=IwAR0... because they're gone before the TCP connection opens. Zero latency. No JavaScript runs. This handles 588 known parameter names across all platforms.

Layer 2: Service Worker

DNR can only remove exact parameter names. It can't match regex patterns like pf_rd_[a-z]+, it can't touch URL fragments, and it can't rewrite paths. Layer 2 fills those gaps:

  • Regex rules that DNR can't express (Amazon's hsa_*, hv*, pd_rd_* patterns)
  • Fragment cleaning for tracking stuffed after # (Facebook's __cft__, single-page apps)
  • Path rewriting like stripping Amazon's /ref=sr_1_1 from product URLs
  • Redirect extraction that unwraps tracking wrappers to reveal the real destination
  • Allowlist-based cleaning where only specific params are kept and everything else is stripped (YouTube: keep v, t, list, drop the rest)

Layer 2 fires on webNavigation.onCommitted, runs the full rule engine, and redirects via tabs.update() if the URL changed. This is sub-millisecond for pure rule matching, so you won't see it happen.

What Gets Stripped

Here's a non-exhaustive sample, because the full list is 943 rules across 262 providers:

Platform Examples of what's removed
Global utm_source, utm_medium, utm_campaign, fbclid, gclid, msclkid, ttclid, twclid, _ga, _gl, dclid, yclid, mkt_tok, mc_cid, _hsenc, _branch_match_id
Amazon tag, ascsubtag, pf_rd_*, pd_rd_*, hsa_*, hv*, qid, sr, dib, /ref= in path
AliExpress aff_*, spm, pvid, terminal_id, browser_id, clickTime, tracelog, scm_id
Google ved, ei, gs_lcp, sclient, sxsrf, tbs, prmd, biw, bih, sca_*
Facebook fbclid, __cft__, __tn__, __eep__, fref, hrc, fb_ref, fbadid
LinkedIn trackingId, refId, trk, lipi, midToken, trkEmail, licu, _li
YouTube Everything except v, t, list, index, feature, start
Twitter/X s, t, twclid, twsrc, cxt, ref_src
Instagram igshid, img_index

Plus 85 redirect extraction rules that unwrap Google AMP links, DuckDuckGo /l/?uddg= redirects, Amazon /gp/redirect.html, affiliate wrappers from ShareASale, Awin, and others.

Rule Sources

All cleaning logic lives in JSON files. There is no platform-specific TypeScript code. Three rulesets are compiled and chained at startup:

ClearURLs (clearurls.json): 205 providers from the open-source ClearURLs project. Auto-updated weekly from rules2.clearurls.xyz. This is the baseline that most URL cleaners use.

Unalix (unalix.json + unalix_extended.json): 50+ additional providers from the Unalix project. These fill gaps that ClearURLs doesn't cover: Amazon's hsa_*/pf_rd_*/asc_* patterns, AliExpress's pvid/browser_id/clickTime, Google's sclient/tbs/sca_*, LinkedIn's midToken/trkEmail, and platform-specific redirect extraction rules.

Clear Trail (cleartrail.json): Our own rules. This is where we put anything the upstream sources miss, and where the allowParams extension lives (the YouTube allowlist). Standard ClearURLs format, plus one custom field.

The allowParams Extension

Standard ClearURLs rules say "remove these parameters." That works for most sites. YouTube is the opposite problem: you want to keep v (the video ID) and t (timestamp) and drop everything else. You can't enumerate every possible tracking param YouTube might add next quarter.

allowParams inverts the logic. If a provider has "allowParams": ["v", "t", "list"], then any parameter NOT in that list gets removed. One field in a JSON file. No code.

Rule Compilation

Rules are compiled using regex wrapping ported from Unalix. A rule string like utm_source doesn't get tested against parameter names with a simple string match. It gets wrapped into:

/(%(?:26|23)|&|^)utm_source(?:(?:=|%3[Dd])[^&]*)/g

This handles percent-encoded separators (%26 for &, %23 for #), the literal & delimiter, start-of-string position for the first parameter, and percent-encoded equals signs. It prevents false substring matches where my_utm_source_extra would incorrectly trigger a rule for utm_source.

Every rule from remote sources goes through safeCompile() with a 2048-character length limit to prevent ReDoS.

Security

This is a privacy tool. It would be embarrassing if it introduced security holes.

SSRF Protection

Shortener expansion follows redirects to discover the real destination. A malicious shortener could redirect to http://169.254.169.254/latest/meta-data (AWS instance metadata) or http://192.168.1.1/admin (your router). Clear Trail validates every hop before making the next request:

  • Private IPv4 ranges: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8
  • IPv6 loopback and link-local: ::1, fe80::/10, fc00::/7
  • Cloud metadata: 169.254.169.254, metadata.google.internal, metadata.goog
  • Any hostname ending in .internal
  • 0.0.0.0

If any hop fails validation, the chain aborts immediately. The request is never sent.

Hop-by-Hop Resolution

The shortener resolver uses fetch() with redirect: 'manual'. It reads the Location header, validates the target, and only then makes the next request. It never hands control to the browser's redirect-following machinery. Maximum 10 hops, 8-second total timeout.

Protocol Validation

javascript:, data:, file:, chrome:, and about: URLs are rejected at every boundary. A redirect chain that lands on javascript:alert(document.cookie) gets blocked, not followed.

Upstream Poisoning Protection

Clear Trail fetches updated ClearURLs rules weekly. A compromised upstream could inject rules that preserve tracking parameters or add legitimate domains to redirect rules. Every update is validated before acceptance:

  • Schema validation: Each provider must have a valid urlPattern, rule arrays must contain strings, no unexpected fields
  • Size limits: Max 5MB JSON, 500 providers, 200 rules per provider, 5000 total rules
  • Regex compilation: Every pattern must compile as valid regex within the 2048-char ReDoS limit
  • Delta detection: Flags updates where >30% of providers were added or removed in a single update
  • Rollback: If validation or compilation fails, the previous known-good ruleset is restored automatically

Homograph Detection

Detects IDN domains using Cyrillic (а → a, о → o, с → c), Greek (α → a, ο → o), and fullwidth characters that impersonate protected domains like Google, PayPal, Amazon, and 30+ others.

Caching

Three LRU caches, all persisted to chrome.storage.local:

Cache Entries TTL What it does
Unshortener 500 7 days Stores expanded URLs so bit.ly/abc doesn't get re-fetched every time you click it
Cleaning 1,000 24 hours Stores cleaning results so the same tracked URL from a newsletter doesn't get re-analyzed on every click
Compiled rulesets 1 Until update Stores the compiled regex objects so 262 providers don't get recompiled on every service worker cold start

Permissions

Permission Why
declarativeNetRequest Layer 1: strip params before the request is sent
declarativeNetRequestFeedback Stats on how many rules matched
webNavigation Layer 2: regex rules, fragments, path rewriting
storage Settings, caches, compiled rulesets
alarms Weekly rule update schedule
<all_urls> Required for DNR to match requests on any domain

No scripting permission. No clipboardWrite. No contextMenus. No content scripts. Nothing is injected into any page.

Options

Right-click the extension icon, select "Options":

  • Domain whitelist: Sites you trust. These will never be cleaned.
  • Shortener expansion: Off by default. When enabled, resolves bit.ly, t.co, amzn.to, etc. to their real destinations. Note: this sends a HEAD request to the shortener service.
  • Rule update: Shows last update time, provider count, DNR rule count. Manual update button.
  • Cache stats: Unshortener and cleaning cache sizes. Clear button.

Development

git clone https://github.com/user/cleartrail.git
cd cleartrail
npm install
npm test              # 95 tests
npm run build         # Bundle with esbuild
npm run build:dnr     # Generate static DNR rules from rulesets
npm run typecheck     # TypeScript type checking

Load as unpacked extension in chrome://extensions with Developer Mode on.

Project Structure

src/core/              Pure library. No Chrome deps. Testable with Node.js + Jest.
  clean-url.ts         cleanUrl(url, providers, options) -> CleanResult
  compile-rules.ts     ClearURLs JSON -> compiled regexes (Unalix-style wrapping)
  generate-dnr.ts      Compiled rules -> declarativeNetRequest rules
  validate-ruleset.ts  Upstream update validation (schema, size, regex, delta)
  url-security.ts      SSRF detection, protocol validation
  homograph.ts         IDN homograph attack detection
  regex-safety.ts      ReDoS-safe regex compilation
  cache.ts             LRU cache with TTL
  errors.ts            Typed exception hierarchy (not generic try/catch)
  types.ts             All type definitions

src/extension/         Chrome-specific. Depends on core.
  service-worker.ts    Entry point, initialization, message handling
  dnr-manager.ts       declarativeNetRequest lifecycle
  navigation-cleaner   Layer 2 via webNavigation.onCommitted
  shortener-resolver   Hop-by-hop expansion with SSRF validation
  rule-updater.ts      Weekly updates with validation and rollback
  cache-manager.ts     LRU cache persistence to chrome.storage.local
  stats.ts             Cleaning counter

data/rulesets/         All cleaning logic. Zero platform-specific code.
  clearurls.json       205 providers from ClearURLs
  unalix.json          50+ providers from Unalix
  unalix_extended.json Extended tracking params from Unalix
  cleartrail.json      Our rules + allowParams extension

Adding a New Tracker

Edit data/rulesets/cleartrail.json:

{
  "new_service": {
    "urlPattern": "^https?:\\/\\/(?:[a-z0-9-]+\\.)*?newservice\\.com",
    "rules": ["tracking_id", "campaign_ref"],
    "referralMarketing": ["affiliate_tag"]
  }
}

Run npm run build:dnr to regenerate the static DNR rules. No TypeScript changes needed.

For allowlist-based cleaning (keep only specific params):

{
  "new_video_service": {
    "urlPattern": "^https?:\\/\\/(?:[a-z0-9-]+\\.)*?videosite\\.com",
    "allowParams": ["id", "t", "playlist"]
  }
}

Credits

  • ClearURLs for the upstream tracking parameter database
  • Unalix for the regex wrapping approach, SSRF protection design, and additional provider rules
  • PeterDaveHello/url-shorteners for the shortener domain list (CC BY-SA 4.0)

License

MIT

About

Chrome extension that automatically removes tracking parameters from URLs as you browse.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages