|
| 1 | +# `_redirects` File Specification |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | +**Authors**: |
| 6 | + |
| 7 | +- Justin Johnson ([@justincjohnson](https://github.com/justincjohnson)) |
| 8 | + |
| 9 | +---- |
| 10 | + |
| 11 | +**Abstract** |
| 12 | + |
| 13 | +The Redirects File specification is an extension of the Subdomain Gateway and DNSLink Gateway specifications. |
| 14 | + |
| 15 | +Developers can enable URL redirects or rewrites by adding redirect rules to a file named `_redirects` stored underneath the root CID of their web site. |
| 16 | + |
| 17 | +This can be used, for example, to enable URL rewriting for hosting a single-page application, to redirect invalid URLs to a pretty 404 page, or to avoid [link rot](https://en.wikipedia.org/wiki/Link_rot) when moving to IPFS-based website hosting. |
| 18 | + |
| 19 | +# Table of Contents |
| 20 | + |
| 21 | +- [File Name and Location](#file-name-and-location) |
| 22 | +- [File Format](#file-format) |
| 23 | + - [From](#from) |
| 24 | + - [To](#to) |
| 25 | + - [Status](#status) |
| 26 | + - [Placeholders](#placeholders) |
| 27 | + - [Splat](#splat) |
| 28 | + - [Comments](#comments) |
| 29 | + - [Line Termination](#line-termination) |
| 30 | + - [Max File Size](#max-file-size) |
| 31 | +- [Evaluation](#evaluation) |
| 32 | + - [Subdomain or DNSLink Gateways](#subdomain-or-dnslink-gateways) |
| 33 | + - [Order](#order) |
| 34 | + - [No Forced Redirects](#no-forced-redirects) |
| 35 | +- [Error Handling](#error-handling) |
| 36 | +- [Security](#security) |
| 37 | +- [Appendix: notes for implementors](#appendix-notes-for-implementors) |
| 38 | + - [Test fixtures](#test-fixtures) |
| 39 | + |
| 40 | +# File Name and Location |
| 41 | + |
| 42 | +The Redirects File MUST be named `_redirects` and stored underneath the root CID of the web site. |
| 43 | + |
| 44 | +# File Format |
| 45 | + |
| 46 | +The Redirects File MUST be a text file containing one or more lines with the following format (brackets indication optionality). |
| 47 | + |
| 48 | +``` |
| 49 | +from to [status] |
| 50 | +``` |
| 51 | + |
| 52 | +## From |
| 53 | + |
| 54 | +The path to redirect from. |
| 55 | + |
| 56 | +## To |
| 57 | + |
| 58 | +The URL or path to redirect to. |
| 59 | + |
| 60 | +## Status |
| 61 | + |
| 62 | +An optional integer specifying the HTTP status code to return from the request. Supported values are: |
| 63 | + |
| 64 | +- `200` - OK |
| 65 | + - Redirect will be treated as a rewrite, returning OK without changing the URL in the browser. |
| 66 | +- `301` - Permanent Redirect (default) |
| 67 | +- `302` - Found (commonly used for Temporary Redirect) |
| 68 | +- `303` - See Other (replacing PUT and POST with GET) |
| 69 | +- `307` - Temporary Redirect (explicitly preserving body and HTTP method of original request) |
| 70 | +- `308` - Permanent Redirect (explicitly preserving body and HTTP method of original request) |
| 71 | +- `404` - Not Found |
| 72 | + - Useful for redirecting invalid URLs to a pretty 404 page. |
| 73 | +- `410` - Gone |
| 74 | +- `451` - Unavailable For Legal Reasons |
| 75 | + |
| 76 | +## Placeholders |
| 77 | + |
| 78 | +Placeholders are named variables that can be used to match path segments in the `from` path and inject them into the `to` path. |
| 79 | + |
| 80 | +For example: |
| 81 | + |
| 82 | +``` |
| 83 | +/posts/:month/:day/:year/:slug /articles/:year/:month/:day/:slug |
| 84 | +``` |
| 85 | + |
| 86 | +This rule will redirect a URL like `/posts/06/15/2022/hello-world` to `/articles/2022/06/15/hello-world`. |
| 87 | + |
| 88 | +### Splat |
| 89 | + |
| 90 | +If a `from` path ends with an asterisk (i.e. `*`), the remainder of the `from` path is slurped up into the special `:splat` placeholder, which can then be injected into the `to` path. |
| 91 | + |
| 92 | +For example: |
| 93 | + |
| 94 | +``` |
| 95 | +/posts/* /articles/:splat |
| 96 | +``` |
| 97 | + |
| 98 | +This rule will redirect a URL like `/posts/2022/06/15/hello-world` to `/articles/2022/06/15/hello-world`. |
| 99 | + |
| 100 | +Splat logic MUST only apply to a single trailing asterisk, as this is a greedy match, consuming the remainder of the path. |
| 101 | + |
| 102 | +### Comments |
| 103 | + |
| 104 | +Any line beginning with `#` will be treated as a comment and ignored at evaluation time. |
| 105 | + |
| 106 | +For example: |
| 107 | + |
| 108 | +``` |
| 109 | +# Redirect home to index.html |
| 110 | +/home /index.html 301 |
| 111 | +``` |
| 112 | + |
| 113 | +is functionally equivalent to |
| 114 | + |
| 115 | +``` |
| 116 | +/home /index.html 301 |
| 117 | +``` |
| 118 | + |
| 119 | +### Line Termination |
| 120 | + |
| 121 | +Lines MUST be terminated by either `\n` or `\r\n`. |
| 122 | + |
| 123 | +### Max File Size |
| 124 | + |
| 125 | +The file size MUST NOT exceed 64 KiB. |
| 126 | + |
| 127 | +# Evaluation |
| 128 | + |
| 129 | +## Subdomain or DNSLink Gateways |
| 130 | + |
| 131 | +Rules MUST only be evaluated when hosted on a Subdomain or DNSLink Gateway, so that we have [Same-Origin](https://en.wikipedia.org/wiki/Same-origin_policy) isolation. |
| 132 | + |
| 133 | +## Order |
| 134 | + |
| 135 | +Rules MUST be evaluated in order, redirecting or rewriting using the first matching rule. |
| 136 | + |
| 137 | +## No Forced Redirects |
| 138 | + |
| 139 | +All redirect logic MUST only be evaluated if the requested path is not present in the DAG. This means that any performance impact associated with checking for the existence of a Redirects File or evaluating redirect rules will only be incurred for non-existent paths. |
| 140 | + |
| 141 | +# Error Handling |
| 142 | + |
| 143 | +If the Redirects File exists but there is an error reading or parsing it, the errors MUST be returned to the user with a 500 HTTP status code. |
| 144 | + |
| 145 | +# Security |
| 146 | + |
| 147 | +This functionality will only be evaluated for Subdomain or DNSLink Gateways, to ensure that redirect paths are relative to the root CID hosted at the specified domain name. |
| 148 | + |
| 149 | +Parsing of the `_redirects` file should be done safely to prevent any sort of injection vector or daemon crash. |
| 150 | + |
| 151 | +The [max file size](#max-file-size) helps to prevent an additional [denial of service attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) vector. |
| 152 | + |
| 153 | +# Appendix: notes for implementors |
| 154 | + |
| 155 | +## Test fixtures |
| 156 | + |
| 157 | +Sample files for various test cases can be found in `QmQyqMY5vUBSbSxyitJqthgwZunCQjDVtNd8ggVCxzuPQ4`. |
| 158 | +Implementations are free to use it for internal testing. |
| 159 | + |
| 160 | +``` |
| 161 | +$ ipfs ls QmQyqMY5vUBSbSxyitJqthgwZunCQjDVtNd8ggVCxzuPQ4 |
| 162 | +QmcBcFnKKqgpCVMxxGsriw9ByTVF6uDdKDMuEBq3m6f1bm - bad-codes/ |
| 163 | +QmYBhLYDwVFvxos9h8CGU2ibaY66QNgv8hpfewxaQrPiZj - examples/ |
| 164 | +QmU7ysGXwAtiV7aBarZASJsxKoKyKmd9Xrz2FFamSCbg8S - forced/ |
| 165 | +QmWHn2TunA1g7gQ7q9rwAoWuot2hMpojZ6cZ9ERsNKm5gE - good-codes/ |
| 166 | +QmRgpzYQESidTtTojN8zRWjiNs9Cy6o7KHRxh7kDpJm3KH - invalid/ |
| 167 | +QmYzMrtPyBv7LKiEAGLLRPtvqm3SjQYLWxwWQ2vnpxQwRd - newlines/ |
| 168 | +QmQTfvjGmvTfxFpUcZNLdTLuKV227KJkGiN6xooHVeVZAS - too-large/ |
| 169 | +``` |
| 170 | + |
| 171 | +For example, the "examples" site can be found in `QmYBhLYDwVFvxos9h8CGU2ibaY66QNgv8hpfewxaQrPiZj`. |
| 172 | + |
| 173 | +``` |
| 174 | +$ ipfs ls /ipfs/QmYBhLYDwVFvxos9h8CGU2ibaY66QNgv8hpfewxaQrPiZj |
| 175 | +Qmd9GD7Bauh6N2ZLfNnYS3b7QVAijbud83b8GE8LPMNBBP 7 404.html |
| 176 | +QmSmR9NShZ89VEBrn9SBy7Xxvjw8Qe6XArD5GqtHvbtBM3 7 410.html |
| 177 | +QmVQqj9oZig9tH3ENHo4bxV5pNgssUwFCXUjAJAVcZVbJG 7 451.html |
| 178 | +QmZU3kboiyi9jV59D8Mw8wzuvsr3HmvskqhYRRhdFA8wRq 317 _redirects |
| 179 | +QmaWDLb4gnJcJbT1Df5X3j91ysiwkkyxw6329NLiC1KMDR - articles/ |
| 180 | +QmS6ZNKE9s8fsHoEnArsZXnzMWijKddhXXDsAev8LdTT5z 9 index.html |
| 181 | +QmNwEgMrExwSsE8DCjZjahYfHUfkSWRhtqSkQUh4Fk3udD 7 one.html |
| 182 | +QmVe2GcTbEPZkMbjVoQ9YieVGKCHmuHMcJ2kbSCzuBKh2s - redirected-splat/ |
| 183 | +QmUGVnZaofnd5nEDvT2bxcFck7rHyJRbpXkh9znjrJNV92 7 two.html |
| 184 | +``` |
| 185 | + |
| 186 | +The `_redirects` file is as follows. |
| 187 | + |
| 188 | +``` |
| 189 | +$ ipfs cat /ipfs/QmYBhLYDwVFvxos9h8CGU2ibaY66QNgv8hpfewxaQrPiZj/_redirects |
| 190 | +/redirect-one /one.html |
| 191 | +/301-redirect-one /one.html 301 |
| 192 | +/302-redirect-two /two.html 302 |
| 193 | +/200-index /index.html 200 |
| 194 | +/posts/:year/:month/:day/:title /articles/:year/:month/:day/:title 301 |
| 195 | +/splat/* /redirected-splat/:splat 301 |
| 196 | +/not-found/* /404.html 404 |
| 197 | +/gone/* /410.html 410 |
| 198 | +/unavail/* /451.html 451 |
| 199 | +/* /index.html 200 |
| 200 | +``` |
| 201 | + |
| 202 | +The non-existent paths that are being requested should be intercepted and redirected to the destination path and the specified HTTP status code returned. The rules are evaluated in the order they appear in the file. |
| 203 | + |
| 204 | +Any request for an existing file should be returned as is, and not intercepted by the last catch all rule. |
0 commit comments