Summary#
An address that stops working is a broken link everywhere it was ever shared. app.Redirects keeps each old address
answering, by sending the visitor, and a search engine, to where the content now lives:
app.Redirects = [
new Redirect("/shop", "/"), // 301 — moved for good
new Redirect("/sale", "/c/summer") { Permanent = false }, // 302 — for now
];Signature#
app.Redirects = [ new Redirect(<from>, <to>) { Permanent = <bool> }, … ];
class Redirect {
string From; // a path on this app, exactly as it is requested — "/old-name"
string To; // a path on this app — "/new-name" — or a full "https://…" address
bool Permanent = true; // 301 when true, 302 when false
}Description#
301 or 302#
A 301 says the move is permanent. A search engine then carries the old address's standing over to the new one and stops listing the old one. A 302 says it is for now, and the old address keeps its standing. Use 302 for a campaign address that points somewhere this month and somewhere else next month.
Answered before any page#
A redirect is checked before any page or [[api-serve|[Serve]]] address is matched. That is why a From that a page
or a served body still answers is refused at compile time: the page would be hidden behind the redirect, so the
compiler asks you to choose which one you meant. Two entries with one From are refused for the same reason.
A From may be file-shaped (/feed.rss). Every address on the app's host reaches it.
Where it may point#
To is a path on this app, or a full http(s):// address on another site. Moving a feed to a news host is a real
case. //host is refused: a browser reads it as another host, even though it looks like a path.
Fixed moves here, data-driven moves on the page#
A move you can write down belongs in this list, such as /shop becoming /. A move that depends on data does not.
When a product renames its slug, only the product page knows the old slug maps to the new one, so it says so with
head { RedirectTo = … }. That answers the same 301, from the page whose data decides it.
It is computed once per version#
The list is an ordinary expression, so it may be built with a helper or a loop. The platform evaluates it once for each compiled version of the app, not per request. A recompile evaluates it again.
Examples#
An app that moved its shop and its feed#
app.Redirects = [
new Redirect("/shop", "/"),
new Redirect("/sale", "/c/summer") { Permanent = false },
new Redirect("/feed.rss", "https://news.example.com/feed.rss"),
];
[Route("/")]
[AllowAnonymous]
[Title("Home")]
component Home() { render { Text("Welcome"); } }
[Route("/c/summer")]
[AllowAnonymous]
[Title("Summer")]
component Summer() { render { Text("Summer sale"); } }What the compiler refuses#
app.Redirects = [
new Redirect("/", "/home"), // "/" is answered by the page at "/" already
new Redirect("/old", "//evil.example"), // not an address: a path on this app, or a full https:// address
new Redirect("/gone", "/a"),
new Redirect("/gone", "/b"), // "/gone" is redirected twice
];See also#
- head:
head { RedirectTo = … }, the move a page's data decides. - [Serve]:
[Serve("/…")], an address answered with a body instead of a page. - routes and pages: how a page gets its address.