How to Normalize Domain Names So Your Lists Dedupe Correctly
Normalize domain names by lowercasing, stripping protocol, www, path, port, and trailing dots, plus IDN punycode folding so analytics and allowlists dedupe right.
How to Normalize Domain Names So Your Lists Dedupe Correctly
I once spent an afternoon trying to figure out why an allowlist of 1,200 customer domains was letting through traffic it should have blocked. The list looked fine. The blocked traffic looked fine. The problem was that Example.com and example.com were sitting in two different rows, and the request had arrived as https://WWW.Example.com/login — a string that matched neither. Nothing was broken in the code. The data was just written four different ways.
Domain names are deceptively messy. They are case-insensitive, they show up wrapped in full URLs, they carry ports and paths that don't belong to the host, and internationalized names can be stored as Unicode or as their xn-- punycode equivalent. Until you fold all of that into one canonical form, any compare, dedupe, or lookup you build on top is quietly counting the same site as several different ones.
This guide walks through what "normalizing a domain" actually means, why analytics and allowlists get it wrong without it, and how to do the cleanup in your browser with the Domain Name Normalizer.
What a canonical domain actually looks like
Here is the concrete point most people skip. Domains are case-insensitive, and a host can arrive wrapped in a URL (https://WWW.Example.com/path) or with a trailing dot (example.com.). Canonical form lowercases the whole thing, strips the protocol, the www label, the path, the port, and the trailing dot, and converts an internationalized domain to its punycode (xn--) form so the same site dedupes to one row.
A few of those steps are worth pausing on:
- Lowercasing is mandatory because DNS treats the host part as case-insensitive.
GitHub.comandgithub.comare the same name; storing both is a bug waiting to happen. - The trailing dot (
example.com.) is technically the fully-qualified form — the empty root label. It's valid, it resolves identically, and almost no other system in your stack writes it that way, so it has to go before a string compare will match. - Punycode is how a name like
café.comis actually transmitted on the wire:xn--caf-dma.com. If half your records store the Unicode form and half store the ASCII form, they will never match on a literal comparison even though they point at the same host.
The exact strip rules and the punycode behavior are part of the tool's parsing engine, so verify the output against the manifest's described behavior for your specific inputs — internationalized labels and unusual masks are exactly where edge cases live.
Why analytics dedupes wrong without it
Analytics platforms count rows. If your referrer or hostname dimension contains Example.com, example.com, www.example.com, and https://example.com/, you get four lines in a report that should be one. The top-referrers table fragments, the totals look smaller than they are, and any threshold ("alert me when a domain sends 500 sessions") fires late or never because the volume is split across spellings.
The same fragmentation hits allowlists and blocklists from the other direction. There, a missed variant is not just a cosmetic split — it's a security gap. An allowlist that contains example.com but receives EXAMPLE.com. will reject a legitimate request; a blocklist with the same gap will wave through traffic you meant to stop. Normalizing both the stored list and the incoming value before you compare them is the only way the match is reliable.
A worked example
Say a teammate pastes you this messy column from three different exports:
https://WWW.Example.com/pricing?ref=hn
Example.com.
example.com:443
café.com
xn--caf-dma.com
GITHUB.COM
Six lines, but only three real hosts. Run it through the normalizer and you get a sorted, deduplicated list:
example.com
github.com
xn--caf-dma.com
The first three inputs all collapse to example.com once the protocol, www, path, query, port, and trailing dot are stripped and the case is folded. The two café.com spellings fold together because the Unicode form is converted to its punycode equivalent, which already matches the fifth line. GITHUB.COM lowercases and the leading whitespace is trimmed. What was a six-row mess that no dedupe would have caught is now three clean rows you can hand to a script or a CRM.
You can also keep the invalid rows visible instead of silently dropping them. A malformed IDN like a bare xn--, a host with a leading dot, or an all-digit label that shouldn't be there can't be canonicalized — surfacing those rows tells you exactly which entries need a human decision rather than burying them.
Picking the right output format
Once the list is clean, the format you copy out in matters as much as the cleanup. The Domain Name Normalizer lets you switch the normalized output between plain lines, CSV, JSON, Markdown, a SQL IN (...) clause, and a TypeScript union, then download the exact artifact. If you're feeding a database filter, the SQL form saves you from hand-adding quotes and commas; if you're typing a literal type for an allowlist constant, the TypeScript union drops straight into code. The CSV and Markdown forms keep line numbers and validation reasons, which is what you want when the list needs an audit trail rather than just a final answer.
One habit worth keeping: copied web text often carries hidden whitespace and zero-width characters. Normalize first, then deduplicate. If you dedupe raw pasted text, two visually identical domains with a stray non-breaking space between them will survive as separate rows, and you're back where you started.
Where it fits with the rest of your pipeline
Normalizing is the middle step. Before it, you usually have to pull the domains out of something larger — a log file, an HTML page, a Markdown doc. The Domain Name Extractor handles that fan-out, finding host strings inside arbitrary text. After extraction and normalization, if all you need is the deduped set, the Domain Name Deduplicator collapses the list, and the Domain Name List Validator flags entries that don't pass as real domains so you can review them before they reach production.
A practical note on validation: a domain passing the format check is not proof that the domain, account, or resource actually exists. Validation tells you the string is well-formed and canonical. Resolution and ownership are separate questions, and treating "valid" as "real" is the second-most common mistake I see after skipping normalization entirely.
Everything here runs locally in the browser. The domains you paste — mixed-case hosts from server logs, IDN names with Unicode labels, columns lifted out of a customer export — are lowercased and punycode-folded inside the tab, with nothing sent to a server. That matters when the list contains customer data or internal identifiers you'd rather not upload anywhere just to tidy up the formatting.
Get one canonical spelling per host, and every downstream count, lookup, and match finally agrees with itself.
Made by Toolora · Updated 2026-06-13