Redirects and path rewrites
ReverseDock has two kinds of middleware for URLs. A redirect answers the client with a
Location header, and the client makes a new request. A path middleware changes the path
before the request goes to the backend, and the client never sees the change.
Both kinds are available since v0.1.
Before you start
Section titled “Before you start”- A working route and service. See Route a domain to an app.
- Routing happens before middlewares run. A path middleware changes what the backend sees, not which route matched.
Redirect to another URL
Section titled “Redirect to another URL”The redirect middleware has two forms: scheme alone, or regex with replacement.
scheme: https(orhttp) keeps the host, path and query and changes the scheme. Setportwhen the target port is not the scheme’s default. If the request already uses the target scheme, the middleware does nothing.regexis matched against the full request URL,scheme://host[:port]/path?query, where the scheme ishttpson a TLS connection andhttpotherwise, and the host is theHostheader as the client sent it.replacementcan use groups as$1or${name}. If the regex does not match, or the result is the current URL, the request continues to the backend.
Status codes
Section titled “Status codes”permanent defaults to false.
permanent |
GET | Other methods |
|---|---|---|
false (default) |
302 Found | 307 Temporary Redirect |
true |
301 Moved Permanently | 308 Permanent Redirect |
The second code tells the client to repeat the request with the same method and body, so a
redirected POST stays a POST. Browsers cache 301 and 308 answers, so start with the default
and set permanent: true once the target is final.
Example: move www to the bare domain
Section titled “Example: move www to the bare domain”This configuration serves example.com and sends every request for www.example.com to it,
keeping the path and query.
listeners: web: { address: ":80" }
defaults: listeners: [web] tls: off
routes: www: match: "Host(`www.example.com`)" service: site middlewares: [to-apex] site: match: "Host(`example.com`)" service: site
services: site: backends: [{ address: "site:8080" }]
middlewares: to-apex: type: redirect regex: "^http://www\\.example\\.com/(.*)$" replacement: "http://example.com/$1" permanent: trueCheck it:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' \ -H 'Host: www.example.com' 'http://localhost/blog/post?id=7'301 http://example.com/blog/post?id=7curl -I sends HEAD, which is not GET, so it gets 308 instead of 301.
The regex sees the Host header exactly as the client sent it. A client that sends
Host: www.example.com:80 does not match ^http://www\.example\.com/; write
www\.example\.com(:\d+)?/ if that matters to you.
Change the path the backend sees
Section titled “Change the path the backend sees”| Type | Setting | What it does |
|---|---|---|
strip_prefix |
prefixes (list) |
Removes the first listed prefix the path starts with. Adds X-Forwarded-Prefix with the removed prefix. |
add_prefix |
prefix |
Puts prefix in front of the path. A trailing / on the prefix is ignored. |
replace_path |
path |
Replaces the whole path. Adds X-Replaced-Path with the original path. |
rewrite |
regex, replacement |
Replaces every match of regex in the path. Adds X-Replaced-Path when the regex matched. |
All four keep the query string. They work on the path after
normalisation: percent-encoded, with dot segments and
// already removed.
Prefixes are plain string prefixes, as in PathPrefix. A strip_prefix of /api turns /api/users
into /users, /api into /, and also /apiv2 into /v2. If that is not what you want,
route with PathPrefix(`/api/`) and strip /api.
If the new path is not in normalised form, for example /api./x stripped of /api becomes
/./x, the request is refused with 400. The backend never receives a path that differs from
what was routed and checked.
Example: serve an API under /api
Section titled “Example: serve an API under /api”The backend serves /v1/users; the public URL is https://app.example.com/api/users.
listeners: web: { address: ":80" }
defaults: listeners: [web] tls: off
routes: api: match: "Host(`app.example.com`) && PathPrefix(`/api/`)" service: api middlewares: [strip-api, add-v1]
services: api: backends: [{ address: "api:8080" }]
middlewares: strip-api: type: strip_prefix prefixes: ["/api"] add-v1: type: add_prefix prefix: /v1Middlewares run in the order listed: /api/users?page=2 becomes /users?page=2, then
/v1/users?page=2. The backend also receives X-Forwarded-Prefix: /api, which some frameworks
use to build links.
A rewrite does the same in one step:
middlewares: api-to-v1: type: rewrite regex: "^/api/(.*)" replacement: "/v1/$1"Regular expressions use the syntax of the Rust regex crate: no look-around and no
backreferences. rdock validate reports a pattern that does not compile.
Check that it worked
Section titled “Check that it worked”rdock validate /etc/rdock/rdock.yamlreports a redirect withoutschemeorregex+replacement, a prefix that does not start with/, and invalid patterns.- To see the path the backend receives, point the route at an echo service such as
traefik/whoamiand look at the request line it prints.