Skip to main content

Polyslug for Laravel

Polyslug gives Eloquent models pretty URLs that are safe to expose, safe to rename, and correct across languages, with leak-free ids, self-healing canonical redirects and hreflang built in.

/blog/laravel-routing-explained_aB3xK
└──────── slug ─────────┘ └id─┘
changeable, localized, SEO-friendly stable, opaque, resolves the model

A slug should be free to change. A URL should never break. Those two goals usually fight each other. Polyslug settles the fight by splitting a URL into two independent parts — a human-readable slug (pretty, per-locale, editable) and a stable opaque identity (an encoded token that resolves the model). Rename the slug all you like: the identity still resolves, and old URLs redirect themselves to the new canonical one.

Why Polyslug

  • Leak-safe ids by default. URLs carry an encoded token, not /pages/1523. No exposed row counts, no enumerable primary keys. The default encoder issues a random, unguessable token per row, and it's the only shipped encoder that makes that claim true end to end. The choice is still yours: Sqids, UUID, ULID, the raw key, or your own. See Identity encoders.
  • Self-healing URLs. Rename freely. A stale slug on a GET/HEAD request is 301-redirected to the current canonical URL automatically: no redirect tables to hand-maintain, no dead links, no lost link equity. See Self-healing routes.
  • Multilingual with hreflang out of the box. One slug per locale, and a reciprocal hreflang set (plus x-default) generated from the same resolver that builds your canonical URL, so they can never drift apart. See Multilingual slugs and hreflang.
  • Polymorphic routing. Serve every content type — pages, articles, products — through a single {type}/{polyslug} route and one registry. See Polymorphic routing.
  • Stable resolution. Route-model binding decodes the identity, not the slug, so a mistyped or outdated slug still finds the right model (then redirects). An unknown or malformed token is a clean 404, never a fuzzy match. See How it works.
  • Scoped uniqueness. Uniqueness can be scoped per tenant, per locale, per category: whatever columns you name. See Uniqueness and scope.
  • History, events and immutability. Superseded slugs are kept so old URLs keep resolving; a SlugChanged event fires on every change; slugs can be frozen. See History, events and immutability.
  • Proven on the databases you deploy to. The one-current-slug guarantee ships as a functional partial unique index on PostgreSQL and SQLite and as generated key columns on MySQL. Those are two genuinely different mechanisms, so the suite proves both against real PostgreSQL 18 and MySQL 8.4 servers rather than inferring one from the other.

Start here

  • Installation — requirements, composer require, and the migration.
  • Quick start — a sluggable model and a self-healing route in two code blocks.
  • How it works — the two-part route key, why resolution is by id, and what makes a write concurrency-safe.

The documentation

  • Features — one page per capability, from making a model sluggable to nested paths, sitemaps and short links. Read in order for a full tour, or jump to what you need.
  • Recipes — twelve app shapes wired end to end: multi-tenant SaaS, news, e-commerce, knowledge base, marketplace, headless CMS, government, ticketing, and more. Each one combines features documented under Features.
  • Reference — the flat lists: every config key, every #[Polyslug] option, the model API, the Artisan commands, the events, and the contracts you can bind.
  • Guides — testing your own suite against Polyslug, diagnosing a setup, and what to do when something doesn't behave.

Source and support

The package is published as pushery/polyslug-for-laravel. Issues and contributions go through the repository; vulnerabilities go through its security policy rather than a public issue.