Eleventy Redirect From

An Eleventy drop-in replacement for the jekyll-redirect-from style of redirect files.

Published on  • Updated on  • 📖 5 min. read Edit on GitHub


When migrating my site from Jekyll on GitHub Pages to Eleventy on GitHub Pages, I wanted to continue to use my page redirects to preserve old links.

Caveat: Emitting new HTML files with redirect content is best for webservers that just serve files. Hosts like Netlify, Vercel, AWS Amplify, or your own webserver all offer ways to centrally configure redirects. Using your host or webserver to configure redirects is probably a better approach than serving redirect HTML files that are handled on the client side.

Backstory

GitHub Pages supports automatically building a Jekyll site when we commit to the target branch. There are a collection of plugins that we can use on top of base Jekyll installation. One of these is jekyll-redirect-from. This lets us specify pages/routes to redirect from, to the current page.

Let's say we have a page with the following front-matter. This will emit a page at /new-page.html.

---
title: My Cool Page
permalink: /new-page
redirect_from: /old-page
---

With the jekyll-redirect-from plugin, it will also emit a page at /old-page.html, with the following content. This causes a browser to immediately load the page at the new URL, while preserving any links to the old page.

<!doctype html>
<html lang="en-US">
  <meta charset="utf-8" />
  <title>Redirecting&hellip;</title>
  <link rel="canonical" href="https://example.com/new-page" />
  <script>
    location = 'https://example.com/new-page';
  </script>
  <meta http-equiv="refresh" content="0; url=https://example.com/new-page" />
  <meta name="robots" content="noindex" />
  <h1>Redirecting&hellip;</h1>
  <a href="https://example.com/new-page">
    Click here if you are not redirected.
  </a>
</html>

Eleventy Template

With Eleventy, I was able to replicate this functionality with a single template utilizing the pagination feature with a before callback. This lets us reduce the collection of all pages into a list of redirect pairs (the source path and the destination path). Because this emits a page for each redirect, it needs to be treated as a full page, so put this in your top-level directory for pages, not in an _includes directory.

Using this template allows for drop-in replacement from the jekyll-redirect-from style of creating redirects. We are able to check if the value is a string or an array in order to handle one or multiple redirects.

Single

redirect_from: /old-url/page

Multiple

redirect_from: [/old-url/page, /some-other-page]

eleventy-redirect.njk

We use a JavaScript front matter block in order to create a before function.

---js
{
  pagination: {
    data: "collections.all",
    size: 1,
    alias: "redirect",
    before: function (data) {
      return data.reduce((redirects, page) => {
        if (Array.isArray(page.data.redirect_from)) {
          for (let url of page.data.redirect_from) {
            redirects.push({ to: page.url, from: url });
          }
        } else if (typeof page.data.redirect_from === 'string') {
          redirects.push({ to: page.url, from: page.data.redirect_from });
        }
        return redirects;
      }, []);
    },
    addAllPagesToCollections: false,
  },
  permalink: "{{ redirect.from }}/index.html",
  eleventyExcludeFromCollections: true,
}
---
<!DOCTYPE html>
<html lang="en-US">
  <meta charset="utf-8" />
  <title>Redirecting&hellip;</title>
  <link rel="canonical" href="{{ redirect.to | url }}" />
  <script>
    location = '{{ redirect.to | url }}';
  </script>
  <meta http-equiv="refresh" content="0; url={{ redirect.to | url }}" />
  <meta name="robots" content="noindex" />
  <h1>Redirecting&hellip;</h1>
  <a href="{{ redirect.to | url }}">Click here if you are not redirected.</a>
</html>

Eleventy emits files in their own directory and as an index.html file, so watch out for trailing slashes and how your webserver handles them. For example, /new-page.html and /new-page/index.html may be handled differently.

Let me know if this works for you and if there's anything you think could be improved. Hope this helped!