npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

urlpurify

v2.2.1

Published

Unwrap redirect, affiliate, and tracking wrapper URLs and strip tracking parameters. Turn noisy links into clean, direct URLs.

Readme

urlpurify

codecov npm version license

Unwrap redirect, affiliate, and tracking wrapper URLs and strip tracking parameters. Turn noisy links into clean, direct URLs.

Links from feeds, emails and social platforms rarely point straight at their destination. They pass through search redirects, link shims, email gateways and affiliate networks, and pick up tracking parameters on the way, the link decoration that follows a reader from site to site. urlpurify unwraps 380+ known wrappers and strips 100+ known tracking parameters plus vendor families. Campaign tags like utm_* and affiliate ids stay by default: they serve the site, not a tracker. It has no dependencies and runs in any modern JavaScript runtime, browsers included.

Installation

npm install urlpurify

Quick start

import { cleanUrl } from 'urlpurify'

const url = 'https://www.google.com/url?q=https%3A%2F%2Fexample.com%2Fpost%3Ffbclid%3Dabc'

cleanUrl(url) // => 'https://example.com/post'

API

cleanUrl(url, options?)

Unwraps wrappers, repeatedly since they nest, then strips tracking parameters. The result is serialized per the URL Standard, and input that can't be parsed comes back unchanged, so the result is always safe to display.

import { cleanUrl, defaultTrackingParams, defaultUnwrappers, unwrapWebArchive } from 'urlpurify'

cleanUrl(url, {
  // First match wins per pass.
  unwrappers: [...defaultUnwrappers, unwrapWebArchive],
  // Names, or regexes tested on the lowercased name.
  trackingParams: [...defaultTrackingParams, 'newsletter_id', /^cmp_[a-z0-9_]+$/],
  // The default.
  maxUnwrapDepth: 12,
})

An unwrapped target that isn't a usable URL is ignored, and the wrapper is kept.

unwrapUrl(url, unwrappers?)

Runs one pass of the unwrappers and returns the first target, or undefined.

stripTrackingParams(url, trackingParams?)

Removes tracking parameters and returns the serialized URL. Both this and cleanUrl apply three rules on top of the list:

  • ref is dropped when it names the URL's own host, as in Ghost's ?ref=example.com, and kept otherwise.
  • A query with a signature (sig, signature, X-Amz-Signature or X-Goog-Signature) stays whole, since dropping anything breaks the signature.
  • ts stays on Alibaba DirectMail click URLs, which fail without it.

A tracking list is compiled on first use and cached per array. To change it, pass a new array.

createParamExtractor(config)

Builds an unwrapper for a target that sits in a query parameter:

import { cleanUrl, createParamExtractor, defaultUnwrappers } from 'urlpurify'

const unwrapExample = createParamExtractor({
  // The domain and every subdomain. Use `hosts` for exact hosts or a regex.
  domains: 'example.com',
  path: '/out',
  params: ['target'],
})

cleanUrl(url, { unwrappers: [...defaultUnwrappers, unwrapExample] })

A value still encoded after one decode, starting with http%3A or https%3A, is decoded once more. A malformed escape, or one in a legacy charset such as %E9 for é in Latin-1, stays as written. For any other shape, write a plain UrlUnwrapper: a function that takes a URL and returns the target or undefined.

Tracking parameters

Tracking parameters are grouped like the unwrappers, by what stripping them takes away from someone:

| Category | Export | Stripping removes | Default | | --- | --- | --- | :---: | | Click attribution | clickAttributionParams | Ad, search and platform click ids | ☑️ | | Visitor ids | visitorIdParams | Ids of one visitor, session, browser or email recipient | ☑️ | | Housekeeping | housekeepingParams | Nothing: cache busters, previews and redirect leftovers | ☑️ | | Campaign tags | campaignParams | The site's own traffic stats, from tags like utm_* | | | Affiliate ids | affiliateParams | The publisher's commission | |

defaultTrackingParams combines the first three. A campaign tag carries the same value for every reader, so it tells the site where a visit came from and nothing about who made it. To strip more, spread them next to the defaults:

import { campaignParams, defaultTrackingParams, stripTrackingParams } from 'urlpurify'

stripTrackingParams(url, [...defaultTrackingParams, ...campaignParams])

trackingParamsLiterals and trackingParamsPatterns hold every known parameter, opt-in categories included, split into names and regexes.

Unwrappers

There are more than 380 unwrappers, each in its own file in src/unwraps, with a comment naming the service and the URL shape it handles. Each is exported on its own and belongs to one category. The categories split by what unwrapping takes away from someone, which is what decides whether you want it:

| Category | Export | Unwrapping removes | Default | | --- | --- | --- | :---: | | Search clicks | searchClickUnwrappers | The search engine's or aggregator's click logging | ☑️ | | Link shims | linkShimUnwrappers | Nothing, or a platform's click count | ☑️ | | Press releases | pressReleaseUnwrappers | The issuer's click stats from the wire | | | Sign-in shims | signInShimUnwrappers | Nothing, but the shim's page needs a sign-in | | | Security gateways | securityGatewayUnwrappers | The gateway's click-time check of the target | | | Email tracking | emailTrackingUnwrappers | The sender's click stats | | | Affiliate links | affiliateUnwrappers | The publisher's commission | | | Advertising | advertisingUnwrappers | Ad click and app install attribution | | | Click counters | clickCounterUnwrappers | A site's or feed's count of which links get clicked | | | Download measurement | downloadMeasurementUnwrappers | A podcast measurement prefix's download counts, or a site's download counts | | | Archives and proxies | archiveProxyUnwrappers | The archived or translated copy: you get the live page | | | Image proxies | imageProxyUnwrappers | The resized or optimized copy: you get the original image | | | Embed proxies | embedProxyUnwrappers | The site's frame around another provider's embed: you get the provider's page | |

defaultUnwrappers combines the first two. The rest cost somebody something or change the page: a feed click counter tells a blog which posts get read, an affiliate link may pay for a small blog, and an archive link points at a copy on purpose. To turn more on, spread them next to the defaults:

import { affiliateUnwrappers, cleanUrl, defaultUnwrappers } from 'urlpurify'

cleanUrl(url, { unwrappers: [...defaultUnwrappers, ...affiliateUnwrappers] })