Sharing this in case it is of value to somebody else, but also because I am curious did I overlook something – almost feels a bit too simple to be true? Pointers to possible issues and improvement suggestions are of course most welcome.
I was looking for the most simple solution to intercept 404 errors and redirect some of them to alternative URLs instead.
My website is almost 17 years old and has accrued dozens of odd forwarding rules as its blog’s URL structure has changed over time. Of course the Retour and kirby3-redirects plugins provide rich features and are configurable through the panel, yet I was looking for a minimalist hardcoded way to catch “Not found” errors and check if a rule exists where that content lives today.
Here’s the shortest I could come up with (still wondering whether the preg_match
and preg_replace
couldn’t be somehow merged into one):
// in /site/config/config.php
return [
'hooks' => [
'route:after' => function ($path, $result) {
if($result === null) {
$redirects = json_decode(F::read(__DIR__ . '/redirects.json') ?? []);
foreach ($redirects as $redirect) {
if (preg_match('#' . $redirect[0] . '#', $path, $matches)) {
$target = preg_replace('#' . $redirect[0] . '#', $redirect[1], $path);
die(Response::redirect($target, $redirect[2] ?? 302));
}
}
}
},
]
]
I decided to outsource the redirect config into a separate file, as it really is a long list by now (way too long to configure as rewrite rules in my .htaccess
; also, this way it plays nicely with both Apache and Nginx with one single source of truth).
// as /site/config/redirects.json
[
[
"^blog/(.*?)$",
"/journal/$1",
301
],
[
"^bookmark/(1234|1235)$",
"/2005/11/bookmark-$1",
301
]
// …and so on
]
The regular expressions in the JSON file follow the same scheme as the RewriteRule
syntax used in .htaccess
files, and the integer defines the HTTP code to be returned. If no matching rule is found, Kirby returns the regular error page.