rias/statamic-redirect
Manage redirects inside Statamic's Control Panel. Create 301/302 rules, use wildcards for dynamic URLs, and send old or changed paths to the right destination without server config. A lightweight addon for smoother site migrations and SEO-friendly link updates.
composer require rias/statamic-redirect
php artisan vendor:publish --tag=statamic-redirect-config # if the tag exists
php artisan migrate # only if the addon ships migrations
Statamic addons auto-register via package discovery, so there is no config/app.php step. After install, clear caches (php artisan config:clear && php artisan cache:clear) and, if the addon stores data in the Stache, php artisan statamic:stache:clear.
Where to look first. With 29 stars and zero dependents, this is a small, single-maintainer package — budget 20 minutes to read the source before trusting it in production. In order:
vendor/rias/statamic-redirect/src/ServiceProvider.php — this tells you everything: whether redirects are stored in a flat file, a Stache index, or a database table; whether middleware is pushed onto the web group or registered via Statamic's own middleware stack; whether there's a CP nav item or a utility route.vendor/rias/statamic-redirect/config/ — the published config, if any, defines the resolution strategy (exact match vs. regex, status code default, middleware priority).vendor/rias/statamic-redirect/src/Http/Middleware/ — the actual matching logic. Read the matching loop; that's where every surprise lives.First use case. Take a single legacy URL from your 404 log, create the redirect in the CP (or the config file, whichever the addon uses), then verify from the outside rather than from the browser:
curl -sI https://yoursite.test/old-page | grep -iE '^(HTTP|location)'
Confirm both the status code and the Location header. Browser caching will lie to you about 301s within seconds of the first hit; curl will not.
CP-managed redirects as the default path. The addon's value is that content editors can fix broken URLs without a deploy. Establish that as the workflow: 404 comes in via the client or the log, an editor adds the redirect, done. Reserve config-file or code-level redirects for things that must exist before the CP is reachable (e.g. redirecting the old domain root during a migration).
Bulk imports. For a domain migration with hundreds of pairs, don't click through the CP. Either write a one-off Artisan command that inserts rows via the addon's model/repository, or seed from a CSV in database/seeders. Either way, follow with a cache/Stache rebuild — the addon may read from an index that doesn't know about your inserts yet.
Middleware ordering. Statamic resolves routes late; a redirect middleware has to run before that resolution or the 404 has already been decided. If the addon registers itself, verify the position in app/Http/Kernel.php ($middlewareGroups['web']) or wherever Statamic 5 keeps its stack. If it doesn't register itself, add it manually:
protected $middlewareGroups = [
'web' => [
\Rias\StatamicRedirect\Middleware\HandleRedirects::class,
// ... rest
],
];
Put it after session/cookie middleware if you want to flash a "you were redirected" message; before it if you want the redirect to be as cheap as possible on a cold cache.
Wildcards and capture groups. Any redirect system worth using supports /blog/{slug} → /articles/{slug}. Test the substitution explicitly — many small addons implement wildcards as a naive str_replace and silently drop the captured segment. Write two or three pairs covering a single-segment, a multi-segment, and a trailing-slash case, and assert on the resulting Location string, not just on assertRedirect().
Wrap redirect logic in tests. Because the package is small, upgrading Statamic is the realistic breakage vector. A tests/Feature/RedirectTest.php with a dozen $this->get('/old')->assertRedirect('/new') cases is the cheapest insurance you'll buy:
it('redirects legacy blog urls', function () {
$this->get('/blog/hello-world')
->assertStatus(301)
->assertRedirect('/articles/hello-world');
});
Pair it with a 404 log. If the addon doesn't ship one, add a listener on Statamic's 404 handling that writes to a missing_urls table (hits, last seen, referrer). The redirect manager becomes genuinely useful when it has a queue of real broken URLs feeding it, rather than waiting for someone to notice.
Deployment. If redirects live in a file or Stache index, your deploy pipeline must ship that state or rebuild it. Add php artisan statamic:stache:warm (or the addon's equivalent) to the post-deploy hook, otherwise a fresh server serves 404s for URLs that exist in your repo.
Query strings and fragments. Most implementations match on path only. Decide deliberately whether /search?q=x should preserve q on the destination — usually it should, and usually it doesn't by default. Fragments (#section) are almost never forwarded and are invisible to the server anyway.
Trailing slashes and case. /About and /about/ are distinct strings to a naive matcher and identical to your users and to Google. Normalize before comparing — lowercase the path and collapse duplicate slashes in your own middleware if the addon doesn't. Test both variants for every redirect you ship.
Redirect loops and chains. A→B plus B→A is an infinite loop that manifests as ERR_TOO_MANY_REDIRECTS in the browser and burns a request per hop server-side. Chains (A→B→C) work but cost latency and leak SEO signal. Add a periodic sanity check — a simple command that walks every redirect's destination against the redirect table and flags matches — before your editor creates their two-hundredth entry.
301 is cached by the browser, hard. Chrome will keep serving a 301 long after you've deleted it. During development, create redirects as 302, verify, then switch to 301. When debugging a "deleted redirect still firing" report from a client, assume browser cache first, then CDN, then the app.
CDN and full-page cache will bypass you entirely. If a static page cache or Cloudflare serves /old-page as a 200, the middleware never runs. Redirect rules must be applied at the edge too, or the cache purged for affected paths on redirect creation. This is the number one reason "the redirect doesn't work in production but works locally."
Don't redirect CP or internal routes. Guard the middleware so it skips /cp, /!/cp, /api, and asset paths. A regex redirect like /.* → / will otherwise lock you out of the control panel; keep an escape hatch in the config (an excluded-paths array) and know how to disable the addon via .env or by commenting the middleware.
Storage backend determines consistency. If the addon uses the Stache, redirects are file-backed and eventually consistent — created in the CP, they may take a request or a warmup to appear. If it uses the database, they're immediate but you now have a migration to run on every environment including production and any ephemeral preview containers.
Multisite. A redirect on site-a.test should not fire on site-b.test unless you intend it to. Check whether the addon scopes entries by site; if it doesn't, either scope it yourself with a custom query or accept that all sites share one global table (fine for many setups, wrong for regionalized domains).
Extension points. Look for events the addon dispatches on match/miss — hooking RedirectHit gives you free analytics on which legacy URLs still get traffic and are therefore safe to keep (or safe to retire). If no events exist, wrap the middleware rather than forking the package; a subclass that logs then calls parent::handle() survives upgrades.
Forking is cheap here. At this size the whole package is a few hundred lines. If you hit a wall — say you need regex capture groups and it only does exact matches — copying the middleware into app/Http/Middleware and dropping the dependency is a legitimate, low-risk move. Weigh that against the maintenance cost of tracking Statamic's middleware changes yourself.
How can I help you explore Laravel packages today?