URL Slug Generator
Clean URL slugs that transliterate letters instead of silently deleting them.
Updated
Your slug
how-to-make-the-perfect-cafe-au-lait-at-home
- Length
- 44 characters
- Words
- 10
- Safe for a URL
- Yes
Accented letters are reduced to their base letter, and letters that are not accented forms of anything (ß, ø, æ, ł, þ) are transliterated rather than dropped, so nothing silently disappears from the URL.
In short
How do you turn a title into a URL slug?
Lowercase it, strip accents back to their base letters, replace every run of punctuation and whitespace with a single hyphen, and drop any separator at either end. How to Make the Perfect Cafe au Lait at Home becomes how-to-make-the-perfect-cafe-au-lait-at-home, 44 characters. Slugging that again changes nothing.
Accent stripping only works on letters that are accented forms of something; letters that are their own letters, such as the German eszett or the Nordic o with stroke, need an explicit mapping or they vanish.
How to use the URL slug generator
Paste a title and the slug appears immediately, lowercased, accent-stripped, punctuation-free, and joined with single hyphens. How to Make the Perfect Cafe au Lait at Home becomes how-to-make-the-perfect-cafe-au-lait-at-home, 44 characters long. Four options sit alongside it: hyphen or underscore as the separator, lowercase on or off, stop-word stripping on or off, and a maximum length. With stop words stripped that same title becomes how-make-perfect-cafe-au-lait-home, and with a 30-character cap it becomes how-to-make-the-perfect-cafe, truncated at a word boundary rather than mid-word.
What separates a real slug generator from replacing spaces with hyphens is what happens to letters outside plain ASCII. The first step is Unicode normalisation to NFD, which decomposes an accented letter into its base letter plus a separate combining mark. The accent can then be removed and the letter kept, so cafe with an acute accent becomes cafe rather than caf.
Skip that step and the punctuation strip deletes the accented character outright: a naive replace turns resume with two acutes into nave-rsum and Reykjavik into reykjavk, which is not a slug anybody would recognise as the article they clicked on.
That is why this tool returns grosse, ol, lodz, and thor where the mapless pipeline returns gro-e, l, odz, and or, and the reference table below shows the two pipelines side by side on the same titles.
Do
- Slug the real headline rather than a shortened rewrite of it.
- Keep the hyphen, which search engines read as a word separator.
- Check for an existing slug before publishing, since stripped accents collide.
- Serve a permanent redirect whenever a published slug has to change.
Don't
- Strip punctuation before transliterating, or the eszett disappears entirely.
- Reach for the underscore, which is read as a word joiner instead.
- Expect a title written entirely in Chinese to produce a slug.
- Cap the length so tightly that the slug stops being specific.
Only need the casing changed?
A slug strips accents and punctuation as well. When the job is just kebab-case, snake_case or Title Case, the case converter splits the words and rejoins them.
Open the case converter →The same titles run through the slug generator on this page, next to what the identical pipeline produces when the transliteration step is removed and only Unicode normalisation is used. Every row where the two columns differ is a language whose letters would be silently deleted from the URL.
| Input title | Slug | Without the transliteration map |
|---|---|---|
| Cafe au lait (with an acute accent) | cafe-au-lait | cafe-au-lait |
| naive resume (with a diaeresis and two acutes) | naive-resume | naive-resume |
| Reykjavik (with an acute on the i) | reykjavik | reykjavik |
| Curacao (with a cedilla) | curacao | curacao |
| Uber grosse (with an umlaut and an eszett) | uber-grosse | uber-gro-e |
| strasse (with an eszett) | strasse | stra-e |
| Ol og aeble (Danish, stroked o and ash) | ol-og-aeble | l-og-ble |
| Kobenhavn (with a stroked o) | kobenhavn | k-benhavn |
| Aegir og Orn (ash and stroked o) | aegir-og-orn | gir-og-rn |
| Lodz (Polish, stroked l and two acutes) | lodz | odz |
| Lodz Poland | lodz-poland | odz-poland |
| Thor (Icelandic, thorn and an acute) | thor | or |
| What is 2+2, really? | what-is-2-2-really | what-is-2-2-really |
| Smith and Sons: A History (1890-1990) | smith-and-sons-a-history-1890-1990 | smith-and-sons-a-history-1890-1990 |
| C++ vs C# for Beginners | c-vs-c-for-beginners | c-vs-c-for-beginners |
| Three spaces around hello world | hello-world | hello-world |
| Slashes wrapped in slashes | slashes | slashes |
| Four Chinese characters | empty, see the note below | empty, see the note below |
Which letters can normalisation not save?
Normalisation alone is not enough, and this is the part almost every home-grown slug function gets wrong. Some letters are not accented forms of anything, they are their own letters in their own alphabets, so decomposition leaves them untouched and the punctuation strip then deletes them.
The German eszett, the Nordic o with stroke and the ash ligature, the Polish l with stroke, and the Icelandic thorn all behave this way. Without an explicit map, grosse written with an eszett slugs to gro-e, ol written with a stroked o slugs to just l, and Lodz slugs to odz. Those are silently wrong, and wrong in exactly the languages whose readers would notice.
The fix is a transliteration table applied before normalisation, mapping the eszett to ss, the stroked o to o, ash to ae, the stroked l to l, thorn to th, eth to d, and the rest of that set to their conventional Latin equivalents.
The formula, worked line by line
A slug is produced by a fixed pipeline, and the order of the steps is what makes it correct. Transliteration comes first because normalisation cannot help the letters it handles. Normalisation comes second so accents become separable. Only then is punctuation removed, because removing it earlier would take the accented letters with it.
The last two steps are what keeps the output clean. Splitting on runs of whitespace, hyphens, and underscores rather than replacing them one for one means no slug can ever contain a doubled separator or end with one, whatever punctuation soup went in. Truncation, when a maximum length is set, cuts back to the last separator so no word is left half-written.
1. transliterate eszett to ss, stroked o to o, ash to ae, stroked l to l, thorn to th
2. normalise NFD, then drop the combining marks in U+0300 to U+036F
3. strip replace anything not a letter, digit, underscore or space with a space
4. split and join split on runs of space, underscore and hyphen; join with the separator
5. truncate if over the limit, cut to the last separator so no word is broken- Input
- How to Make the Perfect Cafe au Lait at Home
- Step 1, transliterate
- nothing to map; the acute is an accent, not a letter
- Step 2, normalise
- the accented e decomposes, the accent drops, the e survives
- Steps 3 to 5
- nothing to strip, lowercase, join with hyphens
- Slug
- how-to-make-the-perfect-cafe-au-lait-at-home, 44 characters
Turn stop-word stripping on and it becomes how-make-perfect-cafe-au-lait-home. Cap the length at 30 and it becomes how-to-make-the-perfect-cafe at 28 characters, cut at a word boundary; cap it at 20 and it becomes how-to-make-the at 15.
The transliteration map is the part worth arguing for, because its absence is a real bug that a test in this repository exists to catch. NFD decomposes a letter only when the letter is defined as a base plus a mark.
The German eszett, the Nordic stroked o and ash, the Polish stroked l, the Icelandic thorn and eth, and the dotless Turkish i are none of them accented forms; they are independent letters, so decomposition returns them unchanged and the punctuation strip in step 3 then deletes them.
The result is grosse becoming gro-e, ol becoming l, Lodz becoming odz, and Thor becoming or. Mapping them explicitly to ss, o, ae, l, th, d, and i produces grosse, ol, lodz, and thor instead.
The pipeline is also idempotent by construction: feeding a finished slug back in returns the same string, because every step is already satisfied. That matters when a slug is regenerated on every save, which most content systems do.
A slug is a naming decision, not a ranking lever: keep it short, keep it readable, use hyphens rather than underscores because search engines have long treated hyphens as word separators and underscores as joiners, and once a URL is published leave it alone or serve a permanent redirect, because every link pointing at the old address is an asset you would be throwing away.
Questions people ask
Sources
Where the constants and formulas on this page come from. Each line names the figure it backs.
The NFD normalisation form the transliteration step applies before stripping combining marks.
UAX #15: Unicode Normalization Forms — Unicode Consortium
The unreserved characters permitted in a URL path without percent-encoding: letters, digits, hyphen, period, underscore and tilde.
RFC 3986 — Uniform Resource Identifier (URI): Generic Syntax — IETF
That Google reads a hyphen in a URL as a word separator and recommends hyphens over underscores.
URL Structure Best Practices for Google Search — Google Search Central
Related guides
Putting JSON in a URL: Validate, Minify, Percent-Encode, or Base64url?
A decision guide for flattening, encoding, checking, and safely transporting JSON in a query string.
August 11, 2026 · 9 min read
How Many Grams in a Cup? Every Ingredient, One Chart
One formula, fourteen densities. Why flour is 120 g, honey is 340 g, and the ingredient — not the cup — decides the number.
August 10, 2026 · 14 min read
How Are Loan Payments Calculated? Amortization, Explained
One level payment, front-loaded interest — the formula worked by hand, the two levers you control, and why the smaller payment is often the costlier loan.
July 23, 2026 · 13 min read