Redirects
Path redirects go in a _redirects file at the root of the deploy, one rule per line. The format is a subset of the one Netlify and Cloudflare Pages read.
The file
/from /to [status]
Separate the fields with spaces or tabs, as many as you like. A line whose first non-blank character is # is a comment, and blank lines are skipped. A comment after a rule on the same line is rejected as extra fields.
The file must be UTF-8. A leading byte-order mark is ignored, and \n, \r\n and lone \r line endings are all accepted.
Only a file named exactly _redirects at the root of the deploy is read as rules, and it is not served. A _Redirects, or a sub/_redirects, is served as an ordinary file.
The file may be at most 64 KB, with at most 1,000 rules and 2,048 bytes per line.
Rules
- Exact.
/old /newredirects/oldto/new. Both paths start with/. - Wildcard.
/blog/* /articles/:splatredirects/blog/2024/postto/articles/2024/post. The*must end the source, and it matches everything after the prefix, slashes included.:splatis the only placeholder, and it must end the target. - Status. The optional third field is
301,302,303,307or308. The default is301.
An extensionless exact source also matches its slashed form, and carries the slash to a plain target: under /about /elsewhere, /about/ redirects to /elsewhere/. A target with a file extension, a query or a fragment is used as written, so under /about /page.html both /about and /about/ go to /page.html.
A rule wins over a file at the same path. On Netlify the file wins unless the rule is forced with !, which is not supported here. Keep each path either a redirect or a file.
Query strings
The request’s query string is carried to the target. Under /old /new, /old?ref=spring redirects to /new?ref=spring, and under /blog/* /articles/:splat, /blog/2024/post?ref=spring redirects to /articles/2024/post?ref=spring.
When the target has a query of its own, the request’s query follows it after &: under /old /new?lang=en, /old?ref=spring redirects to /new?lang=en&ref=spring. A fragment on the target stays at the end.
What is rejected
- Any status other than
301,302,303,307and308. There are no rewrites or proxying, so200is rejected. - A target on another origin. A target must be a path on the same site. A URL such as
https://other.example,//host,/\host, any backslash, and an encoded%2for%5care rejected. - Forced rules and conditions. A
!after the status, conditions on country, language, cookie or role, and query-parameter matches. - Named placeholders.
/movies/:titleis rejected. A colon inside a segment, as in/a:b, is an ordinary path. - A
*in the target. Use:splat. - A wildcard without
:splat, or:splatwithout a wildcard. Both are present or neither is.
When a line is wrong
A wrong line fails the whole deploy with a line-numbered error. When you deploy a directory, kamakiri deploy checks the file before uploading anything. An archive is checked by the service after the upload. Either way, the live site does not change.
This file asks for a 200 on line 3:
/old-page /new-page 301
/blog/* /posts/:splat 301
/api/* /backend/:splat 200
$ kamakiri deploy ./dist
line 3: invalid status 200, must be 301, 302, 303, 307, or 308
$ kamakiri deploy ./dist
3行目: ステータス200は無効です。301、302、303、307、308のいずれかを指定してください
Fix or remove the line and deploy again.
Portability
301 and 302 rules behave the same on Kamakiri, Netlify and Cloudflare Pages. Netlify documents only those two. A rule without a status is 301 here and on Netlify, but 302 on Cloudflare Pages, so write the status when the code matters.
Examples
# Exact, permanent (301 default)
/old/page/ /new/page/
# Temporary
/promo /campaign/spring 302
# Wildcard: the suffix is carried to the target
/blog/* /articles/:splat
# Versioned API, keep-method redirect
/api/v1/* /api/v2/:splat 307