409 lines
27 KiB
Markdown
409 lines
27 KiB
Markdown
> 0.7 adds graphical locations, Basic Auth, cumulative IP ACL editing and a
|
|
> WebSocket toggle. See [host access and locations](VHOST_ACCESS.md).
|
|
|
|
# Virtual hosts, frontend bindings and backend pools
|
|
|
|
This is the source implementation contract. Rust compilation and runtime acceptance
|
|
were not performed in the packaging environment; see TEST_REPORT.md.
|
|
|
|
## One vhost, one backend pool
|
|
|
|
A `vhosts` entry owns an `id`, `enabled`, `frontend`, `source`, `backends`, `load_balancing`,
|
|
`routes`, optional `acl`, `headers` and `security_headers`. `source` defaults to Reverse Proxy
|
|
for older documents; it can instead reference an independently configured Web Service. See
|
|
[WEB_SERVER.md](WEB_SERVER.md). The pre-production top-level
|
|
routing and inline-listener formats are no longer accepted. Unknown JSON fields are
|
|
rejected. The examples are complete documents, not JSON merge patches:
|
|
|
|
- `examples/vhosts-private.json`: explicit loopback IP, two existing HTTP origins,
|
|
adaptive balancing, HTTP health checks, keepalive, no cache or cookie affinity.
|
|
- `examples/vhosts-balanced.json`: explicitly public HTTP 80 and TLS 443, cookie
|
|
affinity and a deliberately short HSTS value; requires a trusted, imported `app`
|
|
certificate/key and real origins. It does not create those services or files.
|
|
|
|
In the examples, both origins need `/health` to return a quick 2xx response. Adjust
|
|
that path/status range for the real service. New pools start quarantined until
|
|
`rise` successful checks; an unavailable or misconfigured check deliberately causes
|
|
503 instead of sending traffic to an unverified member. `mode: off` opts out.
|
|
|
|
The editor includes frontends, members, balancing, health, connection reuse, sticky
|
|
sessions and response protection. Custom locations have graphical match/action/rewrite/ACL/header
|
|
controls plus synchronized Advanced JSON for automation. Unsaved drafts survive translation/SSE refreshes. A stale draft must be
|
|
reloaded before saving: the panel does not silently merge competing revisions.
|
|
|
|
Existing vhost IDs can be renamed in the dashboard as long as the new ID is valid and
|
|
unique. The rename keeps the configured frontend hostnames, routes and backend IDs, but it
|
|
intentionally starts a new vhost runtime identity for metrics/log attribution and current
|
|
health/traffic history. Because the vhost configuration participates in cache compatibility,
|
|
a rename also rotates that compatibility identity and invalidates incompatible cached objects.
|
|
The editor warns before applying the rename.
|
|
|
|
## Frontend selection and private IP isolation
|
|
|
|
`frontend.hosts` is a normalized hostname-pattern list. Exact hostnames and a single
|
|
leading wildcard such as `*.example.com` are supported; regex and mid-label wildcards
|
|
are not. Routing tries the exact hostname first, then the longest matching wildcard.
|
|
This means `api.example.com` can have its own vhost while `*.example.com` handles other
|
|
subdomains on the same listener without stealing the exact route.
|
|
`frontend.listener_ids` references named `listeners.endpoints` configured under
|
|
Settings. A listener owns its IP:port, TLS flag and H2/H3 transport capabilities;
|
|
a vhost never stores a listening address directly. For a new dashboard vhost, the
|
|
editor preselects both an enabled HTTP listener on port 80 and an enabled TLS listener
|
|
on port 443 when they exist (public-scope listeners are preferred). Existing vhost
|
|
bindings are never replaced by that convenience default.
|
|
|
|
`force_ssl` defaults to false. When enabled, any cleartext request that matches this
|
|
vhost receives HTTP `308 Permanent Redirect` to the same normalized hostname and
|
|
original path/query. At least one selected plain HTTP listener and one selected TLS
|
|
listener are required so there is both an ingress to redirect and an HTTPS target. If
|
|
several TLS ports are selected, port 443 is preferred; otherwise the selected TLS target
|
|
port must be unique.
|
|
|
|
`forward_client_ip` defaults to true. Normal vhosts therefore receive the sanitized
|
|
`Forwarded`, `X-Forwarded-For` and `X-Real-IP` client identity generated by RustProxy.
|
|
Set it to false for applications whose own trusted-proxy/bypass logic must see the
|
|
RustProxy TCP peer instead. Only the backend application request loses those three
|
|
client-IP headers; ACLs, GeoIP, rate limiting and Tinyauth still use the effective client
|
|
address, and origin metadata such as `X-Forwarded-Proto`, `X-Forwarded-Host`,
|
|
`X-Forwarded-Port` and `X-Forwarded-URI` remains available to the backend.
|
|
|
|
`websockets` enables WebSocket handling for the vhost. The dashboard defaults new WebSocket
|
|
vhosts to the nginx-compatible HTTP/1.1 Upgrade transport. `websocket_legacy_support: true` is
|
|
a separate per-vhost compatibility switch for older appliances and embedded servers. It affects
|
|
only WebSocket handshakes: RustProxy forces an HTTP/1.1 upstream WebSocket hop, uses the configured backend authority as upstream `Host`,
|
|
rewrites `Origin` to the matching upstream scheme + backend authority, and tolerates legacy `101 Switching Protocols` response
|
|
framing while keeping the `Sec-WebSocket-Accept` proof and offered subprotocol checks strict.
|
|
Ordinary HTTP requests are unchanged. `websocket_rfc8441: true` and `websocket_rfc9220: true` are
|
|
separate opt-ins for HTTP/2 and HTTP/3 Extended CONNECT and are valid only when WebSocket
|
|
support is enabled. They are presented together in the vhost editor.
|
|
RFC 8441 requires an H2-capable selected listener (TLS ALPN or cleartext h2c prior knowledge).
|
|
Because the RFC 9220 advertisement is QUIC-connection scoped, one opted-in vhost causes the
|
|
shared QUIC listener to advertise Extended CONNECT, while request routing still rejects RFC
|
|
9220 on non-opted-in vhosts.
|
|
|
|
`http2_enabled` and `http3_enabled` are per-vhost opt-outs (both default true). The
|
|
panel presents the two switches together and enables each only when at least one
|
|
selected listener provides that protocol. For TLS H2, ClientHello/SNI selects H2+HTTP/1.1 or HTTP/1.1-only ALPN for
|
|
the matched vhost. H3 remains a shared UDP listener capability; disabling it on a
|
|
vhost removes Alt-Svc and rejects H3 for that host.
|
|
|
|
Each vhost can optionally set `tls_policy` with `tls12`, `tls13` and a list of rustls
|
|
cipher-suite names. Omitting it inherits `tls.policy` from Settings. The override is
|
|
selected from ClientHello/SNI before the TCP TLS handshake, so a vhost can for example
|
|
run TLS 1.2 only while another frontend continues to use the global TLS 1.2/1.3 policy.
|
|
At least one protocol version and a compatible effective cipher suite are required.
|
|
Because HTTP/3 shares QUIC TLS on the UDP listener and requires TLS 1.3, a vhost with
|
|
HTTP/3 enabled must use the same TLS policy as the global setting. Disable HTTP/3 on
|
|
that vhost before using a differing per-vhost TLS policy.
|
|
|
|
Routing checks Host/HTTP authority, actual accepted local IP, local port and actual
|
|
TLS state. Neither `Forwarded` nor `X-Forwarded-*` can create frontend access. A
|
|
request for a private hostname on a different local IP is not routed to that host.
|
|
Unknown or unmatched hosts return 404.
|
|
|
|
Bindings must be local addresses assigned to the OS. The process does not add IPs,
|
|
manage routes, configure firewall rules, acquire virtual IPs or run Keepalived/VRRP.
|
|
A missing IP, occupied port or permission error aborts startup without wildcard
|
|
fallback. Low ports may need deployment capabilities; see `../deploy/README.md`.
|
|
|
|
Identical or same-family wildcard sockets are consolidated only when the named
|
|
listener transport capabilities agree. Vhost routing restrictions are not
|
|
consolidated: a wildcard TCP listener can accept on a public IP while still rejecting
|
|
a private vhost there. Per-vhost H2 is narrowed by SNI and therefore does not require
|
|
a second TCP socket. IPv4/IPv6 wildcard overlap is rejected; use explicit addresses.
|
|
Native IPv4 binds are required instead of mapped IPv6 literals. The administration
|
|
port is reserved even on a different IP.
|
|
|
|
IP isolation is not client authentication. A firewall, router, NAT or preceding
|
|
proxy can forward external clients to the private destination IP; RustProxy sees
|
|
that local destination and the connecting peer. Use network ACLs, firewall rules
|
|
and application authentication too. Trusted forwarding headers have a separate explicit CIDR policy (see
|
|
INTEGRATIONS_040.md); PROXY protocol is not implemented. Interface changes require config
|
|
reload/restart to refresh the upstream loop-guard's local-address enumeration.
|
|
|
|
The same literal hostname pattern may occur only where its listener bindings remain
|
|
unambiguous. Exact and wildcard patterns are distinct ownership keys, so an exact host
|
|
and its parent wildcard may coexist. Disabled vhosts reserve their patterns and are
|
|
still validated, but create no pool or additional active listener.
|
|
|
|
## Paths, policies and migration
|
|
|
|
`routes` contains per-vhost paths: `id`, match fields, `enabled`, `source`, nullable `cache`,
|
|
optional `acl_profile_id`, inline `acl`, `country_access`, `method_policy`, `headers` and `backend_id`. `source` selects Reverse Proxy or a reusable Web Service;
|
|
`backend_id` is valid only for Reverse Proxy. An empty array leaves the main vhost source as the fallback. Longest
|
|
segment-boundary prefix wins. Origins receive the original path/query; prefixes
|
|
are not stripped. IDs must use ASCII letters, digits, hyphens and underscores.
|
|
Host method policy, reusable ACL profiles, vhost ACL/country policy and route method/profile/inline
|
|
ACL/country policies are cumulative before pool selection/DNS/cache. A vhost and each route can
|
|
attach a reusable `acl_profile_id`; profiles may contain an IP ACL plus a country block/allow-only
|
|
rule. Inline controls remain independent and cannot be weakened by a profile. Method-policy denial
|
|
returns 405; allow-list mode also emits `Allow`. Their client identity is the
|
|
validated effective peer address, not an untrusted forwarding header.
|
|
|
|
There is no legacy routing UI/runtime or automatic importer in 0.8.0. Development
|
|
databases using the old top-level routes or inline frontend listeners must be reset
|
|
or migrated explicitly before startup. An omitted path backend ID selects from the
|
|
balanced pool; a specified ID must belong to the same vhost and does not fail over to a
|
|
different application's member. Imported health checks start OFF. Ambiguous names,
|
|
unreferenced old backends and invalid/oversized documents stop migration before
|
|
saving. See MIGRATION.md before updating an existing installation.
|
|
|
|
## Backend members and connection reuse
|
|
|
|
Each member has `id`, HTTP(S) origin `url`, `weight` (1..256), `enabled` (true),
|
|
`backup` (false), `drain` (false), `allow_private` (false),
|
|
`accept_local_listener_risk` (false), `preserve_host` (true), `protocol` (`http1`),
|
|
`address_allowlist`, optional `tls_server_name`, `tls_verify` (false) and optional
|
|
`tls_ca_pem`. `tls_server_name` is an HTTPS-only DNS hostname
|
|
used for SNI and, while verification is enabled, certificate hostname verification;
|
|
DNS/TCP still targets the configured `url`. `tls_verify` defaults to false. HTTPS connections remain encrypted, but backend certificate authentication is skipped until an administrator explicitly sets `tls_verify: true`; this includes compatibility with legacy certificate encodings that modern webpki validation rejects. Address/DNS policy remains enforced; local-listener self-targeting has only the explicit risk exception described below. `tls_ca_pem` is ignored while verification is disabled.
|
|
Backend IDs are unique inside a vhost. URLs cannot contain credentials, paths,
|
|
queries or fragments.
|
|
Every DNS answer and every configured literal must pass the same upstream policy
|
|
for health probes and forwarding. Active, pending and prior-snapshot frontend
|
|
addresses and local administration are guarded against self-proxying by default.
|
|
For an intentional local target such as `http://127.0.0.1:9090`, enable both
|
|
`allow_private: true` and `accept_local_listener_risk: true`. The latter is a narrow,
|
|
explicit acceptance of loop/control-plane exposure risk; metadata/link-local and other
|
|
special-address blocks are not bypassed. In the dashboard this risk checkbox is hidden
|
|
unless the entered backend URL directly targets an administration listener reported by
|
|
persisted configuration or live status. Startup-owned/locked administration sockets are
|
|
therefore handled too. On the same administration port, loopback spellings such as
|
|
`localhost`, `127.0.0.1` (and the rest of `127.0.0.0/8`) and `::1` are treated as
|
|
equivalent for showing the explicit risk control, so it is not presented as a normal
|
|
backend option.
|
|
|
|
A vhost can independently enable two proxy-owned response diagnostics:
|
|
|
|
- `backend_header: true` -> `X-RustProxy-Backend: <backend-id>`,
|
|
- `backend_timing_header: true` -> `X-RustProxy-Backend-Time: <milliseconds>ms`.
|
|
|
|
The timing value covers the origin request through receipt of the backend response
|
|
headers; it is not full response-body duration. Both proxy-owned names are protected
|
|
from custom header rules and any upstream values are removed before RustProxy optionally
|
|
writes its own value. The former `X-Rust-Proxy-Backend-Time` spelling is also stripped
|
|
as a legacy lookalike but is never emitted. Responses served without contacting an
|
|
origin do not expose a selected backend or stale origin timing.
|
|
`timing_headers` remains the separate opt-in `Server-Timing` diagnostic.
|
|
|
|
`keepalive` controls **connection pooling**, not an HA service:
|
|
|
|
| Field | Default | Accepted range |
|
|
|---|---|---|
|
|
| `enabled` | true | Boolean |
|
|
| `idle_timeout_seconds` | 30 | 1..3600 |
|
|
| `max_idle_per_host` | 4 | 1..64 |
|
|
| `tcp_keepalive_seconds` | 30 | 1..3600 |
|
|
|
|
These configure HTTP idle reuse and TCP keepalive; HTTP/2 also uses pings for active
|
|
connections, with a 10-second ping timeout and no pinging solely to keep idle
|
|
connections alive. Disabling reuse sets the idle pool limit to zero and disables
|
|
those keepalive probes. It does not abort already active requests/tunnels. The idle
|
|
limit is not a separate per-backend active-request cap; global connection/exchange
|
|
bounds still apply. HTTP health probes use separate no-reuse HTTP/1.1 connections.
|
|
|
|
`http2` over HTTPS prefers h2 but keeps HTTP/1.1 ALPN compatibility. Over plaintext,
|
|
`http2` remains explicit prior-knowledge h2c with no retry-based downgrade. `auto`
|
|
over HTTP is HTTP/1.1. `http2`/`auto` require `preserve_host: false` to keep origin
|
|
URI/authority/TLS identity consistent. HTTPS backend certificate verification is disabled when `tls_verify` is omitted. Enable `tls_verify: true` when origin identity must be validated; for IP-based origins use a DNS `tls_server_name` present in the certificate and provide `tls_ca_pem` when the issuer is private.
|
|
|
|
## Scheduling
|
|
|
|
| `algorithm` | Decision |
|
|
|---|---|
|
|
| `round_robin` | Smooth weighted round-robin |
|
|
| `least_connections` (default) | Lowest `(active exchanges + 1) / effective weight` |
|
|
| `least_traffic` | Lowest recent EWMA payload rate plus active-exchange allowance, divided by effective weight |
|
|
| `adaptive` | Combines active exchanges, EWMA time-to-headers and payload rate, divided by effective weight |
|
|
| `ip_hash` | Weighted rendezvous hash of vhost/member and canonical actual peer IP |
|
|
|
|
An active exchange spans upload, response body and upgraded tunnel, not just the
|
|
wait for response headers. Slow-start gradually increases effective weight after
|
|
initial success/recovery (`slow_start_seconds`: 10, allowed 0..300; 0 disables it).
|
|
IP hashing uses configured weights instead of slow-start to keep assignments stable.
|
|
These are explicit heuristics, not workload predictions or benchmark-backed claims.
|
|
No remote host CPU measurement is used by the scheduler.
|
|
|
|
Only enabled, healthy, non-draining primary members receive new assignments. If
|
|
none exists, eligible backups are used. All unavailable means 503 with Retry-After;
|
|
there is no fallback into another vhost. Balanced routes may retry a body-free `GET`,
|
|
`HEAD` or `OPTIONS` on another healthy member according to `load_balancing.retry`:
|
|
|
|
| Retry field | Default | Accepted range / meaning |
|
|
|---|---|---|
|
|
| `max_retries` | 1 | 0..3; zero disables failover retry |
|
|
| `retry_connect_errors` | true | Retry connection establishment failures |
|
|
| `retry_timeouts` | true | Retry the response-header timeout |
|
|
| `retry_statuses` | `[502,503,504]` | Up to 8 HTTP 5xx statuses |
|
|
|
|
A backend already attempted in the same exchange is excluded from later attempts.
|
|
Path-pinned routes and WebSocket handshakes are not retried. POST/PUT/PATCH and any
|
|
request with a body are never automatically replayed. Failed attempts still feed the
|
|
normal passive-health policy and retry counters/logs; successful failover updates sticky
|
|
affinity to the backend that actually answered.
|
|
|
|
## Health checks and recovery
|
|
|
|
| Field | Default | Accepted range / meaning |
|
|
|---|---|---|
|
|
| `mode` | `tcp` | `off`, `tcp`, `http` |
|
|
| `path` | `/` | Leading slash, up to 1024 bytes; no authority/fragment |
|
|
| `host` | null | Optional validated HTTP Host override |
|
|
| `interval_ms` | 5000 | 250..300000 |
|
|
| `timeout_ms` | 2000 | 50..60000; no greater than interval |
|
|
| `slow_response_ms` | 1500 | 0 disables; otherwise no greater than timeout |
|
|
| `fall` / `rise` | 3 / 2 | 1..20 consecutive failed/successful checks |
|
|
| `expected_status_min` / `max` | 200 / 399 | Inclusive, ordered HTTP status range within 100..599 |
|
|
|
|
TCP checks verify that an approved origin address accepts a connection. HTTP checks
|
|
perform GET on the configured path, without redirects, cookies or authorization.
|
|
They use HTTP/1.1, including verified TLS when appropriate, and measure time to
|
|
headers, not the full body. For an h2c-only service, use TCP checks. Slow probes
|
|
count as active failures. Active TCP/HTTP health is independent from `passive_health`.
|
|
|
|
`passive_health.enabled` defaults to `false`. With that default, normal user traffic
|
|
never changes the shared backend healthy/unhealthy state: HTTP 5xx, connection errors,
|
|
timeouts, response-body I/O failures and slow responses are still counted/logged but
|
|
cannot quarantine the member. When explicitly enabled, the administrator can choose
|
|
`count_connect_errors`, `count_timeouts`, `count_io_errors`, `count_http_5xx` and
|
|
`count_slow_responses`, with optional passive `fall`/`rise` overrides. Missing overrides
|
|
inherit the active health thresholds. Active and passive streaks are separate, so a
|
|
successful user request cannot erase an active-probe failure streak and an active probe
|
|
cannot erase a passive failure streak. `mode: off` disables only active probes; an
|
|
explicitly enabled passive policy may still quarantine a backend.
|
|
|
|
One cancellation-aware supervisor schedules at most 16 probes, one per member,
|
|
with a rotating window for fairness. The tick is 250 ms; configured intervals are
|
|
best-effort under resource pressure, not precise service-level guarantees. DNS
|
|
workers have their own bounded slots. Probe bodies are not collected or cached.
|
|
|
|
## Sticky sessions and drain
|
|
|
|
`sticky.mode` is `off`, `cookie` or `ip_hash`. IP mode overrides the normal algorithm
|
|
using the actual peer IP. Users behind one NAT/proxy share that identity.
|
|
|
|
Cookie mode uses a random 32-character opaque member token, not a backend URL.
|
|
The default name is `RP_AFFINITY_<vhost-id>`; custom names accept 1..96 ASCII letters,
|
|
digits, underscores or hyphens and cannot use the admin session name. TTL defaults
|
|
to 3600 seconds (60..604800), with Path=/, HttpOnly, SameSite=Lax and Secure on TLS.
|
|
`__Host-`/`__Secure-` names require every frontend to use TLS. Cookies are host-only,
|
|
so aliases have independent browser cookies. They do not authenticate users.
|
|
|
|
Cookie-affine requests always bypass shared cache. The dedicated routing cookie
|
|
is stripped before forwarding; origins cannot override it via Set-Cookie. Unknown,
|
|
duplicate or unhealthy-target tokens are ignored/reassigned. Existing valid cookie
|
|
sessions may continue on a healthy draining/backup member; new sessions do not
|
|
choose a draining member. Disabling a member prevents all new assignments,
|
|
including existing cookie sessions, while already admitted exchanges finish.
|
|
|
|
Affinity tokens, health and traffic observations survive compatible hot edits.
|
|
A changed origin/health policy or re-enabled member is checked afresh. Process
|
|
restart resets tokens/counters; clients can be reassigned. Session data itself is
|
|
never replicated: applications needing durable sessions require a shared store.
|
|
|
|
## Response protection
|
|
|
|
`security_headers` supports `hsts` (`enabled`, `max_age_seconds`,
|
|
`include_subdomains`, `preload`), `nosniff`, `frame_options` (`preserve`, `deny`,
|
|
`sameorigin`), `referrer_policy`, `content_security_policy` and `permissions_policy`.
|
|
Header values are validated against splitting and bounded to 8192 bytes.
|
|
|
|
HSTS is configured only for TLS responses. For a matched vhost, plaintext responses
|
|
remove Strict-Transport-Security even if an origin/custom rule adds it. On TLS,
|
|
disabled HSTS means no forced replacement of an origin value; use a custom remove
|
|
rule to suppress that value. Enabled HSTS overrides generic rules. `max_age_seconds`
|
|
accepts 0..63072000 (default31536000); setting zero is available for explicit expiry.
|
|
Preload is opt-in and requires enabled HSTS, includeSubDomains and at least one year.
|
|
This only emits the directive; it does not register a hostname in a preload list.
|
|
Start with a short lifetime and verify every affected hostname before increasing it.
|
|
|
|
Request rules run global -> vhost -> path, then protected identity fields are
|
|
regenerated. Response rules run global -> vhost -> path, followed by dedicated
|
|
security/Server policy and optional compression. Security headers cover cache hits
|
|
and matched-host synthetic errors/ACL denials too. Protected transport fields
|
|
remain uneditable. `false`, `preserve` or null security settings otherwise leave
|
|
origin values unchanged. CSP and Permissions-Policy must suit the application;
|
|
RustProxy validates header encoding, not the semantic correctness of those policies.
|
|
|
|
## Live state, cache and limits
|
|
|
|
The dashboard defaults a newly created vhost to `cache.enabled: false`. When the
|
|
effective cache state is disabled, the editor hides the remaining per-vhost cache
|
|
controls; enabling cache (or explicitly choosing inheritance from an enabled global
|
|
cache) reveals them. This is a UI default for new drafts, not a migration that rewrites
|
|
existing vhost cache settings.
|
|
|
|
|
|
`GET /api/backends` requires an admin session and returns `pools`,
|
|
`active_frontends`, `desired_frontends`. `/api/status` and SSE contain `backend_pools`
|
|
and complete active/desired endpoint plans. The dashboard renders per-member state,
|
|
active exchanges, request/error totals, EWMA header latency and payload traffic.
|
|
Prometheus adds bounded vhost/member labels; access logs include `backend` as
|
|
`vhost/member` for actual pool forwarding (null when no upstream exchange is made).
|
|
|
|
Bytes are observed payload, not NIC throughput; response counters include the
|
|
client-delivered encoded representation. Header latency includes connection/setup and any request upload; it is not full
|
|
exchange time. Active probes use body-free requests for an independent measurement.
|
|
Health mode off is labeled as checks off, not proof of a probe passing. Statistics
|
|
are observations, not persisted configuration. They reset on process restart.
|
|
|
|
Cookie affinity bypasses cache entirely. A pool with no eligible member does not invent
|
|
an unselected cached fallback. Config changes invalidate incompatible cache state.
|
|
RFC 5861/request `max-stale` behavior is owned by the shared cache and does not alter
|
|
backend member selection; request coalescing remains separate from stale-while-revalidate
|
|
deduplication.
|
|
|
|
Limits: 64 vhosts; hostname lists are not capped by an application-level item count;
|
|
listener entries remain capped at 16 per host, with 32 members and 64 paths per host;
|
|
128 backend definitions and 256 paths across all vhosts;
|
|
128 additional declared frontend entries plus two globals; 256 KiB serialized
|
|
configuration. Existing request/connection/DNS/queue bounds remain in force.
|
|
|
|
Every successful config publication rebuilds upstream connection pools. Compatible
|
|
member observations/tokens and in-flight exchanges survive, but idle sockets are
|
|
not retained across publication. Listener/address/ALPN changes are saved immediately
|
|
and require restart; routing changes apply to existing bound sockets immediately.
|
|
When moving a host away from an old IP, it can become unavailable until restart
|
|
rather than being served on an address no longer permitted by desired policy.
|
|
|
|
## Per-vhost operations in 0.3.0
|
|
|
|
The compact list opens a tabbed editor rather than displaying a full form beside
|
|
the list. Health summaries update on the list and editor without replacing unsaved
|
|
form controls. Header rules have a visual request/response editor; Advanced JSON
|
|
remains available and stays synchronized with those rows.
|
|
|
|
The SSL tab selects an existing certificate ID or uploads a chain/private-key
|
|
pair into private staging. Only Save activates the selection. A shared global SNI
|
|
resolver cannot serve different certificates for the same DNS name by destination
|
|
IP, so conflicting selections are rejected. Browser upload is local-admin only;
|
|
there is no ACME, encrypted-key password prompt, certificate download or automated
|
|
cleanup. Details and limits: MANAGEMENT.md.
|
|
|
|
Cache overrides inherit field by field and use one shared memory budget. The list
|
|
and Cache tab offer selective purge. An explicitly disabled global cache can still be
|
|
overridden by an explicitly enabled vhost. Path exclusions and sticky-cookie safeguards
|
|
remain in force. See CACHE.md.
|
|
|
|
The **Transform** tab is a separate vhost opt-in. `transform.enabled` defaults to false
|
|
for every vhost even when the global transform engine is enabled. JavaScript, CSS, HTML,
|
|
SVG and JSON can inherit or override global format switches, and the extension list may
|
|
be replaced per vhost. Eligible origin responses are transformed before shared-cache
|
|
storage and before downstream compression. Origin `Cache-Control: no-transform` always
|
|
wins and cannot be overridden from the vhost.
|
|
|
|
Pages offers a static/maintenance response, a fallback for unassigned paths, and
|
|
error overrides. Vhosts still retain their configured backend pool; static mode
|
|
avoids forwarding but is not a backend-less static-file server. Host/path ACLs run
|
|
first. Explicit error-status overrides change presentation, not authorization.
|
|
Empty/inherit means use the global error policy; to disable one inherited error
|
|
page specifically, configure an explicit replacement rather than expecting null
|
|
to override inheritance. See MANAGEMENT.md for exact precedence.
|
|
|
|
|
|
### Backend Host compatibility
|
|
|
|
New backends use the backend URL host for the upstream `Host` header by default, matching nginx `proxy_pass` behavior. Enable **Preserve client Host** only when the application intentionally routes on the public hostname. This is especially important for embedded/lighttpd-style administration panels that may close or reject requests carrying an unknown Host. Existing explicit settings are preserved during upgrades.
|
|
|
|
When an upstream request fails before an HTTP response is received, access logs include a bounded `upstream_error` field in addition to `upstream_status`.
|