Skip to content

Latest commit

 

History

History
180 lines (126 loc) · 6.84 KB

0041-redirects.md

File metadata and controls

180 lines (126 loc) · 6.84 KB

Summary

Add a redirects config option to the Astro config which allows you to define redirects in a central place. Allow integrations to read this information and apply it to it's own redirects configuration for deployment.

Example

New config option used like so:

import { defineConfig } from 'astro/config';

export default defineConfig({
  redirects: {
    '/other': '/place'
  }
});

You can also specify the status code by using an object notation:

import { defineConfig } from 'astro/config';

export default defineConfig({
  redirects: {
    '/other': {
      status: 302,
      destination: '/place'
    }
  }
});

Background & Motivation

This was original proposed as a stage 1 discussion here and was one of the top voted proposals.

As websites age there are times where routes are rearchitectured. In order preserve existing links to content on the web it is common to set up redirects in your web server.

Redirect configuration varies depending on what web server or host you use. Some times you might even want to change hosts, in which case your redirects need to be converted over to a new format.

Having a redirects configuration within Astro itself allows a single place to define redirects that works everywhere.

Goals

  • Works in development mode.
  • Writes out <meta http-equiv="refresh"> tags in static builds.
  • Gives proper hooks to integrations so that they can write to their own configuration.

Non-Goals

  • Dynamic behavior that goes beyond the capabilities of the file-based routing system. So nothing based on the properties of the request, user session, etc. Regular routes still should be used for this scenario.
  • Redirects to pages that do not exist in the Astro project.
  • External redirects.

Detailed Design

This will be implemented as a feature of the internal routing. The RouteData type will be extended to include:

interface RedirectConfig = string | {
  status: 300 | 301 | 302 | 303 | 304 | 307 | 308;
  destination: string;
}

export interface RouteData {
  type: 'redirect';
  // ...
  redirect?: RedirectConfig;
  redirectRoute?: RouteData;
}

Our core rendering handles routing and will detect this type of route and return a Response with a status code in the 3xx range with the Location header set to the value of the route's redirect property.

When using the object notation { destination: string; status: number; } the status code specified there will be used. Otherwise the default is 301 for GET requests and 308 for any other method.

Dynamic routes

Dynamic routes are supported through the same syntax as in the file-based routing system. For example, if a site moved its blog it might set up a redirect like so:

import { defineConfig } from 'astro/config';

export default defineConfig({
  redirects: {
    '/blog/[...slug]': '/team/articles/[...slug]'
  }
});

In SSG mode this will call the destinations getStaticPaths method to get valid static paths. Those paths will be used to generate the HTML files for the redirects.

Routing priority

Redirects will use the priority assignment algorithm as file-system routing. For example, you could have a file system route /src/pages/blog/contributing.astro and a redirect route /blog/[...slug]. If these were both filesystem routes you would expect contributing.astro to be prioritized over the spread route. This is the case with redirects a well.

In the case of exact matches, the file-system route will be prioritized (by being ordered first in the list). This is meant to match the behavior of host systems (such as Netlify).

Static generation

Currently the static generation code throws for any non-200 response. With this change it will now accept any 3xx as a valid response codes. It will generate an HTML doc that looks like:

`<!doctype html>
<title>OLD_LOCATION</title>
<meta http-equiv="refresh" content="0;url=LOCATION" />

Adapters

Adapters can integrate with this new route type through the astro:build:done hook which includes the routes property. This is an array of route datas that were built. Redirects will be part of this array.

Additionally adapters can disable the HTML generation via a new configuration value build.redirects which can be set to false. Users can also set this value, but it is more likely to come from an integration via the astro:config:setup hook:

export default {
  hooks: {
    'astro:config:setup': ({ updateConfig }) => {
      updateConfig({
        build: {
          redirects: false
        }
      });
    }
  }
}

Adapters who have their own configuration files, such as the Netlify and Cloudflare _redirects file, will disable HTML generation because the host serves HTML before it checks configuration.

Testing Strategy

  • We have existing redirect tests for SSR. These will be updated to test SSG redirect behavior for:
    • redirects config
    • Redirects created via Astro.redirect() and new Response as those are now enabled by this change.

Drawbacks

  • Adds some new complexity to the config.
  • New type of route in the RouteData.
  • There could be some expectations that all adapters handle this perfectly. This is a good expectation! We should do our best to make sure as many adapters support this as possible.
    • The HTML based fallback still does work.

Alternatives

The other major design would be to allow defining redirects in the file where the page now lives. For example in a markdown page you could do:

---
redirect_from:
  - /one
  - /two
---

This method is nice because it is file-based and in the file where the redirect is ultimately going to go to.

The major problem with this design is that we need to load and process every page before we know about these routes. So this would significantly slow down dev, where we don't need to know about all pages until they are requested.

A secondary problem with this alternative is that it's not clear how we should define redirects in non-Markdown pages, for example in an API route how would you define them? Via special comment syntax? The export const foo pattern from prerender?

Adoption strategy

  • This is a small change and can go through in the next minor release.