# Web Server — static sites, downloads and routing The built-in Web Server serves local files and, optionally, passes PHP scripts to an **external PHP-FPM service**. A reusable **Web Service** is referenced by a Vhost or by a Custom location. Its files and PHP settings are not duplicated in the Vhost. ## Create a Web Service 1. Open **Web Server → New Web Service**. Choose an ID/name and an absolute document root readable by the RustProxy service account. 2. Set index files (default `index.html`, `index.htm`), whether directory listing is allowed (off by default), MIME handling and download extensions. 3. Add `try_files`, rewrites, response headers and cache rules only if your application requires them. 4. Enable PHP only if you have a reachable, protected PHP-FPM endpoint; otherwise static files do not need any PHP runtime. 5. Save, create or edit a **Vhost**, select **Web Server** as source and choose the service ID. 6. Test index files, static assets, missing files, direct directory access, downloads and headers. A Web Service can be shared across several Vhosts. A Vhost may also use ordinary Reverse Proxy as its default and a Web Service for `/assets` or another custom location. Web Server and HTTP reverse proxy use the normal frontend TLS, access, logging and eligible cache pipeline. **Linux DEB/RPM paths:** With the updated package unit, `/home/user/public_html`, `/var/www/site` and `/srv/site` are readable using an appropriately scoped service-user group or POSIX ACL; no `systemctl edit` or `ReadOnlyPaths=` exception is needed. The filesystem remains read-only to the service outside its private state/log/config paths. Grant execute/traversal on each parent directory and read permission on served files; see [Installation: external paths](INSTALL.md#linux-debrpm-read-only-access-to-external-paths). ## File serving options | Feature | Behavior / caution | |---|---| | **Document root** | Files are served only through the configured root; keep private keys, source secrets and writable control files outside it. | | **Indexes / listing** | Configurable index priority; directory listing is disabled by default. | | **`try_files`** | Try requested paths, then a fallback such as `/index.html` or the PHP front controller. Useful for SPAs and frameworks. | | **Rewrites** | Bounded regex internal rewrites or supported redirect statuses; evaluated in the Web Service routing pipeline. | | **MIME / downloads** | Built-in type mapping with extensions and explicit attachment options. | | **Headers / cache** | Service-level headers and optional static cache-control rules; later Vhost/location response rules may override. | | **Compression** | Dynamic gzip/Brotli/Zstandard/deflate policy and eligible precompressed `.br`/`.gz` sidecars. | | **File validators / ranges** | Conditional `ETag`/date checks, HEAD and supported byte ranges for eligible files. | | **Symlinks** | `deny` by default; `inside_root` is an explicit less restrictive policy. | | **Hardening** | Optional checks for common sensitive/attack paths and removal of selected server-identifying headers; not a full WAF. | ### Static delivery (`sendfile`) — 0.34.0 In **Web Server → Edit service → Delivery**, `sendfile` supports `off` (default) and `auto`. On **Linux**, eligible full static **GET** responses of at least **64 KiB** over **unencrypted HTTP/1.1** may use a kernel file-transfer path. TLS, HTTP/2, HTTP/3, ranged or transformed content, compressed responses and active object-cache responses keep the normal streaming path. This transport choice should not invalidate otherwise compatible cached objects. It is an **unverified source candidate** in the provided archive: test it on the actual server before use and do not assume a measured speedup. ## Rewrites and application presets The editor supports common website presets to fill suggested fields; review their document root, `try_files`, MIME and PHP values before saving. The two independent hardening checkboxes (block common attack probes and remove fingerprinting headers) are directly below the preset selection and Apply action; the chosen values remain editable. Presets do not install WordPress, Roundcube or other applications. For a single-page app, a fallback similar to `$uri`, `$uri/`, `/index.html` serves the SPA entry for routes without an existing file. For PHP frameworks, use the appropriate front controller; see [PHP-FPM](PHP_FPM.md). ## `.htaccess` compatibility An optional bounded Apache-style `.htaccess` compatibility layer supports a **subset** of rules useful for migrations, not arbitrary Apache modules or dynamic directive execution. It can scan configured paths and enforce stricter handling of unsupported expressions. Prefer explicit `try_files`/rewrite/header controls when possible and test migrated sites. A real Apache configuration cannot be pasted unchanged into RustProxy. ## Admin editor layout In **Web Server → Edit → Delivery**, static file delivery, dynamic compression and precompressed assets have separate sections. In **PHP-FPM**, profile/endpoint, request options, resource limits and FastCGI/php.ini overrides are grouped. Disabled local controls indicate that PHP is off or a shared profile owns those settings; selection and saved values behave as before. ## PHP and error responses A Web Service may enable PHP via a shared PHP-FPM profile or its own configuration. PHP is executed by the FPM service, not RustProxy, and must not be exposed as a raw Internet port. Use dedicated pools for different applications. See [PHP-FPM](PHP_FPM.md). Missing static files return **HTTP 404 Not Found**, unreadable/blocked paths **403 Forbidden**, unsupported static methods **405 Method Not Allowed** (with `Allow: GET, HEAD`), and internal failures **500 Internal Server Error**. The built-in Web Server now uses the existing **application-style HTML error pages** for statuses with a bundled template (including 400, 403, 404, 500 and PHP-FPM gateway errors). Other generated error statuses, such as 405, use the shared styled HTML fallback. Neither internal filesystem paths nor `RustProxy: Web Server ...` diagnostics are included in the public body. Details for 5xx filesystem failures remain in server logs. These generated HTML responses retain the actual HTTP status, `Content-Type: text/html; charset=utf-8`, `X-Content-Type-Options: nosniff` and `Cache-Control: no-store`; `HEAD` preserves the matching `Content-Length` but sends no body. Explicit application/origin responses are still passed through without forced replacement, subject to the Vhost error interception options below. Vhosts can intercept selected application error bodies in **Pages** and return a configured HTML error page while preserving the status. Internal location `error_fallback` is different: it routes the error to a named internal handler. Error-page replacements are marked uncacheable. Origin-supplied and explicitly configured custom error bodies are preserved unless interception is enabled. ## Monitoring and issues A Web Service's dashboard shows request rate, status/error counts, response-header timing, delivered bytes and PHP permit usage (when PHP is configured). These are application observations, not proof of external PHP worker count or final compressed network bytes. - **403 / unexpected block:** inspect document-root permissions (including parent traversal), symlink policy, hardening, inherited ACLs and any SELinux/AppArmor denial; verify the active systemd unit is the updated one. - **404 / unwanted SPA rewrite:** check actual files, index list, `try_files`, location matching and rewrite order. - **Wrong Content-Type/download:** check MIME override and attachment extensions. - **PHP script downloaded instead of executed:** check PHP enablement, script extension and FPM endpoint. - **Cache not filling:** inspect both global and Vhost cache, static response headers and cookies. Keep all document roots and external application processes separately patched and secured.