Redirect deleted pages

We have a bunch of historical event pages that have no value, but we still have upcoming events in the events folder that we need, as well as some historical events with videos that do have value that we need to keep working.

What I want to do is redirect all the old events, without affecting the new ones. The catch: I want to delete the old ones, so I can’t use their template as a mechanism for the redirect.

My old solution: Set the old ones as invisible, then in the template check for visibility. If invisible, redirect to the main events page.

The problem: Now that I’m implementing the control panel, this makes for a VERY cluttered events section in the control panel.

What I need: To be able to use htaccess or something to redirect all subpages under events, but only if they no longer physically exist.

I could have the 404 template do the redirect for me, but that kind of seems like bad form.

Anybody know enough about htaccess to pull something like this off?

Don’t know enough about htaccess, but you could use routes. In your route, you can check if the page exists, if so, go to the page, and if not, redirect wherever.

1 Like

Oh, that’s excellent. I’ll give it a try.

You could even pass a message from the route to the template if the user requested an invalid page, something along the lines: “The event you requested does no longer exist. Maybe you are interested in one of our upcoming events…”

OK, this is what I have so far, but it doesn’t appear to be doing anything. Maybe you’ll see something obvious:

c::set('routes', array(
      'pattern' => 'events/(:num)/(:any)',
      'action' => function($numPlaceholder, $anyPlaceholder) {
          $page = site()->index()->find($anyPlaceholder);
          if ($page != '') {
              return site()->visit($page);
          } else {
              return site()->visit('/events');

What is a correct path to an event page? Is the num part of the URI?

 $page = page('events/ . $numPlaceholder . DS . $anyPlaceholder);
          if (!$page) {
              return site()->visit('events');
          } else {
              return $page;

They are currently in year-based subfolders, so it would be something like /events/2017/cloudnativeconkubecon-north-america.

Oh, whoops. Looks like that DOES work. Turns out I had my routes declaration with my previous route repeated in my localhost config, which means it cleared the new one from the main config out.

@texnixe related: instead of “visit” could I do “redirect” or something? Right now it keeps the old URL, which I guess is OK, but ideally we want Google to realize it’s redirected, and we don’t want the end user to get confused as to why that URL is showing the full events list.

Edit: Oh, duh, use go() instead of site()->visit().

Have you considered Redirecty - Redirects plugin for Kirby 2.1.0+ ?

Seems like it would require manually entering a redirect for each event, which is too much work.

You could simple use go() with an HTTP code or return a Response object


On a recent project, where the client previously had a Shopify website, we did a manual list of old URLs and used Kirby router to handle the redirections:

<?php // site/plugins/redirect/redirections.php

return [
    '2015/event-name' => 'events',
    '2016/event-name-2' => 'events',
    // ...
<?php // site/plugins/redirect/redirect.php

kirby()->set('route', [
    'pattern' => 'events/(:any)',
    'method'  => 'GET',
    'action'  => function($event) {
        $redirections = include(__DIR__ . DS . 'redirections.php');

        if (array_key_exists($event, $redirections)) {
            return redirect::to($redirections[$event], 301);

        return site()->visit("events/$event");

I tried to slightly adapt it to your use case, but since you are always redirecting to the same page, you can simplify it more.

To keep the redirection list updated, I would use a page delete hook, check if the page is an event, then to write it’s URI to the redirections.php file automatically.

This is fine if you want to redirect individual URLs to different targets, but for the above use case keeping a redirect list is not necessary at all. Just out of curiosity, how would the hook know where to redirect to unless you specify this manually somewhere (where, in the page you are about to delete?).

@pedroborges, thanks for the input, and this may be something worth coming back to for future things. For this one, though, the route method suggested by Sonja is perfect.

Also, I don’t generally use the panel, especially for things like deleting old events, so relying on a panel hook for something like this wouldn’t really work.

Got it.

In my use it was a list of ~40 pages so the client did it manually for the URLs the didn’t have a match on the new website. What I suggested using the page delete hook would only work on Matthew’s use case, since all deleted pages would redirect to the same page.

In cases where I have a fixed list of pages that should redirect somewhere else, I usually do this in the .htaccess or ideally in the server configuration. This is preferable to routes, because PHP doesn’t have to kick in first.

In the current case, there is probably no pattern nor a fixed list that you could use, so I think a route is the only way to achieve this.

Using the page delete hook instead of htaccess could make sense in some situations, just because it would be automatic instead of requiring the user to go manually enter something in htaccess after deleting it. However, I’m not entirely sure where that would be advantageous as opposed to just using the generic route method above.

The only use case for this that I can think of would be if you redirect those deleted pages to individual targets that you would save in the page before you delete it, so that the delete hook could fetch that information and add it to the redirect list. You wouldn’t be able to do this with a generic route. But I don’t know if that would be a a feasible workflow.

I actually just had a good use case for this.

You post an article and then realize there’s an embarrassing typo in the URL slug. You don’t want a generic redirect because you want it to link to the correct version of the page, and you don’t want someone to have to dig into .htaccess to fix it.

So, you create a new one, update a redirect field or something in the old one, and then when you delete the old one, have it add it to the redirect list with the contents of the redirect field.

This actually would be cool if it was built into Kirby where you could change the slug of a page and have it automagically redirect the old version.

1 Like