# moved addresses (app.Redirects)

> `app.Redirects` lists the addresses your app has moved and where they went. A request for one is answered with a 301, or a 302 for `Permanent = false`, before any page is looked at, so old links, bookmarks and search results keep working. A move that depends on data, such as a product whose slug changed, belongs in the page's own `head { RedirectTo = … }` instead.

<!-- id: config-redirects · area: config · stability: stable · html: https://osysharp.com/reference/config/redirects/ -->

## Summary        {#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:

```osy syntax
app.Redirects = [
  new Redirect("/shop", "/"),                                // 301 — moved for good
  new Redirect("/sale", "/c/summer") { Permanent = false },  // 302 — for now
];
```

## Signature      {#signature}
```osy syntax
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    {#description}

### 301 or 302   {#status}
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   {#order}
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}
`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   {#which}
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 = … }`](https://osysharp.com/reference/ui/head/). That answers the same 301, from the page whose data decides it.

### It is computed once per version   {#cached}
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       {#examples}

### An app that moved its shop and its feed   {#example}
```osy title="old links keep working — a shop that became the home page, a sale that moves, a feed on another host" test app=config-redirects
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   {#example-refused}
```osy syntax
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       {#see-also}
- [head](https://osysharp.com/reference/ui/head/): `head { RedirectTo = … }`, the move a page's data decides.
- [[Serve]](https://osysharp.com/reference/api/serve/): `[Serve("/…")]`, an address answered with a body instead of a page.
- [routes and pages](https://osysharp.com/reference/ui/routing/): how a page gets its address.
