Aliases and redirects When a page moves, keep its old URL working with an **alias**. For redirects that aren't tied to a page, use a **`_redirects`** file. ## Page aliases List old URLs in `aliases`: ```yaml --- title: Building your own Ngrok in 130 lines aliases: [/blog/2021/building-your-own-ngrok-in-130-lines] --- ``` A single string is fine too: `aliases: /old-url`. Because it's page data, one line in a `_defaults.jsx` covers a whole section. After moving `blog/` to `articles/`: ```js // articles/_defaults.jsx aliases = [page.path.replace(/^\/articles/, "/blog")]; ``` Now `/articles/2021/ngrok` has the alias `/blog/2021/ngrok`, and `/articles` has `/blog`. A page that sets its own `aliases` replaces the default rather than adding to it. Aliases are read from defaults, frontmatter, and the page's `data()`. Aliases set by a layout are ignored. Only built pages get aliases (not grouping nodes), and a draft's aliases are drafts too. Alias rules: - Each alias must start with `/`. A trailing `/` is removed. - It can't contain `?`, `#`, `\`, `//`, or a `.` or `..` segment. - Invalid aliases are errors naming the page. Duplicates are dropped. After this, `page.aliases` is the cleaned-up list, which layouts and `pages()` can read, for example to list a page's old URLs. ## What an alias produces For an HTML page, `build` writes a small redirect page at each alias (`/blog/2021/ngrok/index.html`, or as-is for an alias with an extension like `/old.html`): ```html Redirecting to /articles/2021/ngrok /articles/2021/ngrok ``` These stubs work on any static host, including ones like GitHub Pages that can't send real redirects. They don't use layouts or globals, and they're identical on every build. The canonical link uses the page's path, not an absolute URL, and a `#fragment` in the old URL isn't carried over. `serve` answers an alias with a real `301 Moved Permanently`. For a non-HTML page, such as a feed, a redirect page would break feed readers, so the alias serves a **copy** of the page's output with its content type, in both `build` and `serve`. Old feed URLs keep working for existing subscribers. ## The `_redirects` file For other redirects, add a `_redirects` file at the site root, in a subset of Netlify's format: ``` # from to status /newsletter https://buttondown.com/progrium 302 /blog/* /articles/:splat /talks /articles?tag=talks ``` Each line is `from to [status]`. Blank lines are ignored and `#` starts a comment line. - `from` is a path, cleaned up like an alias. It may end in `/*` to match everything below it; `*` can't appear anywhere else. - `to` is a path starting with `/`, or an absolute `http`/`https` URL. With a wildcard `from`, `:splat` in `to` is replaced by what the `*` matched. A path `to` may include a query string. - `status` is `301`, `302`, `307`, or `308`. The default is `301`. Other Netlify features are errors naming the line: other statuses (`200` rewrites, `404`), the `!` force suffix, named placeholders like `:year`, query conditions, and extra fields. `build` copies `_redirects` to `_public/_redirects`, so hosts that read it (Netlify, Cloudflare Pages, and others) send real redirects. It also writes redirect pages from the rules, so hosts that don't read it still work: - **A rule without a wildcard** writes one redirect page at `from`. If `to` is one of your non-HTML pages, it writes a copy of that page instead. - **A wildcard rule pointing at a path with `:splat`** is expanded against your HTML pages. For `/blog/* /articles/:splat`, every built page under `/articles/` gets a redirect page at the matching `/blog/` path. Drafts aren't expanded. - **Other wildcard rules**, such as ones pointing off-site or without `:splat`, can't be expanded. `build` prints a warning naming the line: the rule only works on hosts that read `_redirects`. - A rule whose `from` has an extension other than `.html` gets no redirect page, and a warning, unless its `to` is one of your non-HTML pages, since an HTML page there would be served with the wrong type. In `serve`, every rule works with its real status, including wildcard rules `build` couldn't expand. The request's query string is kept when `to` has none. ## Conflicts Aliases and redirect rules share one URL space with pages, outputs, and static files. These fail the build, naming both sides: - an alias equal to a page's own path, or to another page's alias; - an alias that is also the `from` of a rule in `_redirects`; - an alias or rule that lands on a page, output, or static file. Paths expanded from a wildcard rule are the exception: if something already lives at the path, the rule skips it, and between overlapping wildcard rules the first one wins. So you can keep a real page at `/blog/2021/something` under a `/blog/*` rule, as on Netlify. In `serve`, a request is matched against pages, outputs, aliases, and plain rules first, then wildcard rules in file order, then files on disk.