# Redirect rules

> Answer redirects at the edge by name and path prefix: how rules match, how the destination is built, the order of checks and what they require.

A redirect rule answers requests for one name of the zone whose path starts with a prefix with a redirect (`301`, `302`, `307` or `308`) to an absolute URL. The request never reaches your origin. A zone can have up to 50 rules.

## How a rule matches

- `host` is one exact name of the zone (the zone itself or a name below it, no wildcards).
- `path_prefix` matches at a segment boundary: `/` matches every path; `/blog` matches `/blog` and `/blog/x`, not `/blogging`. The query string is not part of the match.
- When several rules of the same name match, the **longest prefix** wins.

## How the destination is built

`Location` is the `target`, plus:

- with `preserve_path` (default on): the rest of the path after the prefix;
- with `preserve_query` (default on): the request's query string, appended after the target's own query with `&`.

| Rule | Request | `Location` |
|---|---|---|
| `/` → `https://example.com` | `/a/b?x=1` | `https://example.com/a/b?x=1` |
| `/blog` → `https://news.example.net/articles/` | `/blog/x` | `https://news.example.net/articles/x` |
| `/blog` → `https://news.example.net/articles/`, `preserve_path: false` | `/blog/x` | `https://news.example.net/articles/` |
| `/` → `https://example.com/?ref=old` | `/?utm=y` | `https://example.com/?ref=old&utm=y` |

A rule that would redirect to its own name on a path it captures (a loop) is rejected, for example `www.example.com` `/` → `https://www.example.com/home`.

## Choosing the status

| Status | Use |
|---|---|
| `301` (default) | Permanent move. Browsers and search engines remember it. |
| `302` | Temporary; use it while testing. |
| `307` | Temporary, keeps the method (`POST` stays `POST`). |
| `308` | Permanent, keeps the method. |

## Order at the edge

- ACME HTTP-01 challenges (`/.well-known/acme-challenge/`) are answered first and never redirected.
- A plain `http://` request is first redirected to `https://` on the same name, then the rule applies.
- Rules apply after the access gate, IP and country rules, threat feeds, rate limits and the AI Crawl Control policy, and before the WAF and the origin. See [How a request is processed](https://developers.openwaf.dev/waf/request-processing/).

## www and the apex

The edge already redirects `www.<apex>` to `<apex>` (301, same path and query) when the apex is a proxied site. It steps aside for an explicit rule when the `www` name has a rule that matches the path, or when the apex has a rule whose target is the `www` name (otherwise they would loop). So to make `www` your canonical name, add a rule on the apex: `example.com` `/` → `https://www.example.com`.

## Requirements

- **A proxied DNS record for the name.** Without it the DNS doesn't point to the edge and the edge doesn't know the name. The change succeeds but returns a warning for rules whose name has no proxied record.
- **A certificate that covers the name** for HTTPS. Automatic certificates cover the proxied names of the zone, so the proxied record is enough; with uploaded certificates, upload one that covers the name. See [Edge certificates](https://developers.openwaf.dev/ssl/edge-certificates/).

## Manage the rules

**Dashboard**

Domain → **Rules → Redirects**. **Add redirect** opens the editor with **Name**, **Path prefix**, **Destination URL**, **Status**, **Keep the rest of the path** and **Keep the query string**; it shows an example of the result and the form the rule will be saved in. Edit or remove rules from the list, then save the change.
**CLI**

```sh
openwaf-cloud ctl zones show example.com
openwaf-cloud ctl zones set example.com redirects:='[
  {"host":"old.example.com","target":"https://example.com"},
  {"host":"example.com","path_prefix":"/blog","target":"https://news.example.net/articles/","status":302}
]' --reason "Move the blog"
```
**API**

`PATCH /v1/orgs/{org}/zones/{zone}/edge` with `{"redirects": [...]}`. The list is replaced whole; `[]` removes every rule.
**MCP**

`update_settings` with `settings.redirects` (the whole list). Redirects belong to the zone: don't pass `host` to `update_settings` for them.
