Overview and Problem Statement
A URL slug is the human-readable segment of a URL that identifies a specific page, typically placed after the domain and any category path. In https://example.com/blog/what-is-a-url-slug, the slug is what-is-a-url-slug. It replaces an opaque identifier (a database row ID, a UUID, or a content hash) with a short, descriptive string that a user, a search engine crawler, or a log-parsing script can read without dereferencing it first.
Slugs matter for three overlapping reasons: they give search engines a readable keyword signal in the URL itself, they let users predict a page's content before clicking, and they make server logs and analytics dashboards easier to scan than a column full of UUIDs. Getting the generation rules wrong, however, produces URLs that break routing, collide with existing content, or fail silently on non-ASCII input.
Core Concepts
Anatomy of a Valid Slug
RFC 3986 defines the URI syntax and designates a specific set of unreserved characters, ALPHA, DIGIT, and the four symbols - . _ ~, that never require percent-encoding inside a URI. A slug could technically use any of these, but convention narrows the set further for readability and consistency:
- Lowercase ASCII letters (
atoz) and digits (0to9) only - A single hyphen (
-) as the word separator, never an underscore or a raw space - No leading, trailing, or consecutive hyphens
- Every other character, including
.,_, and~, is either stripped or transliterated before the slug is generated, even though RFC 3986 would allow them unescaped
Restricting the character set this tightly is what keeps a slug identical whether it is typed by hand, pasted into a chat client, or displayed in a search result snippet.
Common Pitfalls and Edge Cases
Four issues account for most slug-related bugs in production systems:
- Case sensitivity. RFC 3986 treats the path component of a URI as case-sensitive (only the scheme and host are case-insensitive), so
/blog/My-Postand/blog/my-postare technically different resources. Without server-side normalization to lowercase, this produces duplicate-content issues for search engines and cache-key fragmentation at the CDN layer. - Non-ASCII input. A title like "Cafe Guide" with an accented e needs transliteration (stripping the diacritic to produce
cafe-guide) before slugification, not direct percent-encoding of the raw UTF-8 bytes. Percent-encoding an accented character still produces a technically valid URL, but one that is unreadable in a browser's address bar and inconsistent across normalization forms (NFC vs NFD). - Reserved path collisions. A slug that happens to match an existing route segment,
admin,api, ortools, can silently shadow real application routes depending on how the router resolves path precedence. Slug generation should check against a reserved-words list, not just against existing content slugs. - Uniqueness after publication. Once a slug is live, changing it breaks every external bookmark and backlink pointing at the old path unless the change ships alongside a 301 redirect from the old slug to the new one.
Slug-Only URLs vs Hybrid ID+Slug URLs
Two URL identifier strategies dominate in practice, and the choice determines how much redirect infrastructure a slug change requires.
A pure slug URL, /blog/what-is-a-url-slug, uses the slug as the sole lookup key. This keeps URLs short and fully descriptive, but the slug column now carries all the weight of the uniqueness and redirect concerns above: changing it, or generating a colliding one, directly breaks or ambiguates routing.
A hybrid ID+slug URL, the pattern used by sites like Stack Overflow (/questions/12345/some-descriptive-title), treats a numeric or short alphanumeric ID as the authoritative lookup key and the trailing slug as a cosmetic, SEO-oriented addition. The server resolves the resource from the ID alone and can safely ignore, or silently correct, a stale or mismatched slug segment. This removes the need for a redirect table entirely: an old slug embedded in a bookmarked link still resolves correctly because the ID never changes, even if the descriptive text after it no longer matches the current title.
The tradeoff is a less clean URL and a routing layer that has to parse out and validate the ID segment before touching the slug at all. For content that rarely renames itself (documentation pages, product listings), a pure slug is usually simpler to maintain. For user-generated or frequently retitled content (forum threads, ticketing systems), the hybrid pattern avoids building a full slug-history mechanism just to keep old links alive.
Slug Persistence and Redirect Strategy
For teams staying with a pure slug scheme, the redirect problem raised in the pitfalls above needs an actual implementation, not just a policy. The standard pattern is a slug history table separate from the main content table:
On a slug change, the application writes the outgoing slug into slug_history before updating the live posts.slug column. A request for an unrecognized slug then falls back to a lookup against slug_history and issues a 301 redirect to the entity's current slug, rather than returning a 404.
One edge case this table needs to guard against: once an old slug is retired, should it become available for a completely unrelated new post to claim? Reusing it breaks every existing redirect pointing at the original entity, since the old_slug value would no longer map to a single entity. The safest default is to treat every slug that has ever been live, current or historical, as permanently reserved, enforced by the same UNIQUE constraint the history table already carries above.
Slug Generation Across Frameworks
Most web frameworks provide a starting point for slug generation rather than leaving it entirely to hand-written regex:
- Django ships
django.utils.text.slugify()in the standard library, and itsSlugFieldmodel field pairs naturally with apre_savesignal or an overriddensave()method to auto-populate the slug from a title field on first save. - Ruby on Rails projects commonly add the FriendlyId gem, which replaces the default
to_parammethod (normally just the row's ID) with a slug column, and includes a built-in candidates API that automatically tries a sequence of fallback slugs on collision. - Node.js and most JavaScript meta-frameworks have no slugify function in the standard library. Teams typically pull in a small dependency (the
slugifynpm package is a common choice) and call it explicitly in a route handler, a build-time static generation function, or a database hook, since dynamic route segments are matched by exact, case-sensitive string equality by default.
Practical Comparison Table
Code Implementation
The following TypeScript function covers transliteration, character filtering, and hyphen normalization in one pass. It does not handle uniqueness, which is a database-layer concern (checking existing slugs and appending a numeric suffix on collision).
The collision-resolution step referenced above is a small wrapper around whatever lookup the datastore already exposes:
Checking slug_history in the same lookup is what keeps a retired slug from being reissued to an unrelated new post, closing the edge case raised in the persistence section above.
Conclusion & Next Steps
A valid slug is less about what RFC 3986 technically permits and more about the narrower convention that keeps URLs readable, predictable, and safe to type by hand: lowercase letters, digits, single hyphens, transliterated non-ASCII input, and a uniqueness check that survives collisions without silently overwriting existing content. Reserved-word checks, a slug history table for redirects, and a considered choice between a pure slug and a hybrid ID+slug scheme close the remaining gap between a working slugify function and a routing layer that will not break in production.
---
Test URL Encoding Behavior with Yurlie
See exactly how a raw title becomes a browser-safe URL, character by character, with our free Yurlie URL Percent Encoder and Decoder.
Yurlie Online Developer Tools
30 privacy-first utilities for developers and cloud architects
UUIDs, hashing, regex, timestamps, SQL, and networking utilities running 100% in your browser.
Explore All 30 Tools →