Skip to content

How to generate URL friendly title

Need to turn article titles into clean, SEO-friendly URLs? Use a slug generator that cleans accents, strips invalid characters, and replaces spaces with hyphens. This builds URLs like example.com/how-to-generate-url-friendly-title readable, safe for browsers, and better for SEO. Below you’ll find a modern PHP function plus examples and WordPress alternatives.

The problem with titles of your articles is that they are not URL friendly. They can contain spaces, UTF-8 characters and other stuff that will break the functionality of your web site.

And you want the title of the article to be in your URL because it is so much SEO and human friendlier. As I often develop sites in this way, here is a small but handy function that you can include in your code and call whenever you need to generate URL friendly text.

As I am often dealing with files, this function has an optional parameter called $dot which tells us whether to leave the dot inside the text (files needs that the dot is not removed) or not.

How to Generate a URL-Friendly Title (Slug)

Why You Need Slugs

Better compatibility — Avoid issues caused by spaces, special characters, or Unicode in URLs.

Improved SEO & readability — URLs like /this-is-a-page are cleaner and more memorable.

<?php
/**
 * Generate a URL-friendly slug from a string.
 *
 * @param string $text      The input text (e.g., title or filename).
 * @param string $sep       Separator for spaces (default: '-').
 * @param bool   $allowDot  Allow dot '.' in output (e.g., for filenames).
 * @return string           Clean slug (or 'n-a' for empty result).
 */
function generateSlug(string $text, string $sep = '-', bool $allowDot = false): string {
    // 1. Transliterate unicode characters (e.g., Č → C)
    if (class_exists('Transliterator')) {
        $trans = Transliterator::create('Any-Latin; Latin-ASCII; NFC');
        if ($trans) {
            $text = $trans->transliterate($text);
        }
    } elseif (function_exists('iconv')) {
        $text = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $text);
    }

    // 2. Lowercase and remove apostrophes
    $text = strtolower($text);
    $text = preg_replace("~[‘’`']+~", '', $text);

    // 3. Keep only allowed chars (letters, numbers, sep, optionally dot)
    $pattern = $allowDot ? "~[^a-z0-9\-\._ ]+~" : "~[^a-z0-9\-\_ ]+~";
    $text    = preg_replace($pattern, ' ', $text);

    // 4. Collapse multiple separators/spaces into one
    $text = preg_replace('~[ _\-]+~', $sep, $text);

    // 5. Trim leading/trailing separators or dots
    $trimChars = preg_quote($sep . ($allowDot ? '.' : ''), '~');
    $text      = preg_replace("~^[" . $trimChars . "]+|[" . $trimChars . "]+$~", '', $text);

    return $text !== '' ? $text : 'n-a';
}

Basically, this function has 3 parameters: first is the text you want converted into URL friendly text, second is optional and tells the function which character should be used instead of spaces and the third is whether to leave the dots or remove them from text.

Function will first convert the input text into ASCII/TRANSLIT one using iconv function. This means that when a character can’t be represented in the target charset, it can be approximated through one or several similarly looking characters. For example, Croatian language has a letter Č which can not be represented in ASCII and it will be converted to C.

After that function is removing anything that is not alphanumerical from the string. Then, we removed extra (double) spaces, convert the string to lower case and filling the spaces with character in $space parameter (the default is -).

So if your title is:
How to generate URL friendly title
The result after this function will be:
how-to-generate-url-friendly-title

More examples:

generate_slug('My.File.Name.jpg', '-', true);             // my.file.name.jpg
generate_slug('Too   many---separators___here!!');        // too-many-separators-here
generate_slug('Title with dots', '_', true, 12);          // title_with.d

Usage Examples:

  • generateSlug("How to Generate URL Friendly Title")how-to-generate-url-friendly-title
  • generateSlug("Čudna riječ", "-", false)cudna-rijec
  • generateSlug("file.name.jpg", ".", true)file.name.jpg

WordPress Built-in Alternative

If you’re in WordPress land, skip custom code—use the native function:

$slug = sanitize_title_with_dashes($title);

This performs the same operations (lowercases, strips invalid chars, removes duplicate dashes), optimized for WP’s ecosystem.

JavaScript: Slugify Strings on the Client or in Node.js

When you need to generate URL-friendly slugs directly in the browser or in a Node.js app, JavaScript gives you plenty of flexibility. A common use case is dynamically creating slugs as a user types a blog post title, so you can show them a preview of the final permalink.

The function below uses Unicode normalization to strip accents (so "Čudna riječ" becomes "cudna rijec"), converts everything to lowercase, removes unsafe characters, and replaces spaces with hyphens. It also supports options like custom separators, allowing dots in filenames, and enforcing a maximum length.

With this approach you can safely generate clean slugs without depending on a backend. This is especially useful for static site generators, SPAs, or quick previews before sending data to an API.

/**
 * slugify("Čudna riječ: Hello World!") -> "cudna-rijec-hello-world"
 */
function slugify(input, {
  separator = "-",
  allowDot = false,
  maxLength = 120
} = {}) {
  if (typeof input !== "string") input = String(input ?? "");

  // 1) Normalize & strip accents (NFD splits letters + diacritics)
  let s = input.normalize("NFD").replace(/[\u0300-\u036f]/g, "");

  // 2) Lowercase
  s = s.toLowerCase();

  // 3) Remove apostrophes/backticks
  s = s.replace(/['’`]+/g, "");

  // 4) Replace disallowed chars with space
  const allowed = allowDot ? /[^a-z0-9._ -]+/g : /[^a-z0-9_ -]+/g;
  s = s.replace(allowed, " ");

  // 5) Collapse whitespace/underscore/dash to single separator
  const sepClass = separator.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
  s = s.replace(/[ _-]+/g, separator);

  // 6) Trim separators (and dots if allowed) from ends
  const trim = allowDot ? new RegExp(`^[${sepClass}.]+|[${sepClass}.]+$`, "g")
                        : new RegExp(`^[${sepClass}]+|[${sepClass}]+$`, "g");
  s = s.replace(trim, "");

  // 7) Truncate without ending on separator/dot
  if (maxLength > 0 && s.length > maxLength) {
    s = s.slice(0, maxLength).replace(new RegExp(`[${sepClass}.]+$`), "");
  }

  return s || "n-a";
}

// Examples
// slugify("Čudna riječ: Hello, World!")           -> "cudna-rijec-hello-world"
// slugify("file.name.jpg", { allowDot: true })    -> "file.name.jpg"
// slugify("Too   many---separators___here!!")     -> "too-many-separators-here"

Laravel: Generate SEO-Friendly Slugs with Str::slug

Laravel includes a built-in helper that makes slug generation simple: Str::slug(). It automatically handles transliteration, lowercasing, and separator replacement, so most of the time it’s all you need. For example, Str::slug('Čudna riječ') will give you cudna-rijec.

If you need more control—like allowing dots for filenames, trimming trailing separators, or enforcing a maximum length—you can extend it with a small helper function. By combining Str::ascii() with regular expressions, you can build a robust slug generator tailored to your project’s needs.

This is ideal for blog platforms, CMS systems, and e-commerce sites where slugs need to be consistent, safe, and predictable. Since Laravel takes care of localization and multibyte strings, you get reliable slugs across many languages without reinventing the wheel.

Easiest (built-in)

Laravel already ships a great slugger:

use Illuminate\Support\Str;

$slug = Str::slug($title, '-'); // transliteration + cleanup

Helper that also supports dots and length:

<?php
use Illuminate\Support\Str;

if (! function_exists('slug_with_options')) {
    function slug_with_options(
        string $text,
        string $separator = '-',
        bool   $allowDot  = false,
        int    $maxLength = 120
    ): string {
        // Transliterate to ASCII first
        $s = Str::ascii($text);

        // Lowercase & remove apostrophes/backticks
        $s = Str::of($s)->lower()->replaceMatches("/[\'’`]+/u", '');

        // Keep only allowed chars; replace others with space
        $pattern = $allowDot ? '/[^a-z0-9\.\-_ ]+/u' : '/[^a-z0-9\-_ ]+/u';
        $s = preg_replace($pattern, ' ', (string)$s);

        // Collapse whitespace/underscore/dash to one separator
        $s = preg_replace('/[ _\-]+/u', $separator, $s);

        // Trim separators (and dot if allowed)
        $trim = preg_quote($separator, '/');
        $s = preg_replace($allowDot ? "/^[{$trim}.]+|[{$trim}.]+$/" : "/^[{$trim}]+|[{$trim}]+$/", '', $

Common Pitfalls to Avoid

  • Deprecated encodings: Avoid directly using UTF-8 characters in URLs.
  • Unchecked characters: Always strip special symbols (?, /, etc.) which can break URLs.
  • Trailing separators: Clean up hyphens at the end or beginning.

Key Takeaways

  • Clean URLs matter — readable, SEO-friendly slugs improve both search rankings and user trust.
  • Normalize and transliterate — strip accents (Č → C) to ensure cross-browser and cross-language compatibility.
  • Use safe characters only — limit slugs to lowercase letters, numbers, hyphens (and dots if needed).
  • Consistency is key — enforce a single separator and trim extra symbols for predictable results.
  • Language support — PHP’s iconv/Transliterator, JS’s normalize(), and Laravel’s Str::slug() make slugs work across locales.
  • Customizable — add options for max length, allowed dots, or alternate separators depending on your project.
  • Framework shortcuts exist — in Laravel, Str::slug() covers 90% of use cases without extra code.

Tags:

4 thoughts on “How to generate URL friendly title”

  1. There is a thing I believe is missing: such a function should also either remove or substitute those characters, like the question mark or slash, that have a reserved meaning inside an URL.

    1. There is this line:

      $string = preg_replace("/[^a-zA-Z0-9 -]/", "", $string);

      It removes all non-alphanumeric characters from string

  2. Hello there – me again. You’re obviously right about that (and I shouldn’t post comments before my first coffe).

    I was playing with that snippet tonight and found something odd in your /s+/ regex: it’s converting all ‘s’ characters into spaces.

    I believe you meant to have /\s+/ there – converting multiple spaces into one?

    1. Yes, you are right. It was the problem with WP syntax plugin which removed a backslash. It is fixed now. Thanks for noticing.

Comments are closed.