Files
RustProxy/docs/CUSTOM_LOCATIONS.md
T
2026-10-08 22:22:46 +02:00

8.5 KiB

Custom locations

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.

Routing model in 0.16.0

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:

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.

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

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.

Supported path transformations:

  • 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.

The query string is preserved. Rewrites change only the selected source path; reverse-proxy cache identity remains based on the public client URL.

Web Server

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.

Local response

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.

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:

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:

{
  "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 0.29.1 implementation note.

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.