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.

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. 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
stripProtocolfalseRemove protocol (not recommended for feed URLs)
stripAuthenticationfalseRemove user:pass@
stripWwwtrueRemove www. prefix
stripTrailingSlashtrueRemove trailing / from paths
stripRootSlashtrueRemove / from root paths
collapseSlashestrue////
stripHashtrueRemove #fragment
sortQueryParamstrueSort params alphabetically
stripQueryfalseRemove entire query string
stripEmptyQuerytrueRemove empty ?
lowercaseQueryfalseLowercase query param names and values
normalizeEncodingtrueNormalize %XX encoding
normalizeUnicodetrueNFC normalization

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 beyond sorting the query
})

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,
      stripRootSlash: true,
      collapseSlashes: true,
      stripHash: true,
      stripQuery: true,
      normalizeEncoding: true,
      normalizeUnicode: 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. 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 existsFn already knows it or when it serves the same feed, which costs one extra request. Otherwise it keeps the URL the response came from.

Preserve Query Params

Keep all query parameters (no stripping):

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