Preserving The Hash And Query String With Jekyll Redirects

Published: September 21, 2017

Tags:

If you’re running Jekyll on GitHub pages and looking to set up redirects, there’s a good chance you stumbled upon jekyll-redirect-from. It’s a nice little tool for creating redirects, simply by declaring them in a page’s front matter. However, if you create a redirect using jekyll-redirect-from, there’s an issue that you might be concerned about…it does not preserve the query string or hash from the original request URL when redirecting the user.

There’s an issue in the repo about this which, at the time of writing this, has been open for nearly a year. There’s also a PR to fix it. However, in the interest of keeping jekyll-redirect-from simple and lightweight it seems unlikely that this will be fixed.

Fortunately, I’ve found a workaround that allows redirects on GitHub pages and preserves the query string and hash.

The Workaround

The workaround is quite simple. jekyll-redirect-from operates by generating a static HTML file with a meta refresh tag, JavaScript, and a fallback link.

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

The proposed solution for preserving the query string and hash involves adding additional JavaScript to the page to check the URL for a query string or hash and append it to the redirect URL. It also updates the meta refresh to wait for one second rather than zero, to allow the JavaScript to execute. The resulting HTML looks like this…

<!DOCTYPE html>
<html lang="en-US">
  <meta charset="utf-8">
  <title>Redirecting…</title>
  <link rel="canonical" href="http://example.com/new-page/">
  <meta http-equiv="refresh" content="0; url=http://example.com/new-page/">
  <h1>Redirecting…</h1>
  <a href="http://example.com/new-page/">Click here if you are not redirected.</a>
  <script>
    var url = 'http://example.com/new-page/';
    if (location.search && url.indexOf('?') === -1) {
      url = url.replace(/($|#)/, location.search + '$1');
    }
    if (location.hash && url.indexOf('#') === -1) {
      url += location.hash; 
    }
    location=url;
  </script>
</html>

The workaround is to take the HTML that would be generated if this PR were merged and simply commit it to the code base in the appropriate location. The redirect_from directive would also need to be removed from the front matter.

For example if we were redirecting from /old-page to /new-page instead of putting a redirect_from rule in /new-page’s front matter, we’d create old-page.html in the root of our repository and add the generated static HTML to redirect to /new-page. old-page.html would look like this…

<!DOCTYPE html>
<html lang="en-US">
  <meta charset="utf-8">
  <title>Redirecting…</title>
  <link rel="canonical" href="http://example.com/new-page/">
  <meta http-equiv="refresh" content="0; url=http://example.com/new-page/">
  <h1>Redirecting…</h1>
  <a href="http://example.com/new-page/">Click here if you are not redirected.</a>
  <script>
    var url = 'http://example.com/new-page/';
    if (location.search && url.indexOf('?') === -1) {
      url = url.replace(/($|#)/, location.search + '$1');
    }
    if (location.hash && url.indexOf('#') === -1) {
      url += location.hash; 
    }
    location=url;
  </script>
</html>

It’s not the cleanest solution, but it gets the job done.

Max Chadwick Hi, I'm Max!

I'm a software developer who mainly works in PHP, but loves dabbling in other languages like Go and Ruby. Technical topics that interest me are monitoring, security and performance. I'm also a stickler for good documentation and clear technical writing.

During the day I lead a team of developers and solve challenging technical problems at Rightpoint where I mainly work with the Magento platform. I've also spoken at a number of events.

In my spare time I blog about tech, work on open source and participate in bug bounty programs.

If you'd like to get in contact, you can find me on Twitter and LinkedIn.