What Is URL Normalization? β Definition and How It Works
Learn which URL transformations are safe, which change meaning, and why normalization decides how much of a crawl is wasted.
URL normalization converts URLs to a canonical form so that different strings pointing at the same resource are recognised as one. Without it, a crawler fetches the same page many times.
1. Why It Decides Crawl Efficiency
These are usually one resource:
http://Example.com/Page/
https://example.com/page
https://example.com/page?utm_source=x
https://example.com/page#section
https://www.example.com/page/index.html
Five distinct strings. A crawler without normalization fetches five pages, stores five copies, and follows five sets of links β and the multiplication compounds at every level of depth.
2. Safe Transformations
These never change which resource is addressed:
| Transformation | Example |
|---|---|
| Lowercase scheme and host | HTTP://Example.COM β http://example.com |
| Remove default port | https://example.com:443/ β https://example.com/ |
| Add empty path | https://example.com β https://example.com/ |
| Resolve dot segments | /a/./b/../c β /a/c |
| Decode unreserved percent-escapes | %7Euser β ~user |
| Uppercase remaining escapes | %3a β %3A |
| Drop the fragment | /page#top β /page |
The fragment is a client-side pointer and is never sent to the server, so dropping it for fetching and deduplication is always correct.
3. Transformations That Are Not Safe
| Transformation | Why it can be wrong |
|---|---|
| Lowercase the path | Paths are case-sensitive on most servers |
| Strip or add a trailing slash | /page and /page/ can be different resources |
Drop index.html |
May not be the directory default |
Remove www. |
Can be a genuinely different host |
| Sort query parameters | Order can be significant to the application |
| Remove all query parameters | Destroys the resource identity |
Every one of these is applied routinely and works on most sites β which is exactly why it is dangerous. It fails on the minority, silently, by merging two distinct pages into one.
4. Tracking Parameters Are the Exception
Removing known tracking parameters is safe in practice, because they are consumed by analytics and never affect the response:
from urllib.parse import urlsplit, urlunsplit, parse_qsl, urlencode
TRACKING_PREFIXES = ("utm_",)
TRACKING_EXACT = {"gclid", "fbclid", "msclkid", "mc_cid", "mc_eid", "ref", "_ga"}
def normalize(url):
parts = urlsplit(url)
scheme = parts.scheme.lower()
host = parts.hostname or ""
port = "" if parts.port in (None, 80, 443) else f":{parts.port}"
path = parts.path or "/"
query = [(k, v) for k, v in parse_qsl(parts.query, keep_blank_values=True)
if not k.lower().startswith(TRACKING_PREFIXES) and k.lower() not in TRACKING_EXACT]
return urlunsplit((scheme, host + port, path, urlencode(query), ""))
Note the empty fifth element β the fragment is dropped.
5. Two Normalization Levels
The distinction that avoids most of the trouble:
Fetch URL β conservative normalization only. This is what you request, and it must address exactly what the link addressed.
Dedup key β aggressive normalization: sorted parameters, trailing-slash unification, whatever the site's observed behaviour justifies. Used only to decide whether to fetch, never as the request.
Keeping them separate means an over-aggressive rule costs a skipped fetch rather than a wrong request.
6. Use the Site's Own Answer
Where a page declares <link rel="canonical">, that is the site telling you which URL it considers primary. Recording it lets you collapse variants correctly rather than by guesswork.
It is a claim, not a guarantee β canonical tags are sometimes wrong or self-referential across genuinely distinct pages β so treat it as strong evidence rather than proof.
7. Practical Rules
- Always: lowercase scheme and host, drop the fragment, resolve dot segments, remove default ports.
- Always: strip known tracking parameters.
- Never: lowercase the path, or assume trailing-slash equivalence, without checking the site.
- Keep the fetch URL and the dedup key separate.
- Record canonical URLs when pages declare them.
- Normalise before the seen-set check, or the set does not work.
8. Summary
URL normalization canonicalises URLs so one resource is not fetched many times, using transformations that are provably safe β case of scheme and host, default ports, dot segments, fragments, tracking parameters β and avoiding those that merely usually work, such as path casing and trailing slashes.
Maintain a conservative fetch URL and a separate aggressive dedup key, and prefer a page's declared canonical URL over inference.