Regex: URL slug
A slug is the readable part of a URL, like regex-cheatsheet. The pattern allows lower-case letters and digits in runs joined by single dashes, so a slug cannot start or end with a dash or contain two in a row.
Pattern
^[a-z0-9]+(?:-[a-z0-9]+)*$ How it works
- ^
- Start of the string.
- [a-z0-9]+
- A run of lower-case letters and digits.
- (?:-[a-z0-9]+)*
- Any number of further runs, each after a single dash.
- $
- End of the string.
Matches
- hello-world
- post-2026
- a
Doesn't match
- Hello-World
- -start
- double--dash
- end-
- with_underscore
In your language
- JavaScript
/^[a-z0-9]+(?:-[a-z0-9]+)*$/A literal;
new RegExp(source, flags)builds the same from a string.- Python
re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")A raw string, so backslashes reach
reas written.re.ASCIIkeeps\dto0-9, as in JavaScript (Python matches any Unicode digit otherwise). Usere.fullmatchto test a whole string.- Java
Pattern.compile("^[a-z0-9]+(?:-[a-z0-9]+)*$")A normal string literal, so every backslash is doubled.
matcher(s).matches()tests the whole string.- Go
regexp.MustCompile(`^[a-z0-9]+(?:-[a-z0-9]+)*$`)A raw string in backticks. RE2 runs in linear time but has no lookaround and no backreferences.
- PHP
preg_match('/^[a-z0-9]+(?:-[a-z0-9]+)*$/D', $input)PCRE with
/delimiters inside a single-quoted string. Without theDmodifier,$also matches before a final newline, so it is added to patterns that end in$.- C#
new Regex(@"^[a-z0-9]+(?:-[a-z0-9]+)*$")A verbatim string: backslashes stay, a quote is doubled.
RegexOptions.ECMAScriptkeeps\dto0-9, as in JavaScript.$also matches before a final newline; to reject one, end the pattern with\zinstead.
Common mistakes
Generate, then validate
Build slugs from titles in code: lower-case, strip accents (normalize to NFD and drop the combining marks), replace every run of other characters with one dash, trim dashes from the ends. The pattern then only guards slugs typed by hand.
Titles outside Latin script
A Japanese or Russian title leaves nothing after stripping. Transliterate it, or put an id in the URL instead.
Dashes, not underscores
Google recommends hyphens rather than underscores between words in URLs, and paths are case-sensitive, so
My-Pageandmy-pageare two addresses. Keep slugs lower-case with dashes.