v0.34.0_fina;

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-18 12:00:59 +02:00
1 parent 1ca019d849
commit c207e8a03f
69 files changed
+1496 -13838

No files matched your search

+24 -115
View File
@@ -1,132 +1,41 @@
# Custom locations
# Custom locations and path rules
RustProxy 0.22.3 supports the common location-level controls that are typically used for application reverse proxies. The UI intentionally exposes a safe subset instead of accepting arbitrary Nginx configuration text.
Custom locations add exceptions to a Vhost's normal source. They are **not** required for the root path `/`: any request not matched by a custom rule continues to the Vhost's main backend pool or Web Service. Configure them under **Vhosts → Edit → Custom locations**.
## Routing model in 0.16.0
## Path matching
The vhost's main backend pool is an internal fallback and is **not** one of the Custom locations. Custom locations are optional overrides only. Request routing is therefore:
| Match mode | Purpose | Priority |
|---|---|---|
| `exact` | One exact normalized path | Highest |
| `prefix_priority` | Longest prefix that suppresses regex evaluation | Ahead of regex when matched |
| `regex` / `regex_insensitive` | Case-sensitive or insensitive regular expression | Tested in configured order after priority-prefix rules |
| `prefix` | Ordinary longest matching path prefix | Fallback among matching rules |
```text
exact custom location
-> priority/regex/prefix custom location according to the documented precedence
-> main vhost backend pool
```
An empty Custom locations list is valid. Adding `/test`, a regex, a local response or a dedicated-backend location cannot disable `/`, `/abc` or other unmatched paths. Older saved documents that contain the former untouched synthetic `root` prefix `/` entry are normalized automatically. If `/` was deliberately customized with a backend, cache/ACL/header rule, rewrite or local response, it remains a real Custom location and intentionally overrides the main fallback.
## Dashboard editor
The visual editor is the primary workflow. Every location card now shows a live **Effective behavior preview** at the bottom with the match mode/pattern, proxy or local-response action, selected backend, rewrite behavior, cache policy, ACL composition and request/response header-rule counts. The preview is calculated from the unsaved form state so an administrator can verify intent before saving.
Request and response header rules use numbered visual cards with localized **Operation / Header name / Header value** controls. `Set`, `Append` and `Remove` are supported. The Advanced JSON view remains available for automation/copy-paste, but is no longer required for normal editing. The documentation button in the location tab explains precedence, rewrites, local responses, policy composition and common examples.
## Match modes
Each location has an ID, a pattern and one match mode:
| RustProxy mode | Nginx-style equivalent | Behaviour |
| --- | --- | --- |
| `prefix` | `location /path/` | Longest matching path prefix. A matching regex may still win. |
| `prefix_priority` | `location ^~ /path/` | Longest prefix and, when selected, skips regex locations. |
| `exact` | `location = /path` | Exact normalized path only. Exact matches have highest priority. |
| `regex` | `location ~ pattern` | Case-sensitive Rust regular expression. Regex locations are tested in configuration order. |
| `regex_insensitive` | `location ~* pattern` | Case-insensitive Rust regular expression. Regex locations are tested in configuration order. |
Routing and ACL checks collapse repeated literal slashes, matching Nginx's usual `merge_slashes on` behaviour. For example `/static/js//events.js` is routed as `/static/js/events.js`. Unless a rewrite is configured, the original request target is still sent to the backend.
Encoded separators (`%2f`, `%5c`), encoded `%`, dot segments and other ambiguous path forms are still rejected instead of being silently normalized.
Ambiguous and unsafe encoded path forms are rejected rather than treated as alternative spellings. Avoid overlapping regex rules when order matters; test paths with and without trailing `/`. A Vhost's main source handles paths without a matching custom location.
## Actions
A location can proxy to a backend, serve an existing Web Service, return a local response, or redirect the client to another absolute HTTP(S) URL.
- **Proxy:** use the Vhost backend pool or target a specific backend. A location can have an explicit cache override.
- **Web Server:** reference an existing Web Service with its saved document root and PHP/static settings.
- **Local response:** return a chosen `200–599` status, content type and body without connecting to an origin. `HEAD`, `204` and `304` do not return a body.
- **Redirect:** issue `301`, `302`, `303`, `307` or `308` to a valid absolute HTTP(S) target. Query preservation is configurable. Redirect rewrite expressions may reference regex captures.
### Proxy
### Path rewriting
The location can use the vhost pool or pin traffic to one backend. A dedicated backend can be created from the location editor. Cache policy can inherit from the vhost or be explicitly enabled/disabled for the location.
`preserve` forwards the original path, `strip_prefix` removes the matched prefix, `replace_prefix` substitutes a new normalized prefix and `regex` rewrites using captures such as `$1` or `${name}`. Queries remain intact unless a redirect explicitly changes handling. Public cache keys are based on the client-facing URL, not an internal rewritten backend path.
Supported path transformations:
**Example:** send `example.com/api/*` to a dedicated application while `/` remains on the main website. Create a `/api/` prefix location, select its backend and choose whether to preserve or strip `/api/`. Inspect the editor's effective-behavior preview before saving.
- **Preserve** — send the original client path unchanged.
- **Strip prefix** — remove the matched prefix before proxying.
- **Replace prefix** — replace the matched prefix with another normalized path prefix.
- **Regex rewrite** — available for regex locations; Rust regex captures can be referenced as `$1` or `${1}` in the replacement.
## Headers, access and method rules
The query string is preserved. Rewrites change only the selected source path; reverse-proxy cache identity remains based on the public client URL.
Each location can add request/response header operations (`set`, `append`, `remove`), reusable ACL profiles, inline IP/CIDR rules, country access and HTTP-method allow/block lists. Every applicable host and Vhost policy must still pass; a location cannot remove a higher-level denial. Transport/forwarded-identity headers are protected from unsafe overrides. Authentication, rate limits, WebSocket policy and other Vhost settings continue to apply.
### Web Server
## Internal error fallback
`Web Server` selects one reusable service previously created in the **Web Server** workspace. The location stores only the Web Service ID; document root, index list and directory-listing policy remain in that shared service. Existing location matching and path rewriting are reused, but backend pinning and reverse-proxy object-cache controls do not apply to local files. See [WEB_SERVER.md](WEB_SERVER.md).
Mark a location **Internal** to make it unavailable to ordinary public URI matching. A Vhost or location may direct specific origin responses (`403`, `404`, `500`, `502`, `503`, `504`) to a named internal location ID in the same Vhost. The dispatch is internal, not a browser redirect; request context and access evaluation are preserved. Invalid targets or loops are rejected.
### Local response
If all you need is a different error **page body**, use **Vhost → Pages → Intercept application error responses**, which keeps the original status without making a second origin request.
`Return custom response` is the equivalent of a simple Nginx `return`: choose an HTTP status from 200 through 599, a Content-Type and a response body. RustProxy does not open a backend connection for this action. `HEAD`, `204` and `304` responses do not send a body.
## Not an Nginx configuration parser
### Redirect
`Redirect` is a backend-less action. Choose `301`, `302`, `303`, `307` or `308`, set an absolute `http://` or `https://` target, and optionally preserve the incoming query string. RustProxy validates the target and does not allow capture expansion inside the target scheme/authority.
For capture-based path rewriting, configure a separate **Redirect rewrite regex**. Captures can be referenced as `$1` or `${name}` in the redirect target path/query. This separation mirrors an Nginx `location` selector followed by a separate `rewrite` expression. For example, the Nginx pattern:
```nginx
location ~* \.(?:tar\.gz|zip?)$ {
rewrite ^/(.*)$ https://objectstorage.example.invalid/b/repo/o/$1 permanent;
}
```
corresponds to a case-insensitive regex Custom location `\.(?:tar\.gz|zip?)$`, action **Redirect**, status `301`, target `https://objectstorage.example.invalid/b/repo/o/$1`, and redirect rewrite regex `^/(.*)$`. Query preservation is controlled independently.
## Request and response headers
Every location has a visual header configurator and an Advanced JSON view. Both edit the same data.
Supported operations are:
- `set` — replace the header with the configured value;
- `append` — append another header value;
- `remove` — remove the header.
Example JSON:
```json
{
"request": [
{"op": "set", "name": "X-App-Route", "value": "api"}
],
"response": [
{"op": "set", "name": "Cache-Control", "value": "no-store"},
{"op": "remove", "name": "X-Legacy-Header"}
]
}
```
Hop-by-hop/framing headers, forwarding identity headers, RustProxy request IDs and WebSocket handshake headers are protected and cannot be overwritten by custom rules. This prevents a location rule from changing transport framing or spoofing the verified client identity.
## Access control
A location can define its own IPv4/IPv6 CIDR ACL, select a reusable ACL profile, add a location-specific country block/allow-only rule, and configure an HTTP method policy (`off`, `allow` or `block`). Method names are normalized to uppercase and validated as HTTP method tokens.
All access layers are additive. The request must pass the vhost method policy, location method policy, host/profile/vhost IP ACLs, location profile ACL, inline location ACL and every applicable country policy. A location therefore cannot override a denial from its vhost. Method denial returns `405 Method Not Allowed` (with `Allow` for allow-list mode); IP/country denials retain the normal `403` pipeline. These checks occur before backend selection/connection.
Authentication, vhost rate limits, security headers, compression, custom error pages and WebSocket policy still apply through the normal vhost pipeline.
## Internal locations and error fallbacks
RustProxy 0.29.1 can mark a Custom location as `internal`. An internal location is addressed by its stable location `id` and is never selected by a public URI match. The vhost main source or another Custom location can use `error_fallback` to internally dispatch configured `403/404/500/502/503/504` responses to that target. The default intercepted status is `404`.
This is an internal request handoff, not an HTTP redirect. The original method, URI/query and request context are retained. The fallback target re-enters normal access/header/cache/compression/response processing. Fallback targets must be enabled internal locations in the same vhost; validation rejects cycles and runtime dispatch is depth-limited. See [current vhost documentation](VHOSTS.md).
### Replace origin error bodies without an internal location (0.29.7)
The independent, optional **Vhost → Pages → Intercept application error responses**
setting (`pages.intercept_statuses`, for example `[404, 503]`) replaces a
backend or Web Server/PHP origin's error *body* with the configured vhost
error page, then global error page, or a built-in page. It retains the HTTP
status and sets `Cache-Control: no-store`. Unlike `error_fallback`, it does
**not** forward the request to a different backend. The existing internal
`error_fallback` action has priority and its final response remains intact.
See [Web Server – application HTTP error interception](WEB_SERVER.md#intercept-application-http-errors-0297).
## What is intentionally not an Nginx parser
Custom locations do **not** execute arbitrary Nginx directives or configuration snippets. RustProxy does not currently implement location-level `try_files`, per-location filesystem aliases/roots, embedded scripting, arbitrary `if` expressions or arbitrary module directives. Static roots are defined only by reusable Web Services. Use the explicit UI controls and the documented JSON schema instead; unknown fields are rejected on save.
The editor does not run arbitrary Nginx snippets, filesystem aliases, arbitrary `if` rules or per-location static roots. Configure static paths in a reusable Web Service, then assign that service to the location. Inspect [Vhosts](VHOSTS.md) and [Access](ACCESS.md) for the policies applied before connecting to the backend.