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.
Your URLs pass through two layers. Both are driven by JSON rulesets, not hardcoded logic.
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.
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_1from 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.
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 |
ved, ei, gs_lcp, sclient, sxsrf, tbs, prmd, biw, bih, sca_* |
|
fbclid, __cft__, __tn__, __eep__, fref, hrc, fb_ref, fbadid |
|
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 |
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.
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.
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.
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.
This is a privacy tool. It would be embarrassing if it introduced security holes.
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.
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.
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.
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
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.
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 |
| 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.
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.
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 checkingLoad as unpacked extension in chrome://extensions with Developer Mode on.
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
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"]
}
}- 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)
MIT