Skip to content

Customize URL Tiers ​

Feedcanon applies URL normalization tiers to generate candidates, ordered from cleanest to least clean. The first candidate serving the same content wins. The URL being cleaned takes part too, at the first tier that leaves it unchanged, and a tier listed after that one is never tried. So list your own tiers from cleanest to least clean as well.

Default tiers:

  1. Tier 1: Strip query, www, and trailing slash
  2. Tier 2: Strip www and trailing slash, keep query
  3. Tier 3: Keep www, strip trailing slash, keep query
  4. Tier 4: Keep www and trailing slash, keep query

INFO

In addition to the structural tiers, you can plug extra cleaning into the cleanUrlFn option in FindCanonicalOptions: strip tracking params, unwrap redirect wrappers, or apply any custom rewrite. It runs on every response URL before candidates are generated, so the cleanup stays consistent across all tiers, and on self URLs before they are fetched. The urlpurify package provides ready-made functions for this.

Normalization Options ​

Each tier accepts all NormalizeOptions except stripQueryParams. Tier options are not merged with any defaults: an option left out of a tier is off. The middle column shows the value the default Tier 2 uses, as a reference point:

OptionDefault Tier 2Description
stripSchemefalseRemove the scheme (not recommended for feed URLs)
stripAuthenticationfalseRemove user:pass@
stripWwwtrueRemove www. prefix
stripHostTrailingDottrueRemove the trailing . of a host, example.com. → example.com
stripTrailingSlashtrueRemove trailing / from paths
collapseSlashestrue/// → /
stripHashtrueRemove #fragment
sortQueryParamstrueSort params alphabetically
stripQueryfalseRemove entire query string
stripEmptyQuerytrueRemove empty ?
lowercaseQueryfalseLowercase query param names and values
normalizeEncodingtrueNormalize %XX encoding

Examples ​

Minimal Tiers ​

Use a single tier with minimal normalization:

typescript
import { findCanonical } from 'feedcanon'

const url = await findCanonical('https://example.com/feed', {
  tiers: [{}], // No URL transformations
})

Aggressive Tiers ​

Strip the query, www, trailing slash and hash with a single tier:

typescript
import { findCanonical } from 'feedcanon'

const url = await findCanonical('https://example.com/feed', {
  tiers: [
    {
      stripWww: true,
      stripTrailingSlash: true,
      collapseSlashes: true,
      stripHash: true,
      stripQuery: true,
      normalizeEncoding: true,
    },
  ],
})

Strip Tracking Params ​

Clean tracking params before candidate generation (at the top level, not per-tier):

typescript
import { findCanonical } from 'feedcanon'
import { stripTrackingParams } from 'urlpurify'

const url = await findCanonical('https://example.com/feed', {
  cleanUrlFn: stripTrackingParams,
  tiers: [
    { stripWww: true, stripTrailingSlash: true },
    { stripTrailingSlash: true },
  ],
})

A cleanUrlFn that only edits the query is trusted: Feedcanon uses its result without fetching it, so it should remove only params that do not change which feed the URL serves. The exception is a result that already failed earlier in the call, or that fails when the feed's self URL, a probe or a later candidate names it, which is not used. A result with a different host or path, such as an unwrapped redirect link, is a URL nobody fetched yet. Feedcanon uses it only when it serves the same feed, which costs one extra request, even when existsFn already knows it. Otherwise it keeps the URL the response came from. When the result redirects permanently, the redirect target is used, and cleanUrlFn runs on it once more: a query-only edit is applied, any other result is ignored. A verified result existsFn knows, under either protocol, ends the search like any other existsFn match.

Preserve Query Params ​

Keep all query parameters (no stripping):

typescript
const url = await findCanonical('https://example.com/feed', {
  tiers: [
    { stripWww: true, stripTrailingSlash: true },
  ],
})