# Vhost access and visual locations ## 0.8.0 edge traffic controls The Security/Traffic policy section now includes per-client-IP request limits, burst, window length, country-specific limit overrides, country blocking or allow-only mode, per-vhost HTTP/3 enable/disable, optional `Server-Timing`, and a Cloudflare real-client-IP switch. Country rules use the install-time GeoIP country database. Allow-only mode fails closed when a country cannot be resolved; block mode only blocks resolved matches. Exceeded limits return 429 plus `Retry-After`; country policy denial returns 403. Both use built-in HTML pages unless a configured custom error page replaces them. Cloudflare mode trusts `CF-Connecting-IP` only when the **direct peer** is inside the compiled Cloudflare edge CIDRs. An arbitrary client sending that header directly does not become trusted. The verified address becomes the effective client IP for ACLs, rate limiting and forwarded metadata. On the same verified Cloudflare hop, a single two-letter `CF-IPCountry` value is accepted as the country source for country policy, logging and Traffic analytics. Missing, duplicate, invalid and Cloudflare `XX` values fall back to the local GeoIP lookup of that verified effective client IP. Vhost listener editing is now selection-only. Listener addresses/IPs and transport settings live under Settings > Listeners; a vhost selects one or more named endpoints. Existing inline/legacy bindings are promoted to named endpoints when the vhost is saved. New backend rows use the HTTPS-by-IP debugging defaults: include in the default LB pool, allow private origin, HTTP/1.1, preserve client Host and backend TLS verification OFF. The panel shows the risk and asks before saving an HTTPS-by-IP backend that does not match the complete preset. Existing backend values are not rewritten automatically. Health mode `off` disables active probes only. Passive Backend Health is a separate opt-in policy and defaults to OFF. With the default policy, normal request failures are still counted but never mark a backend healthy/unhealthy; an administrator may explicitly enable passive health even while active probe mode remains `off`. ## WebSockets `vhosts[].websockets` controls HTTP/1.1 Upgrade forwarding for that host. Existing configuration omitting it retains the previous enabled behavior. New hosts created in the panel default to disabled. Disabled valid upgrades return 403 without opening an upstream connection; normal HTTP continues. Basic Auth/Tinyauth and ACLs still apply to an allowed handshake. This does not implement WebSockets over HTTP/3, WebTransport or generalized CONNECT, and does not terminate already open tunnels when toggled; it governs subsequent handshakes. After a successful `101`, normal TCP EOF/reset/abort/broken-pipe closure is recorded as tunnel closure rather than an invalid proxy request; handshake/protocol errors remain errors. ## Basic Auth and ACL The Authentication tab selects None, Tinyauth or HTTP Basic. Basic and Tinyauth cannot both be enabled. The Basic editor manages users, realm, TLS requirement and whether verified `Authorization: Basic ...` is passed to the backend. Only bounded Argon2id password hashes are persisted. New/changed passwords are sent to `POST /api/basic-auth/hash` through the protected administrator API; no password is stored in browser storage, desired configuration, audit or access logs. An empty password while editing keeps the saved hash. New passwords use the existing 12-128 byte administrator password policy; Basic credentials cannot contain control characters. Usernames are case-sensitive, up to 128 UTF-8 bytes, without colons or control characters. This is not an htpasswd/APR1/bcrypt importer. TLS is required by default and is checked from the accepted connection, not `X-Forwarded-Proto`. Plaintext HTTP is allowed only by an explicit host option; avoid that outside a deliberately protected TLS-termination network. HTTP Basic encodes, not encrypts, the username/password. Keep the frontend and any credential-carrying upstream hop protected. Administrator access defaults to loopback; direct remote access is supported when Administration HTTPS is enabled and the bind/hostname policy is configured. Successful credentials are removed before forwarding by default. Selecting forward passes the original **verified** Basic Authorization header, not a password from configuration or an unverified client value. The final enforcement step runs after custom request-header rules, which cannot reintroduce removed credentials. `Remote-User`/`Remote-Email` are stripped in Basic mode; the separate Tinyauth mode continues to use verified identity headers from that service. Global host policies, reusable ACL profiles, vhost ACLs and location ACLs remain cumulative: access requires all applicable policy scopes **and** successful authentication. An ACL profile can be defined once under **ACL profiles**, combine default allow/deny IPv4/IPv6 CIDR rules with country block/allow-only policy, then be attached to several vhosts. Per-vhost rules remain available and are evaluated in addition to the profile. Within one policy scope, explicit IP deny rules and blocked countries take precedence. When country mode is **Allow only**, a listed country or an explicit IP allow rule may satisfy that scope; therefore `default=deny` can express e.g. private CIDRs **or** selected countries. An unresolved country still fails closed unless the client matches an explicit IP allow. This does not let a permitted IP skip another profile/vhost/location policy or a password. The graphical inline ACL editor still supports default allow/deny and IPv4/IPv6 CIDR rules. Trusted-proxy settings control which peer can contribute the effective client IP; untrusted forwarding headers do not grant network access. Authentication runs before static pages, cache, origin connections and upgrades. 401 responses include a Basic challenge and `private, no-store`; custom pages or header rules cannot convert these failures into successful access. Protected vhosts bypass shared response caching. A process-wide limit allows two Argon2 workers; failed-password accounting is bounded to 4096 IPs, with ten failures per minute blocking that address for the rest of the window. Busy hashing returns 503; a blocked address returns 429 with Retry-After. This is abuse resistance, not MFA or a replacement for perimeter rate limiting, especially for many clients behind NAT. ## HTTP method policy Each vhost can leave method filtering **Off**, define an **Allow only** list, or define a **Block** list. Methods are validated as HTTP method tokens and normalized to uppercase. A denied request returns `405 Method Not Allowed` before authentication, cache, DNS or origin connection; allow-list mode also returns an `Allow` header containing the configured methods. A Custom location has the same independent method policy. Vhost and location policies are cumulative: a location cannot re-enable a method rejected by its vhost. This makes patterns such as `GET, HEAD, POST` allow-only at host level and a stricter `GET, HEAD` allow-only policy on a read-only location explicit and fail-closed. ## Custom locations The **Custom locations** tab supports ordinary prefix, priority prefix (`^~`), exact (`=`), case-sensitive regex (`~`) and case-insensitive regex (`~*`) matching. Exact matches win first; a selected priority prefix skips regex evaluation; regexes are tested in declaration order; the longest ordinary prefix is the fallback. Exact paths preserve a meaningful trailing slash. A proxy location can use the normal pool, pin one backend, inherit/override cache behavior and apply an additional inline ACL. It can also select a reusable ACL profile, apply its own country allow/block policy and define an HTTP method allow/block policy. These controls are cumulative with the vhost policies and are enforced before a backend connection is opened. Path handling can preserve the public URL, strip a prefix, replace a prefix or perform a capture-based regex rewrite. Query parameters are preserved. Repeated literal slashes are normalized only for routing/ACL decisions; without an explicit rewrite the backend receives the original target, including `//`. Encoded separators, percent double-decoding and dot segments remain rejected. A location can alternatively **Return custom response** with a 200–599 status, Content-Type and body without opening a backend connection. Route/vhost ACL and authentication still apply. A 3xx return plus a visual `Location` response-header rule is the supported simple redirect pattern. Request/response headers have a graphical `set`/`append`/`remove` builder, inline documentation and a synchronized **Advanced JSON** view. Protected framing, forwarding-identity, request-ID and WebSocket handshake headers cannot be overridden. The full routes JSON remains available as an automation/escape hatch, but invalid/unknown fields are rejected rather than silently ignored. RustProxy intentionally implements an explicit safe subset, not arbitrary Nginx configuration: location-level `try_files`, filesystem `root`/`alias`, embedded scripting, arbitrary `if` and module directives are not accepted. See [Custom locations](CUSTOM_LOCATIONS.md) for examples and the exact supported subset. Reference: HTTP Basic security considerations, RFC 7617: https://www.rfc-editor.org/rfc/rfc7617.html#section-4