UseToolSuite UseToolSuite

URL Slugs: Best Practices for SEO-Friendly URLs

A practical guide to URL slugs: Unicode normalization (NFD) and transliteration, handling database slug collisions, stop-word filtering, and routing best practices.

Necmeddin Cunedioglu Necmeddin Cunedioglu 7 min read
Part of the Generative Engine Optimization (GEO): How to Get Cited by AI Search in 2026 series

Practice what you learn

URL Slug Generator

Try it free →

URL Slugs: Best Practices for SEO-Friendly URLs

A URL slug is the human-readable part of a URL that identifies a page. In https://example.com/blog/url-slug-best-practices, the slug is url-slug-best-practices.

Generating a slug looks trivial: replace spaces with dashes and call toLowerCase().

But slugs are a foundational routing decision that affects SEO, database indexing, Unicode handling, and long-term stability. Once a slug is published and indexed by Google, changing it is risky — it requires 301 redirects to avoid losing accumulated SEO authority.

This guide covers the anatomy of a good slug, Unicode normalization for international input, handling slug collisions in databases, and the regex needed to build a solid generator.

Stop writing custom regex. Paste any multilingual string into our URL Slug Generator to get an SEO-friendly, database-safe slug using transliteration dictionaries.

1. Why Slugs Matter

Search engines use keywords in the URL as a ranking signal. But the benefits go beyond keyword matching.

A. Click-through rate

On a search results page, people scan the URL to confirm the content matches. A developer is far more likely to click example.com/blog/react-hooks-advanced-guide than example.com/blog/post?id=382837&lang=en. A higher CTR signals quality to Google, which helps your ranking.

B. Shareability

Clean URLs look trustworthy in Slack, Discord, Teams, or X. URLs padded with percent-encoded characters or long hashes look like spam, which hurts sharing.

C. Information architecture

Structured slugs force you to categorize content logically, producing a hierarchy that’s easier for caching networks (like Cloudflare) to handle.

2. Five Rules for a Good Slug

https://example.com/blog/advanced-react-hooks-performance-guide
                          └──────────────── slug ───────────────┘

Rule 1: lowercase

URL paths are case-sensitive, so example.com/Blog and example.com/blog are different pages to Google. If both load the same content, you get flagged for duplicate content. Force every character to lowercase before it hits the database.

Rule 2: hyphens, not underscores

Google’s crawlers treat hyphens (-) as word separators and underscores (_) as word joiners.

  • react-hooks is parsed as two words: “react”, “hooks”.
  • react_hooks is parsed as one word: “reacthooks”. Replace underscores with hyphens.

Rule 3: strip special characters

Remove !, ?, &, @, #, %, =, and quotes. These are reserved characters under RFC 3986 with special meaning to the browser and server — # is an anchor fragment, ? starts a query string. Let them into a slug and your routing breaks.

Rule 4: filter stop words

Stop words — “a”, “the”, “is”, “in”, “to”, “and”, “for” — add no semantic value for a search engine. Removing them increases keyword density and keeps the URL short.

  • Input: The Ultimate Guide to React Hooks for Beginners in 2026
  • Slug: ultimate-guide-react-hooks-beginners-2026

Rule 5: keep it short (3–8 words)

Shorter URLs tend to rank better. A 15-word slug gets truncated in the results and dilutes your keywords. Cap slugs at about 75 characters.

3. Unicode and Internationalization

English ASCII is easy. Accepting input in Turkish, German, French, Mandarin, or Arabic brings in Unicode normalization.

Transliteration vs percent-encoding

When a URL contains non-ASCII characters (an accented letter), browsers “fix” the string with percent-encoding when it’s copied or requested.

  • Turkish input: Türkçe Başlık Örneği
  • Percent-encoded URL: t%C3%BCrk%C3%A7e-ba%C5%9Fl%C4%B1k-%C3%B6rne%C4%9Fi

That’s unreadable, looks suspicious, and loses all keyword value. The right approach is transliteration — converting non-ASCII characters to their closest ASCII equivalents before saving the slug.

LanguageInputTransliterated slug
TurkishTürkçe Başlık Örneğiturkce-baslik-ornegi
GermanWie schreibt man groß?wie-schreibt-man-gross (ß → ss)
FrenchLes meilleures façonsles-meilleures-facons (ç → c)
SpanishCómo mejorar tu SEOcomo-mejorar-tu-seo

JavaScript: Unicode NFD normalization

To handle transliteration in Node.js or the browser, use Unicode Normalization Form Canonical Decomposition (NFD).

An accented character like é can be stored as a single character (U+00E9) or as two: the base letter e (U+0065) plus a combining acute accent (U+0301).

To build a robust generator, decompose the string, strip the floating accent modifiers with a regex, then remove any remaining invalid characters.

/**
 * Generates an SEO-friendly URL slug from a raw UTF-8 string.
 * Uses NFD normalization and regex filtering.
 */
function generateSlug(text) {
  return text
    .toString()
    .toLowerCase()
    // 1. Normalize to Canonical Decomposition (NFD)
    .normalize('NFD')
    // 2. Strip the combining diacritical marks (floating accents)
    .replace(/[\u0300-\u036f]/g, '')
    // 3. Replace whitespace with hyphens
    .replace(/\s+/g, '-')
    // 4. Remove non-alphanumeric characters (except hyphens)
    .replace(/[^\w\-]+/g, '')
    // 5. Collapse multiple hyphens
    .replace(/\-\-+/g, '-')
    // 6. Trim leading and trailing hyphens
    .replace(/^-+/, '')
    .replace(/-+$/, '');
}

console.log(generateSlug("Déjà Vu: The 100% Ultimate Guide!"));
// Output: "deja-vu-the-100-ultimate-guide"

(Note: the NFD approach above doesn’t fully handle edge cases like the German ß or the Cyrillic alphabet. For true internationalization, add a dictionary mapping layer before normalization. Our URL Slug Generator does this automatically.)

4. Handling Slug Collisions in the Database

The slug is usually the lookup key in the database (SELECT * FROM articles WHERE slug = 'my-first-post' LIMIT 1;).

Because it’s used for indexing, the column needs a UNIQUE constraint. That creates a problem: what if two users create an article with the same title?

If User B writes “My First Post”, the generator produces my-first-post. When the ORM runs the INSERT, PostgreSQL or MySQL throws a unique-constraint violation, crashing the request with a 500.

Strategy 1: recursive appending (the standard)

Before saving, query whether the slug exists. If it does, append -1 and check again, recursing until you find an open slot.

// Recursive appending
let baseSlug = generateSlug(userTitle);
let currentSlug = baseSlug;
let collisionCounter = 1;

// Query until a unique slot is found
while (await db.article.exists({ slug: currentSlug })) {
    currentSlug = `${baseSlug}-${collisionCounter}`;
    collisionCounter++;
}

// If 'my-post', 'my-post-1', 'my-post-2' exist, this saves 'my-post-3'
await db.article.create({ slug: currentSlug, ...data });

The downside: it needs multiple sequential queries, which is an I/O bottleneck for high-volume publishing.

Strategy 2: a hash suffix (high-volume)

Instead of checking the database, platforms like Medium, Dev.to, and Reddit append a short random alphanumeric hash to every slug at creation.

  • User A: example.com/blog/my-first-post-a7x9bq2
  • User B: example.com/blog/my-first-post-m4v1p0z

This guarantees uniqueness instantly, eliminating the pre-save collision check and maximizing write throughput.

5. Frameworks and CMSes

If you’re using a framework rather than a custom backend, configure routing to handle slugs correctly.

WordPress

WordPress generates slugs from post titles.

  1. Go to Settings → Permalinks.
  2. Select Post name (/%postname%/).
  3. Don’t use the legacy “Plain” setting (?p=123), which is bad for SEO, and avoid “Day and name” (which injects dates).
  4. Edit the auto-generated slug before publishing to strip stop words and shorten it.

Next.js and Astro (file-based routing)

In static site generators with file-based routing, the filename literally becomes the slug.

❌ Avoid: src/content/blog/2024-01-15-react-post.md
(Generates: /blog/2024-01-15-react-post)

✅ Better: src/content/blog/react-hooks-guide.md
(Generates: /blog/react-hooks-guide)

Don’t put dates in filenames unless you’re running a time-sensitive news site. Dated URLs make evergreen content look stale and prevent you from updating and republishing without breaking the URL.

6. Three Dangerous Routing Mistakes

1. Changing slugs without a 301 redirect

If you change a slug after Google indexed the page or users bookmarked it, every existing link returns 404, and you lose the SEO authority that page built up. If you must change a slug, add a 301 Moved Permanently redirect from the old URL to the new one at the server level (Nginx, Apache .htaccess, or next.config.js).

2. Parameter-based routing for static content

Don’t use query parameters as the primary identifier for static pages.

  • example.com/store?category=shoes&item=air-max-90
  • example.com/store/shoes/air-max-90 Crawlers handle heavily parameterized URLs poorly, and they look suspicious to users.

3. Keyword stuffing

Don’t cram every keyword variation into the URL.

  • example.com/blog/best-react-hooks-tutorial-react-hooks-guide-2026-free-download
  • example.com/blog/react-hooks-guide Google’s BERT models detect keyword stuffing and penalize it. Keep URLs concise and readable.

7. FAQ and Edge Cases

Q: Should I include the database row ID in the slug? A: Yes, if you generate thousands of posts a day. StackOverflow uses example.com/questions/12345/how-to-center-a-div — the 12345 is the primary key, and the backend fetches by ID, ignoring the text. This keeps queries fast while still feeding keywords to Google.

Q: What about emojis? A: Strip them during regex filtering. An emoji in a URL gets percent-encoded into a long string (🚀 becomes %F0%9F%9A%80) and adds no SEO value.

Q: Does capitalization impact SEO? A: Not directly, but it affects stability. Linux servers (Nginx/Apache) are case-sensitive — if a user types /Blog but the directory is /blog, the server returns 404. Forcing lowercase prevents that.

Q: Are subfolders better than a flat structure? A: For large sites, yes. /electronics/laptops/macbook-pro beats a flat /electronics-laptops-macbook-pro — subfolders help Google understand your hierarchy and let you track analytics by directory.

Q: Can I use slashes inside a slug? A: No. A forward slash (/) is the directory delimiter. A stray slash makes the server look for a non-existent subdirectory and return a 404.

Further Reading


Stop fighting regex dictionaries and Unicode normalization by hand. Build clean routing with our URL Slug Generator, and make your links share well with our Meta Tag Generator.

Necmeddin Cunedioglu
Necmeddin Cunedioglu Author
7 min read
-- views

Software developer and the creator of UseToolSuite. I write about the tools and techniques I use daily as a developer — practical guides based on real experience, not theory.