URLPolicy reference¶
URLPolicy controls how URLs are normalized before they become canonical. Every canonical URL flows through this pipeline. Create one instance and attach it to your SEOConfig.
Fields¶
| Option | Type | Default | Description |
|---|---|---|---|
enforce_https |
bool |
True |
Force HTTPS scheme in all canonical URLs. |
lowercase_paths |
bool |
True |
Lowercase the path component. |
trailing_slash |
Literal |
"never" |
Trailing slash policy: "always", "never", or "preserve". |
collapse_duplicate_slashes |
bool |
True |
Collapse consecutive slashes into one. |
strip_tracking_params |
bool |
True |
Remove tracking parameters (UTM, fbclid, gclid, 60+ more). |
allowed_query_params |
list[str] |
[] |
Allowlist of query parameters to keep. Empty list keeps all non-tracking params. |
enforce_https¶
When True, all canonical URLs use https://. When False, the scheme from config.public_base_url is used.
URLPolicy(enforce_https=True)
# http://example.com/page → https://example.com/page
URLPolicy(enforce_https=False)
# Uses whatever scheme is in public_base_url
lowercase_paths¶
When True, the path component is lowercased. This prevents duplicate content from case-insensitive URL variations.
trailing_slash¶
Controls whether canonical URLs end with a trailing slash. Accepts exactly three values:
"always": append a trailing slash to every path"never": remove trailing slashes from every path"preserve": leave the path as-is
URLPolicy(trailing_slash="always")
# /blog/post → /blog/post/
URLPolicy(trailing_slash="never")
# /blog/post/ → /blog/post
URLPolicy(trailing_slash="preserve")
# Keeps original casing and slash
collapse_duplicate_slashes¶
When True, consecutive slashes are collapsed into a single slash. Prevents path injection-style duplicates.
strip_tracking_params¶
When True, removes common tracking parameters from URLs. Removes UTM parameters (utm_source, utm_medium, etc.), fbclid, gclid, gclsrc, and 60+ other tracking parameters.
This feature uses the optional detrack library when available, with a pure-Python fallback.
URLPolicy(strip_tracking_params=True)
# /page?utm_source=twitter&utm_campaign=summer&gclid=abc → /page
allowed_query_params¶
When set, only the listed query parameters are kept. All others are stripped. When empty (the default), all non-tracking parameters are preserved.
Processing order: tracking parameters are stripped first, then the allowlist is applied.
URLPolicy(allowed_query_params=["q", "page", "sort"])
# /search?q=seo&page=2&utm_source=twitter&ref=sidebar → /search?q=seo&page=2
Strict policy¶
Combines all options for maximum URL consistency. Use this when you need canonical URLs to be identical regardless of how users arrive.
from seoslug import URLPolicy
policy = URLPolicy(
enforce_https=True,
lowercase_paths=True,
trailing_slash="never",
collapse_duplicate_slashes=True,
strip_tracking_params=True,
allowed_query_params=["q", "page"],
)
What this policy does:
- Forces HTTPS (no mixed content)
- Lowercases paths (
/Blog/Postand/blog/postare the same) - Removes trailing slashes (no slash/no-slash duplicates)
- Collapses
//(no path injection) - Strips UTM, fbclid, gclid (clean URLs)
- Keeps only
qandpage(no arbitrary query params)
config = SEOConfig(
canonical_host="example.com",
public_base_url="https://example.com",
url_policy=URLPolicy(
enforce_https=True,
lowercase_paths=True,
trailing_slash="never",
collapse_duplicate_slashes=True,
strip_tracking_params=True,
allowed_query_params=["q", "page"],
),
)
Result: every canonical URL is predictable, clean, and reproducible.