How to Create SEO-Friendly URL Slugs — Rules, Examples, and Code

Learn best practices for creating clean, readable URL slugs that help with SEO and user experience, including internationalization, code snippets, and CMS guidance.

The Quick Answer

A URL slug is the readable part of a URL that identifies a specific page:

https://example.com/blog/how-to-create-url-slugs
                         └─────────────────────┘
                                  slug

Good slug: how-to-create-url-slugs Bad slug: post?id=123&ref=abc

A good slug is lowercase, uses hyphens as word separators, contains only ASCII letters and numbers, and is short enough to read at a glance — typically 3 to 5 words.

Why Slugs Matter

For SEO

Search engines use URLs to understand page content. Keywords in slugs provide a lightweight ranking signal and, more importantly, improve click-through rates in search results:

// Good - tells Google what the page is about
/blog/javascript-array-methods

// Bad - no useful information
/blog/post-12345

Google highlights matching keywords in URLs in search results. A user searching for "javascript array methods" sees their query bolded in the URL, making the result more clickable.

For Users

People read and share URLs. Readable slugs build trust and set expectations:

// Would you click this?
example.com/products/blue-wireless-headphones

// Or this?
example.com/products/item?sku=BWH-001&ref=nav

Readable URLs are also easier to share verbally, in chat, and on social media. When a URL is truncated in a tweet or message preview, a descriptive slug still communicates what the page is about.

Slug Formatting Rules

1. Lowercase Everything

// Good
/blog/my-article-title

// Bad
/blog/My-Article-Title

Why: URLs are case-sensitive on many servers. My-Article and my-article can resolve to different pages, creating duplicate content issues. Consistent lowercase prevents this entirely.

2. Use Hyphens, Not Underscores

// Good
/blog/my-article-title

// Bad
/blog/my_article_title

Why: Google treats hyphens as word separators, but underscores join words. The slug blue-shoes is indexed as two words ("blue" and "shoes"), while blue_shoes is treated as one token ("blue_shoes"). This has been confirmed by Google's Matt Cutts and remains the standard.

3. Remove Special Characters

// Original: "What's the Best Way to Learn C++?"

// Good
/blog/whats-best-way-learn-cpp

// Bad
/blog/what's-the-best-way-to-learn-c++?

Special characters like ?, &, =, #, and % have reserved meanings in URLs. Including them in slugs causes encoding issues and makes URLs harder to read. Replace known abbreviations with readable text when possible (C++ → cpp).

4. Remove Stop Words (When It Helps)

Stop words: a, an, the, is, at, which, on, for, etc.

// With stop words
/blog/how-to-create-a-url-slug

// Without (more concise)
/blog/create-url-slugs

Remove stop words if the slug is too long. Keep them if removal changes meaning or hurts readability. "how-to-tie-a-tie" is clearer than "tie-tie."

5. Keep It Short

// Good (3-5 words)
/blog/javascript-array-methods

// Too long
/blog/the-complete-guide-to-all-javascript-array-methods-with-examples

Aim for 3–5 words or under 60 characters. Google truncates displayed URLs in search results beyond roughly 60 characters, and longer URLs are harder to share, copy, and remember.

Handling Accented and International Characters

One of the most common slug challenges is dealing with non-ASCII text — accented letters, characters from non-Latin scripts, and special typographic symbols.

Transliteration: Accented Characters to ASCII

Accented characters like é, ü, ñ, ø, and ß should be converted to their closest ASCII equivalents. This is called transliteration:

Input Transliterated Slug
Café Résumé Cafe Resume cafe-resume
Über uns Uber uns uber-uns
El niño El nino el-nino
Ærø Island Aero Island aero-island
Straße Strasse strasse

The standard approach uses Unicode NFKD normalization, which decomposes characters like é into e + a combining accent mark. You then strip the combining marks, leaving the base letter.

Non-Latin Scripts

For text in Cyrillic, Chinese, Japanese, Arabic, or other non-Latin scripts, you have two options:

  1. Transliterate to Latin — Convert characters to their romanized equivalents. "Москва" becomes moskva. This works well when your audience primarily uses Latin-script search engines.

  2. Use percent-encoded Unicode — Keep the original script in the URL. Modern browsers display Unicode URLs correctly, and Google indexes them. "example.com/блог/москва" appears readable in the browser address bar.

For most international sites, option 1 (transliteration) produces cleaner, more portable slugs.

Common Transliteration Mappings

Character ASCII equivalent Language
ä, ö, ü ae, oe, ue German
ß ss German
æ, ø, å ae, oe, aa Danish, Norwegian
ñ n Spanish
ç c French, Portuguese, Turkish
ş, ğ, ı s, g, i Turkish
ł, ż, ź l, z, z Polish
ð, þ d, th Icelandic

Some transliterations are language-dependent. German "ü" is conventionally transliterated as "ue," while Turkish "ü" is typically just "u." Choose mappings based on your audience's expectations.

Slugify Code in Multiple Languages

JavaScript

function slugify(text) {
  return text
    .normalize('NFKD')                // Decompose accented characters
    .replace(/[\u0300-\u036f]/g, '')   // Strip combining diacritical marks
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, '')          // Remove remaining special chars
    .replace(/\s+/g, '-')             // Spaces to hyphens
    .replace(/-+/g, '-')              // Collapse consecutive hyphens
    .replace(/^-+|-+$/g, '');         // Trim leading/trailing hyphens
}

slugify("Héllo Wörld!")    // "hello-world"
slugify("Café Résumé")     // "cafe-resume"
slugify("  Multiple   Spaces  ")  // "multiple-spaces"

The normalize('NFKD') step is the key to handling accented characters — it breaks é into e + ◌́ (combining acute accent), and the regex strips the accent.

Python

from slugify import slugify

slugify("Hello World!")        # "hello-world"
slugify("Café Résumé")         # "cafe-resume"
slugify("日本語テスト")          # "ri-ben-yu-tesuto"

Install with pip install python-slugify. The library handles Unicode transliteration, CJK characters, and edge cases automatically. For Django projects, use django.utils.text.slugify which is built in but does not transliterate non-Latin scripts by default.

PHP

function slugify(string $text, string $separator = '-'): string {
    // Transliterate
    $text = transliterator_transliterate(
        'Any-Latin; Latin-ASCII; Lower()', $text
    );
    // Remove non-alphanumeric characters
    $text = preg_replace('/[^a-z0-9\s-]/', '', $text);
    // Replace whitespace and repeated separators
    $text = preg_replace('/[\s-]+/', $separator, trim($text));
    return $text;
}

slugify("Héllo Wörld!");    // "hello-world"

This uses PHP's intl extension for robust transliteration. In Laravel, use Str::slug() which handles this automatically.

Ruby

require 'babosa'  # gem install babosa

"Héllo Wörld!".to_slug.transliterate.normalize.to_s
# => "hello-world"

In Rails, ActiveSupport provides parameterize:

"Hello World!".parameterize
# => "hello-world"

CMS and Framework Guidance

Different platforms handle slugs differently. Here is what to know for the most common ones:

WordPress

WordPress auto-generates slugs from post titles. You can edit them in the post editor under the "Permalink" section. WordPress handles basic transliteration for some languages but may need the WP Slugs or Flavor plugin for better non-Latin support.

Tips:

  • Edit the auto-generated slug before publishing — WordPress does not remove stop words by default
  • If you change a slug after publishing, WordPress does not create a redirect automatically — use a redirect plugin
  • Avoid changing slugs on high-traffic pages unless necessary

Next.js / Gatsby (Static Site Generators)

These frameworks typically derive slugs from filenames or frontmatter:

// File-based routing (Next.js App Router)
app/blog/javascript-array-methods/page.tsx
// Slug = "javascript-array-methods"

// Frontmatter-based (MDX/Gatsby)
---
slug: javascript-array-methods
---

For dynamic slug generation, use a library like slugify or github-slugger:

npm install github-slugger
import GithubSlugger from 'github-slugger'
const slugger = new GithubSlugger()
slugger.slug('Hello World!')  // 'hello-world'

Django

Django provides slugify in django.utils.text:

from django.utils.text import slugify
slugify("Hello World!")  # "hello-world"

For Unicode support, set allow_unicode=True or use python-slugify for transliteration. In models, combine with unique=True on the slug field to prevent duplicates:

class Article(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)

Hugo

Hugo auto-generates slugs from filenames. Override with frontmatter:

slug: "custom-slug-here"

Configure default slug behavior in config.toml:

[permalinks]
  posts = "/:slug/"

SEO Best Practices

Include Your Target Keyword

// Targeting "chocolate chip cookies recipe"
/recipes/chocolate-chip-cookies

// Not helpful
/recipes/recipe-47

Place the primary keyword near the start of the slug. Search engines give slightly more weight to words earlier in the URL, and truncated URLs in search results still show the keyword.

Match User Intent

// Informational
/blog/what-is-seo

// Transactional
/products/wireless-mouse

// Navigational
/about/contact

Align the slug with the search intent of your target query. Informational slugs often work well with "what-is" or "how-to" prefixes. Product slugs should describe the item, not an internal SKU.

Avoid Dates in URLs (Usually)

// Bad - becomes outdated and adds unnecessary depth
/blog/2024/01/15/seo-tips

// Better - evergreen
/blog/seo-tips

Exception: News sites where publication date is editorially important. For evergreen content, dateless URLs allow you to update the content without the URL looking stale.

Keep URLs Flat

// Too deep - hard to read, confusing hierarchy
/blog/2024/tech/programming/javascript/arrays/methods

// Better - clear and concise
/blog/javascript-array-methods

Each additional path segment implies a hierarchical relationship. Deep nesting makes URLs harder to read and gives search engines a more complex site structure to crawl. Keep paths to 2–3 segments when possible.

Keep URLs Stable

Changing URLs loses:

  • Search engine rankings built over time
  • Backlinks from other sites
  • Social media shares and bookmarks

If you must change a slug, always set up a 301 redirect from the old URL to the new one. This tells search engines to transfer link equity to the new address. Without the redirect, any authority the old URL had is lost.

Common Mistakes

1. Keyword Stuffing

// Bad - reads like spam
/seo-tips-seo-guide-seo-tutorial-seo-help

// Good - natural and descriptive
/seo-guide-for-beginners

Repeating keywords in slugs does not improve rankings — search engines recognize this as manipulation and it hurts click-through rates because the URL looks untrustworthy.

2. Using IDs Without Descriptive Text

// Bad - tells nobody anything
/products/12345

// Good - describes the product
/products/wireless-bluetooth-headphones

Numeric IDs are fine as internal identifiers but should not be the only thing in a slug. If your CMS generates ID-only URLs, override them with descriptive slugs.

3. Including File Extensions

// Unnecessary for modern web servers
/about-us.html

// Cleaner
/about-us

Modern web servers and frameworks serve pages without file extensions. Removing .html, .php, or .aspx from slugs makes URLs cleaner and prevents issues if you change server technology later.

4. Inconsistent Separator Usage

// Mixing separators - confusing
/blog/my_article-title

// Consistent
/blog/my-article-title

Pick one separator (hyphens) and use it everywhere. Mixing hyphens and underscores creates inconsistency and makes URLs harder to type correctly.

5. Not Handling Duplicate Slugs

When two pages generate the same slug (e.g., two articles titled "Best Practices"), most CMS platforms append a number: best-practices-2. This is functional but not ideal. Differentiate slugs manually:

// Instead of auto-generated duplicates
/blog/best-practices
/blog/best-practices-2

// Use distinct slugs
/blog/css-best-practices
/blog/javascript-best-practices

Slug Migration Checklist

When redesigning a site or changing URL structures, follow this checklist to avoid losing search traffic:

  1. Export all current URLs — crawl your site or export from your CMS
  2. Map old URLs to new URLs — create a spreadsheet with old slug → new slug
  3. Implement 301 redirects — server-level redirects (.htaccess, nginx.conf, or your framework's redirect rules)
  4. Test every redirect — verify with curl -I or a redirect checker tool
  5. Update internal links — search your codebase and content for old URLs
  6. Submit updated sitemap — resubmit sitemap.xml in Google Search Console
  7. Monitor 404 errors — check Google Search Console for crawl errors over the following weeks

A 301 redirect transfers approximately 90-99% of link equity to the new URL. A 302 (temporary) redirect does not transfer equity — always use 301 for permanent slug changes.

Generate Clean Slugs Instantly

Slug Generator

Convert any text — including accented characters — into a clean, URL-friendly slug. Batch mode and custom separators available.

Open Slug Generator

Frequently Asked Questions

What is a URL slug?

A URL slug is the human-readable part of a web address that identifies a specific page. In example.com/blog/how-to-bake-bread, the slug is how-to-bake-bread. Good slugs use lowercase letters, numbers, and hyphens.

Should I use hyphens or underscores in URL slugs?

Use hyphens. Google treats hyphens as word separators and underscores as word joiners. The slug blue-shoes is indexed as two words, while blue_shoes is treated as one.

How long should a URL slug be?

Aim for 3–5 words or under 60 characters. Google truncates URLs in search results around 60 characters. Shorter slugs are easier to read, share, and remember.

How do I handle accented characters in URL slugs?

Convert them to their closest ASCII equivalents through transliteration. Use Unicode NFKD normalization to decompose characters like é into e + accent mark, then strip the marks. Libraries like python-slugify, github-slugger, and PHP's transliterator_transliterate handle this automatically.

Do URL slugs affect SEO?

Yes, as a minor direct ranking factor. The bigger impact is on click-through rates — users are more likely to click a descriptive URL in search results than one with random IDs. Google also bolds matching keywords in URLs.

Can I change a slug after publishing?

You can, but it creates a new URL. Always set up a 301 redirect from the old URL to the new one. Without the redirect, you lose accumulated search rankings, backlinks, and social shares.

What happens if two pages have the same slug?

Most CMS platforms append a number (my-article-2). This works but is not ideal. Use distinct, descriptive slugs for every page to avoid confusion for both users and search engines.

Should I remove stop words from URL slugs?

Remove them if the slug is too long or they add no meaning. Keep them if removal changes the meaning — "how-to-tie-a-tie" is clearer than "tie-tie." There is no SEO penalty for including stop words.

How do I slugify text in Python?

Use python-slugify: from slugify import slugify then slugify("Hello World!") returns "hello-world". Install with pip install python-slugify. For Django, django.utils.text.slugify is built in.

Is it okay to use non-English characters in URL slugs?

Modern browsers and search engines support Unicode URLs. However, transliterated ASCII slugs are more portable — they work reliably in all contexts including email clients, social media previews, and older systems.

Related Tools

Related Tools