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:
- Tier 1: Strip query, www, and trailing slash
- Tier 2: Strip www and trailing slash, keep query
- Tier 3: Keep www, strip trailing slash, keep query
- 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:
| Option | Default Tier 2 | Description |
|---|---|---|
stripScheme | false | Remove the scheme (not recommended for feed URLs) |
stripAuthentication | false | Remove user:pass@ |
stripWww | true | Remove www. prefix |
stripHostTrailingDot | true | Remove the trailing . of a host, example.com. → example.com |
stripTrailingSlash | true | Remove trailing / from paths |
collapseSlashes | true | /// → / |
stripHash | true | Remove #fragment |
sortQueryParams | true | Sort params alphabetically |
stripQuery | false | Remove entire query string |
stripEmptyQuery | true | Remove empty ? |
lowercaseQuery | false | Lowercase query param names and values |
normalizeEncoding | true | Normalize %XX encoding |
Examples
Minimal Tiers
Use a single tier with minimal normalization:
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:
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):
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):
const url = await findCanonical('https://example.com/feed', {
tiers: [
{ stripWww: true, stripTrailingSlash: true },
],
})