Blog

How to generate URL slugs

What makes a good URL slug, how to deal with accents and punctuation, and how to generate slugs in code or with the free slug generator.

February 10, 2026

You publish a post called “Café Culture in Zürich: 10 Spots You’ll Love” and the URL comes out as /blog/caf%C3%A9-culture-in-z%C3%BCrich%3A-10.... Nobody wants to share that link. A good slug turns it into /blog/cafe-culture-zurich, and here’s how to get there.

What is a URL slug

A slug is the readable part of a URL that names one page. In https://example.com/blog/how-to-generate-url-slug, the slug is how-to-generate-url-slug.

The word comes from newspapers, where a slug was the short working name for a story while it was being edited. On the web it does the same job: a short, descriptive name that people can read and that stays the same for as long as the page exists.

Text
https://example.com/posts/8f14e45f-ceea-41a6-a6ee-12345678   opaque ID
https://example.com/blog/how-to-generate-url-slug               descriptive slug

The second URL tells readers and search engines what the page is about before anyone clicks.

Why slugs matter for SEO

Let’s be honest up front: the URL is a small ranking signal, and a perfect slug won’t rescue a weak page. It still helps in a few concrete ways:

  • Relevance. A slug like /blog/url-slug-generator backs up what the page is about.
  • Clicks. Search results show the URL, or a breadcrumb built from it. A readable one reassures people they’ve found what they searched for.
  • Sharing. A link pasted into a chat or email describes itself when the slug makes sense.
  • Structure. Logical path segments such as /blog/category/post-title show how the site is organised.
  • Bare links. When another site links with the raw URL as the link text, the words in your slug are the only description.

Slug best practices

  1. Use lowercase. URL paths are case-sensitive on most servers, so /Blog/My-Post and /blog/my-post can be two different pages. Sticking to lowercase avoids duplicates and redirects.
  2. Separate words with hyphens. Google recommends hyphens over underscores. It treats a hyphen as a gap between words, but may read url_slug as a single word.
  3. Keep it short. Three to five meaningful words is a good target. Drop filler like “a”, “the” and “and” unless the meaning needs them.
  4. Use only safe characters: a-z, 0-9 and -. No spaces, punctuation or accented letters.
  5. Collapse repeated hyphens. Stripping punctuation can leave my--post. Turn it into my-post.
  6. Trim hyphens at both ends. A slug that starts or ends with a hyphen looks like something fell off.

Here’s what those rules do to some real titles. Dropping filler words and spelling out symbols, like cpp for C++, are judgement calls you make by hand:

TitleSlug
How to Generate URL Slugs (SEO-Friendly URLs)how-to-generate-url-slugs
10 Tips & Tricks for Better Web Scraping10-tips-tricks-better-web-scraping
What is JSON? A Beginner’s Guidewhat-is-json-beginners-guide
Café, Naïve, & Résumé: Handling Accentscafe-naive-resume-handling-accents
C++ vs. Rust: Performance Comparison (2026)cpp-vs-rust-performance-comparison-2026

Handling accents and special characters

Accented letters should be transliterated, which just means swapped for the closest plain ASCII letters. That beats percent-encoding them (the %C3%A9 mess from the intro) or dropping them.

Text
é -> e    café -> cafe
ñ -> n    español -> espanol
ü -> u    über -> uber
ç -> c    façade -> facade
ß -> ss   Straße -> strasse
ø -> o    Ørsted -> orsted
å -> a    Åland -> aland
ł -> l    Łódź -> lodz

Some languages have their own conventions. German often writes ü as ue (ueber), so pick one rule for your site and stick to it.

JavaScript

The trick here is Unicode normalisation. NFD form splits an accented letter into a plain letter plus a separate accent mark, and then you delete the marks:

JavaScript
function generateSlug(title) {
  return title
    .normalize("NFD") // split letters from their accents
    .replace(/[\u0300-\u036f]/g, "") // drop the accents
    .replace(/['’]/g, "") // drop apostrophes so "beginner's" stays one word
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-") // any run of other characters becomes one hyphen
    .replace(/^-+|-+$/g, ""); // trim hyphens at the ends
}

generateSlug("How to Generate URL Slugs"); // "how-to-generate-url-slugs"
generateSlug("Café Naïve Résumé"); // "cafe-naive-resume"
generateSlug("10 Tips & Tricks for Web Scraping!!!"); // "10-tips-tricks-for-web-scraping"
generateSlug("Ärger mit Ölförderung in Österreich"); // "arger-mit-olforderung-in-osterreich"
generateSlug("---Hello   World---"); // "hello-world"

NFD only helps with letters that split into a base letter and a mark. Letters such as ß, ø, ł and đ don’t, so this function quietly deletes them. If your content uses them, add a small lookup table.

Python

Python
import re
import unicodedata


def generate_slug(title: str) -> str:
    normalized = unicodedata.normalize("NFD", title)
    ascii_text = normalized.encode("ascii", "ignore").decode("ascii")
    ascii_text = ascii_text.replace("'", "")
    slug = re.sub(r"[^a-z0-9]+", "-", ascii_text.lower())
    return slug.strip("-")


print(generate_slug("How to Generate URL Slugs"))  # how-to-generate-url-slugs
print(generate_slug("Café Naïve Résumé"))  # cafe-naive-resume
print(generate_slug("What is JSON? A Beginner's Guide"))  # what-is-json-a-beginners-guide

How to use the slug generator

  1. Type or paste a title into the slug generator. The slug updates as you type.
  2. Choose a separator: Hyphen (the default), Underscore or Dot.
  3. Leave Lowercase on unless you have a reason to keep capitals.
  4. Leave Transliterate accents on to turn é, ß and ł into e, ss and l. With it off, accented letters are treated like punctuation, so Café becomes caf.
  5. Set a Max length if you need one. The slug is cut at the last separator before the limit, so you never get half a word. Leave it empty for no limit.
  6. Check the character and word counts, then Copy the slug.

Two things the generator leaves to you. It doesn’t remove filler words, and it treats apostrophes like any other punctuation, so “Beginner’s” becomes beginner-s. Edit the title first if you want beginners. It also drops letters that aren’t in its transliteration table, such as most Vietnamese vowels with two accent marks.

Slugs on popular sites

Text
github.com/facebook/react
stackoverflow.com/questions/12345/how-to-parse-json-in-javascript
dev.to/username/how-to-build-a-rest-api-with-node-js-1abc
medium.com/@user/how-to-learn-web-development-abc123def456
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
example.com/2026/02/how-to-generate-url-slugs/

Most of them use lowercase words joined by hyphens. Stack Overflow, Dev.to and Medium also add an ID or a random suffix. The ID is what actually finds the page, and the slug is there for people and search engines.

MDN breaks the rules on purpose. Its paths mirror JavaScript names such as Global_Objects/Array, so they keep the capitals and underscores.

Slug helpers in frameworks

You rarely need to write your own. Most frameworks ship a slug function, or have a popular library for it:

Ruby on Rails
"How to Generate URL Slugs".parameterize
# => "how-to-generate-url-slugs"
Django
from django.utils.text import slugify

slugify("How to Generate URL Slugs")  # "how-to-generate-url-slugs"
Laravel (PHP)
use Illuminate\Support\Str;

Str::slug("How to Generate URL Slugs"); // "how-to-generate-url-slugs"
Node.js (slugify package)
import slugify from "slugify";

slugify("How to Generate URL Slugs", { lower: true, strict: true });
// "how-to-generate-url-slugs"
Go (gosimple/slug)
import "github.com/gosimple/slug"

slug.Make("How to Generate URL Slugs") // "how-to-generate-url-slugs"

Common mistakes

  • Dates you don’t need. /blog/2026/02/10/how-to-generate-slugs makes evergreen content look old by next spring. Keep dates for news and other time-bound pages.
  • Changing a slug after publishing. Every existing link and bookmark breaks. If you really must, add a 301 (permanent) redirect from the old URL to the new one.
  • Too many filler words. “The”, “a”, “is” and “for” add length without adding meaning.
  • Putting the name in the query string. Use /products/wireless-mouse, not /products?name=wireless-mouse.
  • Long slugs. They get cut off in search results and wrap awkwardly in chat messages.
  • Percent-encoded characters. %20 or %C3%A9 are hard to read and easy to break when copied. Transliterate instead.

Keeping slugs unique

Sooner or later two posts get the same title, and so the same slug. You need a rule for that. The usual options:

Text
how-to-generate-url-slugs-2                  numeric suffix
how-to-generate-url-slugs-a1b2c3             short random suffix
/questions/12345/how-to-generate-url-slugs   ID in the path

A numeric suffix is easy to add with the function from earlier:

JavaScript
async function uniqueSlug(title, exists) {
  const base = generateSlug(title);
  let slug = base;
  let n = 1;
  while (await exists(slug)) {
    n += 1;
    slug = `${base}-${n}`;
  }
  return slug;
}

// exists() checks your database, for example:
const slug = await uniqueSlug("How to Generate URL Slugs", (s) =>
  db.posts.exists({ slug: s }),
);

Need data from a webpage as JSON?

PulpMiner turns any public page into structured JSON and can save it as a live API. See it work on a demo page, no sign-up.

Try it on a demo page