Paste titles, get URL slugs: one per line, transliterated properly (including the characters naive slugify functions drop), with duplicates in the batch flagged before they become duplicate URLs. Below: the hyphen question settled, the umlaut conventions, and what actually matters about slug length.
What makes a good slug
The slug is the URL's human part: /blog/uuid-v4-vs-v7/. It shows up in search results, link previews, browser history and every pasted link, so it is marketing surface and interface at once. A good one is short, lowercase, hyphen-separated, made of the words someone would search for, and stable for the page's lifetime. Google's URL structure guidelines say much the same: readable words rather than IDs, hyphens rather than underscores, and nothing exotic that trips crawlers or humans.
Everything else in a URL is machinery; the slug is the part you choose. Which is why generating it deserves thirty seconds of care rather than whatever the CMS autogenerated from a working title that later changed.
How to use this generator
Type or paste titles, one per line; the slugs appear live, one per line, aligned with the input. Batch use is the point: paste a content plan's fifty headlines and read the result as a URL scheme, not fifty isolated strings. The DUPLICATES stat turns red when two lines collapse into the same slug, which is precisely the thing you want to catch at planning time, and lines that transliterate to nothing (a fully non-Latin title, say) are named instead of silently dropped.
The defaults produce the boring, correct slug: lowercase, hyphens, ampersands written out as "and", apostrophes collapsed so What's becomes whats rather than what-s. Flags cover the deliberate deviations: --underscores for file-name conventions, --keep-case if your platform is case-sensitive on purpose, --drop-stopwords for shorter slugs, --german-umlauts for the ae/oe/ue convention, and max chars cuts at a word boundary.
Hyphens vs underscores, settled
Use hyphens in URLs. The reason is mechanical, not mystical: search engines treat a hyphen as a word separator, so blue-widget is the words "blue" and "widget", while an underscore historically joins its neighbours into a single token. Programmers reach for underscores out of code habit, and in code they remain right; a URL is prose, not an identifier.
The same logic ranks the other separator candidates: dots imply file extensions, plus signs and %20 are what you get when spaces leak through unplanned, and camelCase dies the moment someone lowercases the URL. Hyphen, then, everywhere a human might read the address.
Umlauts, ø and the NFKD trap
Most slugify implementations do one thing: Unicode-decompose the string (NFKD) and strip the combining marks, which turns é into e and ü into u. The trap is that a whole class of European letters is not a base letter plus mark: ø, ł, đ, ß, æ, œ, þ have no decomposition, so the naive pipeline silently deletes them, and Łódź becomes odz. This engine maps them explicitly (ø→o, ł→l, ß→ss, æ→ae and friends), which is the difference you can test right in the sample.
| Input | naive NFKD strip | this tool | with --german-umlauts |
|---|---|---|---|
| Größe | groe | grosse | groesse |
| Łódź café | odz-cafe | lodz-cafe | lodz-cafe |
| Ørsted & Søn | rsted-sn | orsted-and-son | orsted-and-son |
For German content, decide once between the stripped form (größe → grosse) and the German convention (groesse); both are common, mixing them on one site creates near-duplicate URLs that are miserable to untangle later.
Length, stop words, casing
Slug length has no meaningful ranking effect; it has a strong usability effect. Three to six content-bearing words cover almost every page, and the max-chars field exists for platforms with hard limits or teams that want a uniform cap; the cut lands at a word boundary because half-words look broken in every context a URL appears.
Lowercase is the default because URL paths are case-sensitive per the spec and inconsistently case-sensitive in practice, a combination that produces either 404s or duplicate URLs and nothing good. Stop words are taste: dropping them shortens, keeping them reads better. The one hard rule in this section is uniqueness, which is why the duplicate detection sits in the stats and not in a footnote.
Changing a slug later
A slug is a promise: every link, bookmark and search entry depends on it. When it must change, the move is a permanent 301 redirect from the old URL to the new one, plus updating internal links and the sitemap so nothing keeps hopping through the redirect. Done that way, renames are routine; done without, they are self-inflicted 404 campaigns. When to use 301 versus 302 and how caches treat them is its own topic, covered in our redirects guide.
Slugify in code
| Environment | Approach |
|---|---|
| JavaScript | s.normalize('NFKD').replace(/\p{M}+/gu, '').replace(/[^a-zA-Z0-9]+/g, '-')… plus explicit ø/ł/ß mapping |
| Python | python-slugify: full transliteration incl. ß→ss, options for stop words and max length |
| Ruby on Rails | "title".parameterize, with separator: option |
| PHP / WordPress | sanitize_title(), which percent-encodes umlauts rather than transliterating |
Whatever you pick, run the umlaut and ø test cases from the table above through it once. Half the slugify functions in production fail at least one of them, and the failure is silent: the slug is merely uglier or emptier than intended, and nobody looks at it again until it is indexed.
Slug questions
Should I use hyphens or underscores in URLs?
Hyphens. Google's documentation on URL structure has recommended them for years for one concrete reason: hyphens are treated as word separators, while words joined by underscores can be read as one token, so blue_widget may match differently than blue-widget. It is not a ranking bonus, it is about the words in your URL being recognised as words. Underscores remain the right choice in the places URLs are not: file names, database identifiers, code. That is exactly why this tool defaults to hyphens and puts underscores behind a flag.
Are URLs case-sensitive?
The scheme and host are not (EXAMPLE.com and example.com are the same site), but the path, query and fragment are case-sensitive by the URI standard (RFC 3986); whether /About and /about serve the same page depends entirely on the server. That asymmetry is the problem: if both happen to work, you have duplicate URLs splitting links and confusing caches, and if only one works, every wrongly-cased link 404s. All-lowercase slugs sidestep the whole class of trouble, which is why lowercasing is this tool's default and --keep-case is the exception you opt into.
How long should a URL slug be?
There is no ranking penalty for long slugs, so the limits that matter are human: search results, chat apps and address bars truncate long URLs visually, and nobody retypes a 90-character slug from a screenshot. Three to six meaningful words is the practical sweet spot; keep the keyword phrase, drop the filler. Hard technical limits sit far away (browsers handle URLs of 2000+ characters). The max-chars field here cuts at a word boundary, because a slug ending mid-wor looks broken everywhere it appears.
Should I remove stop words from URL slugs?
Optional, and less important than the internet believes. Search engines handle both fine; dropping a, the and of makes slugs shorter and keyword-denser, keeping them makes slugs read more naturally. Drop them when the title is long, keep them when they carry meaning: how-to-say-no loses its point as how-say. This is why stop-word removal is a flag here rather than a default, and if removing them would leave nothing, the tool keeps the words rather than emit an empty slug.
How are German umlauts converted in URLs, ä to a or ae?
Both conventions exist, and consistency matters more than the pick. The German convention writes ä ö ü ß as ae oe ue ss, matching how Germans type them on ASCII keyboards and keeping words readable (maerchen, not marchen); most German sites and DIN 5007-style sorting follow it. A plain diacritic strip produces a o u, which is what generic slugify libraries do and what mixed-language sites often standardise on. This tool does the plain strip by default and the German convention behind --german-umlauts; whichever you choose, use it site-wide, because groesse.html and grosse.html are different URLs.
Can URLs contain Unicode characters like Chinese, Arabic or emoji?
Yes: modern browsers display Unicode paths fine, and on the wire they travel percent-encoded UTF-8 per the standards. For a site whose audience searches in that language, native-script slugs are legitimate and Google indexes them. The costs are practical: the percent-encoded form (%E6%97%A5%E6%9C%AC) is what gets copied into plain-text contexts, some legacy systems mangle it, and mixed-script URLs raise homograph-spoofing flags. For Latin-alphabet sites, transliterating to a-z0-9 remains the boring, robust choice; for others, go native and test your stack end to end.
Does changing a URL slug hurt SEO?
Changing a URL without a redirect throws away the page's accumulated signals and 404s every existing link; with a permanent 301 redirect from old to new, the transition is routine and any fluctuation is temporary. The checklist: 301 (not 302) from the exact old URL, update internal links so nothing hops through the redirect, update the sitemap and canonical, and never reuse the old slug for something else. The 301-vs-302 distinction and how long to keep redirects alive is covered in our redirects guide.
How do I create a slug in JavaScript or Python?
JavaScript, dependency-free: title.normalize("NFKD").replace(/\p{M}+/gu, "").replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase(). That handles é and ü but silently drops ø, ł and ß, which is the usual gap; map those explicitly (this page's engine does) or use a library. Python: python-slugify (from slugify import slugify) handles the full transliteration including ß to ss. Rails has "title".parameterize built in, and static site generators like Hugo and Astro slugify file names with the same NFKD-based logic.