Route by path
This guide serves app.example.com from a web application and sends everything under /api to a separate API service, which expects its paths without the /api prefix.
What you need
Section titled “What you need”- The setup from Route a domain to an app.
The configuration
Section titled “The configuration”listeners: web: { address: ":80" } websecure: { address: ":443", tls: {} }
certificates: example: cert_file: /etc/rdock/certs/example.com.pem key_file: /etc/rdock/certs/example.com.key
routes: web: match: "Host(`app.example.com`)" service: web api: match: "Host(`app.example.com`) && (Path(`/api`) || PathPrefix(`/api/`))" service: api middlewares: [strip-api]
middlewares: strip-api: type: strip_prefix prefixes: [/api]
services: web: backends: [{ address: "10.0.0.5:3000" }] api: backends: [{ address: "10.0.0.6:8080" }]Both routes match app.example.com. The api rule is longer, so its default priority is higher and it is tried first; requests it does not match fall through to web. See Routing and priority.
How path matchers compare
Section titled “How path matchers compare”Path(`/api`)matches exactly/api.PathPrefix(`/api/`)matches every path that starts with the string/api/.PathRegexptakes a regular expression. It is not anchored: write^and$when you mean the whole path.
PathPrefix compares plain strings, not path segments. PathPrefix(`/api`) matches /api/users, and also /apiv2 and /api-docs. That is why the route above uses Path(`/api`) || PathPrefix(`/api/`): it takes /api and everything below it, and nothing else.
Normalised paths
Section titled “Normalised paths”Before routing, ReverseDock normalises the path:
- Percent-escapes of unreserved characters are decoded (
%7ebecomes~); other escapes are written in upper case (%2fbecomes%2F). - Dot segments are removed and repeated slashes collapsed:
/a/./b/../cbecomes/a/c,//apibecomes/api.
The normalised path is the one matched and the one forwarded, so the backend sees exactly the path that was routed. A request for /static/../api/users is routed and forwarded as /api/users.
For matching, ReverseDock decodes the path except for reserved characters such as / and ?, and %25. PathPrefix(`/foo bar`) therefore matches /foo%20bar, but an encoded slash is never a separator: PathPrefix(`/a/b`) does not match /a%2Fb.
A path with an invalid percent-escape, or a request target with a # fragment, is answered with 400 Bad Request.
Remove the prefix
Section titled “Remove the prefix”The strip-api middleware removes /api before the request goes to the API service:
| Client asks for | API receives |
|---|---|
/api |
/ |
/api/users |
/users |
/api/users?page=2 |
/users?page=2 |
prefixes is a list; the first prefix the path starts with is removed. The removed prefix is sent to the backend in X-Forwarded-Prefix, so an application can build links that include it.
Pair strip_prefix with a rule that only matches the prefix as a whole segment, as above. With PathPrefix(`/api`) alone, /apiv2 would match and arrive at the API as /v2.
If removing the prefix leaves a path that is not in normalised form (for example /api./x stripped of /api), the request is answered with 400, so the backend never sees a path that differs from what was routed.
To add a prefix instead, or to replace or rewrite the path, use add_prefix, replace_path or rewrite. See Redirects and rewrites.
Check that it works
Section titled “Check that it works”curl -s -o /dev/null -w '%{http_code}\n' https://app.example.com/api/usersThe API’s access log should show a request for /users. In ReverseDock’s access log, the line has "route":"api" and path as the client sent it.
curl -s -o /dev/null -w '%{http_code}\n' https://app.example.com/apiv2This request goes to the web service, because /apiv2 is neither /api nor under /api/.