This makes use of Runway's error handling and works well with the new PerchSystem::use_error_page(404); (which I LOVE). First, create a collection named "301 Redirects" with this template:

Template: 301_redirects.html

  <p><br><strong>Note:</strong> For both old and new URL, please omit protocol (http://) and domain. I.e. <strong></strong> would be <strong>/about/</strong></p>

<perch:content id="old_url" type="text" label="Old URL" size="xl" title="true" required="true" />
<perch:content id="new_url" type="text" label="New URL" size="xl" required="true" />

Now, edit the 404 master page to check the collection for an existing 301 before sending the visitor to the 404 page:

Master page: /perch/templates/pages/errors/404.php


// get URL with parameters and set variable
$array[]= parse_url($_SERVER['REQUEST_URI']);
if (empty($array[0]['query'])) {
else {

// check 301 collection for old URL
$result = perch_collection('301 Redirects', [
  'skip-template' => true,
  'return-html' => true,

// 301 exists, send 301 header and forward to new URL
if (!empty(array_filter($result))) {
  header("HTTP/1.1 301 Moved Permanently");
  header("Location: ".$result[0]['new_url']);

// no 301 found, send 404 header and output your 404 markup. if you use mine, be sure to change the email address in the link!
else {
  header("HTTP/1.0 404 Not Found");

<div class="container" style="max-width:40em;text-align:center;">
  <br><br><h1>404 - Page Not Found</h1>
  <p>Sorry, the page you requested could not be found.</p>
  <p>If you've reached this message by clicking a link on our site or elsewhere, please <a target="_blank" href="">let us know</a> so we can fix this error.</p>
  <p><br><a href="/" class="rectLink">Go to Homepage</a></p><br>