From 316da6d7b8b51268b5070acb5ff3e0ff237cb17f Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Mon, 9 Nov 2026 02:14:25 +0100 Subject: [PATCH] docs(v3): sync with the latest v3 changes on master Bring the v3 docs in line with static-web-server master (v3.0.0-beta.2 plus unreleased changes): - New defaults: port 8080, root `.`, HTTPS redirect port 8080, and the dual-stack `::` host - Document `--open`/`--open-path` and `--directory-listing-format=auto` - Trusted proxies trust no one by default; metrics labels are bounded and exclude probes; `/readyz` fails while the root is missing - Basic auth only skips CORS preflights; `Vary: Accept` for markdown negotiation; when compression is skipped; pre-compression is on by default; symlink checks apply to the file actually served - Rebuild the TOML manifest with every current key and default - Extend the migration guide with the missing breaking changes - Fix the `env#server_*` anchors, which never matched VitePress slugs Co-Authored-By: Claude Opus 5.5 --- .vitepress/config.v3.mts | 1 + src/v3/configuration/cli.md | 24 +++-- src/v3/configuration/env.md | 28 +++-- src/v3/configuration/file.md | 101 +++++++++++------- src/v3/defaults.md | 21 ++-- src/v3/download-install.md | 2 +- src/v3/download-install.md.tmpl | 2 +- src/v3/features/basic-authentication.md | 6 +- src/v3/features/blocking-threads.md | 4 +- src/v3/features/cache-control-headers.md | 4 +- src/v3/features/compression-static.md | 6 +- src/v3/features/compression.md | 19 +++- src/v3/features/cors.md | 12 +-- src/v3/features/directory-listing.md | 37 +++++-- src/v3/features/docker.md | 56 +++++----- src/v3/features/error-pages.md | 6 +- src/v3/features/etag.md | 6 +- .../file-descriptor-socket-passing.md | 2 +- src/v3/features/follow-symlinks.md | 6 +- src/v3/features/graceful-shutdown.md | 4 +- src/v3/features/health-endpoint.md | 13 ++- src/v3/features/http-https-redirect.md | 12 +-- src/v3/features/http-methods.md | 8 +- src/v3/features/http1.md | 4 +- src/v3/features/http2.md | 2 +- src/v3/features/include-hidden.md | 4 +- src/v3/features/logging.md | 44 ++++---- src/v3/features/maintenance-mode.md | 8 +- .../features/markdown-content-negotiation.md | 6 +- src/v3/features/memory-cache.md | 8 +- src/v3/features/metrics.md | 30 ++++-- src/v3/features/multiple-index-files.md | 4 +- src/v3/features/open-browser.md | 36 +++++++ src/v3/features/security-headers.md | 2 +- src/v3/features/text-charset.md | 2 +- src/v3/features/tls.md | 2 +- src/v3/features/trailing-slash-redirect.md | 4 +- src/v3/features/unix-domain-socket.md | 10 +- src/v3/features/use-relative-root.md | 4 +- src/v3/features/windows-service.md | 2 +- src/v3/features/worker-threads.md | 4 +- src/v3/index.md | 7 +- src/v3/migration-guide.md | 66 ++++++++++-- src/v3/quick-start.md | 5 +- 44 files changed, 406 insertions(+), 228 deletions(-) create mode 100644 src/v3/features/open-browser.md diff --git a/.vitepress/config.v3.mts b/.vitepress/config.v3.mts index 9a04f26..2d91271 100644 --- a/.vitepress/config.v3.mts +++ b/.vitepress/config.v3.mts @@ -73,6 +73,7 @@ export const v3: DefaultTheme.Config = { { text: 'Maintenance Mode', link: '/v3/features/maintenance-mode' }, { text: 'In-Memory Cache', link: '/v3/features/memory-cache' }, { text: 'WebAssembly', link: '/v3/features/webassembly' }, + { text: 'Open in Browser', link: '/v3/features/open-browser' }, { text: 'Man Pages and Shell Completions', link: '/v3/features/man-pages-completions' }, { text: 'Markdown Content Negotiation', link: '/v3/features/markdown-content-negotiation' }, { text: 'Default Charset for Text Responses', link: '/v3/features/text-charset' }, diff --git a/src/v3/configuration/cli.md b/src/v3/configuration/cli.md index 5dfcfac..041c215 100644 --- a/src/v3/configuration/cli.md +++ b/src/v3/configuration/cli.md @@ -19,9 +19,13 @@ Commands: Options: -a, --host - Host address (E.g 127.0.0.1 or ::1) [env: SERVER_HOST=] [default: ::] + Host address (E.g 127.0.0.1 or ::1). The default `::` accepts both IPv6 and IPv4 connections where the OS supports dual-stack sockets [env: SERVER_HOST=] [default: ::] -p, --port - Host port [env: SERVER_PORT=] [default: 8787] + Host port [env: SERVER_PORT=] [default: 8080] + --open [] + Open the server URL in the system default web browser after startup [env: SERVER_OPEN=] [default: false] [possible values: true, false] + --open-path + URL path opened in the browser. Requires the open option [env: SERVER_OPEN_PATH=] -f, --fd Instead of binding to a TCP port, accept incoming connections to an already-bound TCP socket listener on the specified file descriptor number (usually zero). Requires that the parent process (e.g. inetd, launchd, or systemd) binds an address and port on behalf of static-web-server, before arranging for the resulting file descriptor to be inherited by static-web-server. Cannot be used in conjunction with the port and host arguments. The included systemd unit file utilises this feature to increase security by allowing the static-web-server to be sandboxed more completely [env: SERVER_LISTEN_FD=] --unix-socket @@ -35,7 +39,7 @@ Options: -b, --max-blocking-threads Maximum number of blocking threads [env: SERVER_MAX_BLOCKING_THREADS=] [default: 512] -d, --root - Root directory path of static files [env: SERVER_ROOT=] [default: ./public] + Root directory path of static files [env: SERVER_ROOT=] [default: .] --page50x HTML file path for 50x errors. If the path is not specified or simply doesn't exist then the server will use a generic HTML error message. If a relative path is used then it will be resolved under the root directory [env: SERVER_ERROR_PAGE_50X=] [default: ./50x.html] --page404 @@ -69,7 +73,7 @@ Options: --https-redirect-host Canonical host name or IP of the HTTPS server. It depends on "https_redirect" to be enabled [env: SERVER_HTTPS_REDIRECT_HOST=] [default: localhost] --https-redirect-from-port - HTTP host port where the redirect server will listen for requests to redirect them to HTTPS. It depends on "https_redirect" to be enabled [env: SERVER_HTTPS_REDIRECT_FROM_PORT=] [default: 80] + HTTP host port where the redirect server will listen for requests to redirect them to HTTPS. It depends on "https_redirect" to be enabled [env: SERVER_HTTPS_REDIRECT_FROM_PORT=] [default: 8080] --https-redirect-from-hosts List of host names or IPs allowed to redirect from. HTTP requests must contain the HTTP 'Host' header and match against this list. It depends on "https_redirect" to be enabled [env: SERVER_HTTPS_REDIRECT_FROM_HOSTS=] [default: localhost] --index-files @@ -85,7 +89,7 @@ Options: --directory-listing-order Specify a default code number to order directory listing entries per `Name`, `Last modified` or `Size` attributes (columns). Code numbers supported: 0 (Name asc), 1 (Name desc), 2 (Last modified asc), 3 (Last modified desc), 4 (Size asc), 5 (Size desc). Default 6 (unordered) [env: SERVER_DIRECTORY_LISTING_ORDER=] [default: 6] --directory-listing-format - Specify a content format for directory listing entries. Formats supported: "html" or "json". Default "html" [env: SERVER_DIRECTORY_LISTING_FORMAT=] [default: html] [possible values: html, json] + Specify a content format for directory listing entries. Formats supported: "html", "json" or "auto". Default "html" [env: SERVER_DIRECTORY_LISTING_FORMAT=] [default: html] [possible values: html, json, auto] --directory-listing-download= Specify list of enabled format(s) for directory download. Format supported: `targz`. Default to empty list (disabled) [env: SERVER_DIRECTORY_LISTING_DOWNLOAD=] [possible values: targz] --security-headers [] @@ -107,15 +111,15 @@ Options: -w, --config-file Server TOML configuration file path [env: SERVER_CONFIG_FILE=] [default: ./sws.toml] --access-log-format - Custom access log format string using %{token} syntax. When set, replaces the default request logging with a configurable format that includes response data. Tokens: method, uri, status, bytes, duration, remote_addr, x_real_ip, forwarded_for, host, user_agent, referer, timestamp, version. x_real_ip and forwarded_for are logged only for requests from --trusted-proxies, if set. Example: '%{remote_addr} "%{method} %{uri}" %{status} %{bytes}' [env: SERVER_ACCESS_LOG_FORMAT=] [default: ""] + Custom access log format string using %{token} syntax. When set, replaces the default request logging with a configurable format that includes response data. Tokens: method, uri, status, bytes, duration, remote_addr, x_real_ip, forwarded_for, host, user_agent, referer, timestamp, version. x_real_ip and forwarded_for are logged only for requests from --trusted-proxies. Example: '%{remote_addr} "%{method} %{uri}" %{status} %{bytes}' [env: SERVER_ACCESS_LOG_FORMAT=] [default: ""] --log-remote-address [] Log the remote address of each request, if available [env: SERVER_LOG_REMOTE_ADDRESS=] [default: false] [possible values: true, false] --log-x-real-ip [] - Log the X-Real-IP header for remote IP information [env: SERVER_LOG_X_REAL_IP=] [default: false] [possible values: true, false] + Log the X-Real-IP header of requests from --trusted-proxies [env: SERVER_LOG_X_REAL_IP=] [default: false] [possible values: true, false] --log-forwarded-for [] - Log the X-Forwarded-For header for remote IP information [env: SERVER_LOG_FORWARDED_FOR=] [default: false] [possible values: true, false] + Log the first X-Forwarded-For address of requests from --trusted-proxies [env: SERVER_LOG_FORWARDED_FOR=] [default: false] [possible values: true, false] --trusted-proxies - List of IPs to use X-Forwarded-For from. The default is to trust all [env: SERVER_TRUSTED_PROXIES=] + List of proxy IPs to use the X-Real-IP and X-Forwarded-For headers from. The default trusts no one, so the headers are not logged unless set. IPv4 entries also match their IPv4-mapped IPv6 form (`::ffff:a.b.c.d`) [env: SERVER_TRUSTED_PROXIES=] --redirect-trailing-slash [] Check for a trailing slash in the requested directory URI and redirect permanently (308) to the same path with a trailing slash suffix if it is missing [env: SERVER_REDIRECT_TRAILING_SLASH=] [default: true] [possible values: true, false] --include-hidden [] @@ -129,7 +133,7 @@ Options: --text-charset [] Set a default `charset=utf-8` parameter on limited set of `text` responses that don't already have one [env: SERVER_TEXT_CHARSET=] [default: true] [possible values: true, false] --health [] - Add the `/livez` (liveness) and `/readyz` (readiness) endpoints for Kubernetes-style probes. They answer `GET` and `HEAD` with `200 OK` and log requests at `debug` level only [env: SERVER_HEALTH=] [default: false] [possible values: true, false] + Add the `/livez` (liveness) and `/readyz` (readiness) endpoints for Kubernetes-style probes. They answer `GET` and `HEAD` with `200 OK` (`/readyz` returns `503` once shutdown starts or while the root directory is missing) and log requests at `debug` level only [env: SERVER_HEALTH=] [default: false] [possible values: true, false] --metrics [] Enable the /metrics endpoint that exposes Prometheus metrics for HTTP requests, connections, and latency [env: SERVER_METRICS=] [default: false] [possible values: true, false] --maintenance-mode [] diff --git a/src/v3/configuration/env.md b/src/v3/configuration/env.md index 06c1fcb..250c80b 100644 --- a/src/v3/configuration/env.md +++ b/src/v3/configuration/env.md @@ -9,11 +9,19 @@ The server can be configured via the following environment variables. ## SERVER_HOST -Host address (e.g. `127.0.0.1` or `::1`). Default `[::]`. +Host address (e.g. `127.0.0.1` or `::1`). The default `::` accepts both IPv6 and IPv4 connections where the OS supports dual-stack sockets. Default `::`. ## SERVER_PORT -Host port. Default `8787`. +Host port. Default `8080`. + +## SERVER_OPEN + +Open the server URL in the system default web browser after startup. See [Open in Browser](../features/open-browser.md). Default `false` (disabled). + +## SERVER_OPEN_PATH + +URL path opened in the browser. Requires `SERVER_OPEN`. Default empty (`/`). ## SERVER_LISTEN_FD @@ -41,7 +49,7 @@ Maximum number of blocking threads. Default `512`. ## SERVER_ROOT -Root directory path of static files. Default `./public`. +Root directory path of static files. Default `.` (current directory). ## SERVER_ERROR_PAGE_50X @@ -53,7 +61,7 @@ HTML file path for 404 errors. If the path is not specified or simply doesn't ex ## SERVER_FALLBACK_PAGE -A HTML file path (not relative to the root) used for `GET` requests when the requested path doesn't exist. The fallback page is served with a `200` status code, useful when using client routers. If the path doesn't exist then the feature is not activated. +A HTML file path (not relative to the root) used for `GET` requests when the requested path doesn't exist. The fallback page is served with a `200` status code, useful when using client routers. Requests for missing assets keep their `404`, see [Fallback Page](../features/error-pages.md#fallback-page-for-use-with-client-routers). If the path doesn't exist then the feature is not activated. ## SERVER_LOG_LEVEL @@ -109,7 +117,7 @@ Canonical host name or IP of the HTTPS server. It depends on `SERVER_HTTPS_REDIR ## SERVER_HTTPS_REDIRECT_FROM_PORT -HTTP host port where the redirect server will listen for requests to redirect them to HTTPS. It depends on `SERVER_HTTPS_REDIRECT` to be enabled. Default `80`. +HTTP host port where the redirect server will listen for requests to redirect them to HTTPS. It depends on `SERVER_HTTPS_REDIRECT` to be enabled. Default `8080`. ## SERVER_HTTPS_REDIRECT_FROM_HOSTS @@ -141,7 +149,7 @@ Specify a default code number to order directory listing entries per `Name`, `La ## SERVER_DIRECTORY_LISTING_FORMAT -Specify a content format for directory listing entries. Formats supported: `html` or `json`. Default `html`. +Specify a content format for directory listing entries. Formats supported: `html`, `json` or `auto` (chosen per request from the `Accept` header). See [Output format](../features/directory-listing.md#output-format). Default `html`. ## SERVER_DIRECTORY_LISTING_DOWNLOAD @@ -193,15 +201,15 @@ Log the remote address of each request, if available. Default `false`. ## SERVER_LOG_X_REAL_IP -Log the `X-Real-IP` header for remote IP information. Default `false`. +Log the `X-Real-IP` header of requests from `SERVER_TRUSTED_PROXIES`. Default `false`. ## SERVER_LOG_FORWARDED_FOR -Log the `X-Forwarded-For` header for remote IP information. Default `false`. +Log the first `X-Forwarded-For` address of requests from `SERVER_TRUSTED_PROXIES`. Default `false`. ## SERVER_TRUSTED_PROXIES -List of IPs to use `X-Forwarded-For` from. The default is to trust all. Default `""`. +List of proxy IPs to use the `X-Real-IP` and `X-Forwarded-For` headers from. IPv4 entries also match their IPv4-mapped IPv6 form (`::ffff:a.b.c.d`). See [Trusted proxies](../features/logging.md#trusted-proxies). Default empty (trusts no one, so the headers are not logged). ## SERVER_REDIRECT_TRAILING_SLASH @@ -229,7 +237,7 @@ Set a default `charset=utf-8` parameter on limited set of `text` responses that ## SERVER_HEALTH -Add the `/livez` (liveness) and `/readyz` (readiness) endpoints for Kubernetes-style probes. They answer `GET` and `HEAD` with `200 OK` and log requests at `debug` level only. See [Health endpoint](../features/health-endpoint.md) for details. Default `false` (disabled). +Add the `/livez` (liveness) and `/readyz` (readiness) endpoints for Kubernetes-style probes. They answer `GET` and `HEAD` with `200 OK` (`/readyz` returns `503` once shutdown starts or while the root directory is missing) and log requests at `debug` level only. See [Health endpoint](../features/health-endpoint.md) for details. Default `false` (disabled). ## SERVER_METRICS diff --git a/src/v3/configuration/file.md b/src/v3/configuration/file.md index c8233fe..28c1dd2 100644 --- a/src/v3/configuration/file.md +++ b/src/v3/configuration/file.md @@ -6,7 +6,7 @@ outline: deep **`SWS`** can be configured using a [TOML](https://toml.io/en/) file to adjust the general server features as well as other advanced ones. -It's disabled by default and can be enabled by passing a _string file path_ via the `-w, --config-file` option or its equivalent [SERVER_CONFIG_FILE](./../configuration/env#server_config_file) env. +It's disabled by default and can be enabled by passing a _string file path_ via the `-w, --config-file` option or its equivalent [SERVER_CONFIG_FILE](./../configuration/env#server-config-file) env. > [!TIP] The default config file path is checked at startup time > If using the default config file path (`./sws.toml`), SWS will attempt to load it at startup time. @@ -21,58 +21,81 @@ Below is just an example showing all features with their default values. #### Address & Root dir host = "::" -port = 80 -root = "./public" +port = 8080 +root = "." + +#### Open the server URL in the browser on startup +open = false +# open-path = "/" + +#### Unix domain socket (Unix only) +# unix-socket = "/run/sws.sock" +# unix-socket-mode = 0o660 # octal +unix-socket-force = false + +#### File descriptor binding +# fd = 0 #### Logging log-level = "error" log-format = "json" +log-with-ansi = false +# log-file = "/var/log/sws.log" #### Cache Control headers cache-control-headers = true +#### ETag validators +etag = true + #### Auto Compression compression = true compression-level = "default" +#### Check for existing pre-compressed files +compression-static = true + #### Error pages # Note: If a relative path is used then it will be resolved under the root directory. page404 = "./404.html" page50x = "./50x.html" -#### HTTP/2 + TLS +#### Page fallback for 404s +# page-fallback = "" + +#### TLS, HTTP/2 & HTTPS redirect +tls = false +tls-cert = "" +tls-key = "" http2 = false -http2-tls-cert = "" -http2-tls-key = "" https-redirect = false https-redirect-host = "localhost" -https-redirect-from-port = 80 +https-redirect-from-port = 8080 https-redirect-from-hosts = "localhost" -#### CORS & Security headers -# security-headers = true -# cors-allow-origins = "" +#### Security headers +security-headers = true -#### Directory listing -directory-listing = false +#### CORS +cors-allow-origins = "" +cors-allow-headers = "origin, content-type, authorization" +cors-expose-headers = "origin, content-type" -#### Directory listing sorting code -directory-listing-order = 1 +#### List of index files +index-files = "index.html" -#### Directory listing content format +#### Directory listing +directory-listing = false +directory-listing-order = 6 directory-listing-format = "html" - -#### Directory listing download format directory-listing-download = [] #### Basic Authentication # basic-auth = "" -#### File descriptor binding -# fd = "" - -#### Worker threads +#### Worker and blocking threads threads-multiplier = 1 +max-blocking-threads = 512 #### Grace period after a graceful shutdown grace-period = 0 @@ -86,45 +109,44 @@ max-header-size = 16384 #### Maximum connections served at the same time (0 means no limit) max-connections = 0 -#### Page fallback for 404s -# page-fallback = "" - #### Custom access log format (replaces the default request line when set) # access-log-format = '%{remote_addr} - [%{timestamp}] "%{method} %{uri} %{version}" %{status} %{bytes} "%{referer}" "%{user_agent}"' #### Log request Remote Address if available log-remote-address = false -#### Log real IP from X-Forwarded-For header if available +#### Log real IPs from the X-Real-IP and X-Forwarded-For headers of trusted proxies +log-x-real-ip = false log-forwarded-for = false -#### IPs to accept the X-Forwarded-For header from. Empty means all +#### Proxy IPs to accept the X-Real-IP and X-Forwarded-For headers from. Empty means none trusted-proxies = [] #### Redirect to trailing slash in the requested directory uri redirect-trailing-slash = true +#### Hidden files and symlinks +include-hidden = false +follow-symlinks = false + #### Use relative root (skip root canonicalization at startup) use-relative-root = false -#### Check for existing pre-compressed files -compression-static = true - #### Health-check endpoints (GET or HEAD `/livez` and `/readyz`) health = false +#### Prometheus metrics endpoint (`/metrics`) +metrics = false + #### Markdown content negotiation accept-markdown = false -#### Default charset for text responses (enabled by default) -# text-charset = false +#### Default charset for text responses +text-charset = true -#### List of index files -# index-files = "index.html, index.htm" #### Maintenance Mode - maintenance-mode = false -# maintenance-mode-status = 503 +maintenance-mode-status = 503 # maintenance-mode-file = "./maintenance.html" ### Windows Only @@ -155,6 +177,13 @@ maintenance-mode = false # source = "**/*.{jpg,jpeg,png,ico,gif}" # headers.Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload" +#### d. Only for some response statuses, and for every response (not only files) +# [[advanced.headers]] +# source = "**" +# status = [404] +# scope = "all" +# headers.Cache-Control = "no-store" + ### URL Redirects (examples only) @@ -196,7 +225,7 @@ maintenance-mode = false # capacity = 100 # ttl = 1800 # 30 minutes # tti = 300 # 5 minutes -# max-file-size = 8192 # 8 MiB +# max-file-size = 8192 # in KiB (8 MiB) ``` ### General options diff --git a/src/v3/defaults.md b/src/v3/defaults.md index eb64b06..b9330fb 100644 --- a/src/v3/defaults.md +++ b/src/v3/defaults.md @@ -8,7 +8,7 @@ This page lists every default and the reasoning behind it. For changing a defaul When running `static-web-server` with no flags, you get: -- Serves `./public/` on port `8787`, all interfaces. +- Serves the current directory (`.`) on port `8080`, all interfaces (IPv6 and IPv4). - `index.html` as the directory index. - `404.html` and `50x.html` as error pages (if they exist). - Gzip/Brotli/Zstd compression for text content. @@ -23,12 +23,12 @@ When running `static-web-server` with no flags, you get: ## Network & binding -| Setting | Default | Rationale | -| ----------------------- | ----------------------------------- | -------------------------------------------------------------------------------- | -| Host | `[::]` (all interfaces, dual-stack) | Reachable from any network interface. IPv4-mapped IPv6 makes it work everywhere. | -| Port | `8787` | Non-privileged port, no `root` needed. | -| Unix socket | disabled | TCP is the common case; UDS is opt-in for reverse-proxy setups. | -| File descriptor passing | disabled | Only needed in inetd/systemd/launchd sandboxing scenarios. | +| Setting | Default | Rationale | +| ----------------------- | --------------------------------- | -------------------------------------------------------------------------------- | +| Host | `::` (all interfaces, dual-stack) | Reachable from any network interface, over IPv6 and IPv4 where the OS allows it. | +| Port | `8080` | Non-privileged port, no `root` needed. | +| Unix socket | disabled | TCP is the common case; UDS is opt-in for reverse-proxy setups. | +| File descriptor passing | disabled | Only needed in inetd/systemd/launchd sandboxing scenarios. | ## Concurrency @@ -41,14 +41,14 @@ When running `static-web-server` with no flags, you get: | Setting | Default | Rationale | | ------------------------ | ------------------------- | ------------------------------------------------------------------------------------ | -| Root directory | `./public` | Predictable relative path and works immediately after `cd` into a project. | +| Root directory | `.` (current directory) | Works immediately after `cd` into the directory to serve. | | Index files | `index.html` | Matches every static-site generator and web server convention. | | Custom 404 page | `./404.html` (if present) | Drop a file and it's picked up; falls back to a generic built-in message. | | Custom 50x page | `./50x.html` (if present) | Same pattern as 404. | | Fallback page | disabled | Only needed for SPAs with client-side routing. | | Directory listing | disabled | Prevents information disclosure. Enable it explicitly when needed. | | Directory listing order | unordered (`6`) | No default sort avoids the cost of reading every directory entry's metadata. | -| Directory listing format | `html` | Human-readable in a browser; switch to `json` for programmatic use. | +| Directory listing format | `html` | Human-readable in a browser; use `json` or `auto` for programmatic use. | | Directory download | disabled | Opt-in feature; prevents unintended bulk downloads. | | Trailing slash redirect | enabled | Normalizes directory URLs (`/blog` to `/blog/`), which keeps relative links working. | @@ -97,7 +97,7 @@ The types considered compressible include all `text/*` MIME types, plus `applica | Log format | `json` | Machine-parseable for production log aggregators (Loki, ELK, etc.). Use `pretty` for development. | | ANSI colors | disabled | Off by default since `json` format doesn't use them. Only meaningful with `pretty` format. | | Remote address logging | disabled | Privacy-conscious default. Enable to log client IPs at `info` level. | -| `X-Real-IP` / `X-Forwarded-For` | disabled | Only needed behind a reverse proxy. Configure `trusted-proxies` when enabling. | +| `X-Real-IP` / `X-Forwarded-For` | disabled | Only needed behind a reverse proxy. Logged only for IPs listed in `trusted-proxies`. | | File logging | disabled | Logs go to `stderr`. Opt into file output with an explicit path. | ## Other features @@ -105,6 +105,7 @@ The types considered compressible include all `text/*` MIME types, plus `applica | Setting | Default | Rationale | | ---------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------ | | Health endpoints | disabled | Opt-in via `--health`. Returns `200 OK` at `/livez` and `/readyz` with no log noise. For Kubernetes probes. | +| Open in browser | disabled | Opt-in via `--open` for local development. | | Metrics endpoint | disabled | Opt-in via `--metrics`. Exposes Prometheus metrics at `/metrics`. | | Markdown content negotiation | disabled | Opt-in. When enabled, serves `.md` files for `Accept: text/markdown` requests. | | Maintenance mode | disabled | Opt-in for planned downtime. Returns `503` with a custom page. | diff --git a/src/v3/download-install.md b/src/v3/download-install.md index 51c25cf..2f52cfa 100644 --- a/src/v3/download-install.md +++ b/src/v3/download-install.md @@ -174,7 +174,7 @@ scoop install static-web-server Via [Wasmer](https://wasmer.io/wasmer/static-web-server/) ```sh -wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787 +wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8080 ``` ## Binaries diff --git a/src/v3/download-install.md.tmpl b/src/v3/download-install.md.tmpl index 279219d..2e13bef 100644 --- a/src/v3/download-install.md.tmpl +++ b/src/v3/download-install.md.tmpl @@ -173,7 +173,7 @@ scoop install static-web-server Via [Wasmer](https://wasmer.io/wasmer/static-web-server/) ```sh -wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787 +wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8080 ``` ## Binaries diff --git a/src/v3/features/basic-authentication.md b/src/v3/features/basic-authentication.md index 5352a99..597c536 100644 --- a/src/v3/features/basic-authentication.md +++ b/src/v3/features/basic-authentication.md @@ -2,7 +2,7 @@ **`SWS`** provides ['Basic' HTTP Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc7617) using an `user:password` pair. -This feature is disabled by default and can be controlled by the string `--basic-auth` option or the equivalent [SERVER_BASIC_AUTH](./../configuration/env#server_basic_auth) env. +This feature is disabled by default and can be controlled by the string `--basic-auth` option or the equivalent [SERVER_BASIC_AUTH](./../configuration/env#server-basic-auth) env. The format to use is the following: @@ -38,7 +38,9 @@ Finally, assign the credentials and run the server. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --basic-auth 'username:$2y$...' ``` + +Every request needs valid credentials, including `OPTIONS` requests. The only exception is a [CORS](./cors.md) preflight request (`OPTIONS` with the `Origin` and `Access-Control-Request-Method` headers, while CORS is enabled), since browsers send it without credentials. diff --git a/src/v3/features/blocking-threads.md b/src/v3/features/blocking-threads.md index bc49819..29351c0 100644 --- a/src/v3/features/blocking-threads.md +++ b/src/v3/features/blocking-threads.md @@ -2,7 +2,7 @@ **SWS** allows limiting the number of blocking threads powered by the [Tokio](https://tokio.rs/) runtime. -This feature can be controlled by the numeric `-b, --max-blocking-threads` option or the equivalent [SERVER_MAX_BLOCKING_THREADS](./../configuration/env.md#server_max_blocking_threads) env. +This feature can be controlled by the numeric `-b, --max-blocking-threads` option or the equivalent [SERVER_MAX_BLOCKING_THREADS](./../configuration/env.md#server-max-blocking-threads) env. > [!INFO] WebAssembly > @@ -12,7 +12,7 @@ Below is an example. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./my-public-dir \ --max-blocking-threads 20 ``` diff --git a/src/v3/features/cache-control-headers.md b/src/v3/features/cache-control-headers.md index e0cf6e7..24d2639 100644 --- a/src/v3/features/cache-control-headers.md +++ b/src/v3/features/cache-control-headers.md @@ -6,7 +6,7 @@ outline: deep **`SWS`** provides support for _arbitrary_ [`Cache-Control`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control) HTTP header specifying `public` and `max-age` response directives. -This feature is enabled by default and can be controlled by the boolean `-e, --cache-control-headers` option or the equivalent [SERVER_CACHE_CONTROL_HEADERS](./../configuration/env#server_cache_control_headers) env. +This feature is enabled by default and can be controlled by the boolean `-e, --cache-control-headers` option or the equivalent [SERVER_CACHE_CONTROL_HEADERS](./../configuration/env#server-cache-control-headers) env. > [!TIP] Customize HTTP headers > @@ -49,5 +49,5 @@ avif, bmp, bz2, css, doc, gif, gz, htc, ico, jpeg, jpg, js, jxl, map, mjs, mp3, Below is an example of how to enable the feature. ```sh -static-web-server --port 8787 --root ./public --cache-control-headers +static-web-server --port 8080 --root ./public --cache-control-headers ``` diff --git a/src/v3/features/compression-static.md b/src/v3/features/compression-static.md index 8c01ff3..8a6f17a 100644 --- a/src/v3/features/compression-static.md +++ b/src/v3/features/compression-static.md @@ -8,7 +8,7 @@ outline: deep SWS can look up existing pre-compressed file variants (`.gz`, `.br` or `.zst`) on disk and serve them directly with **zero CPU cost** with no on-the-fly compression overhead. -This feature can be controlled by the boolean `--compression-static` option or the equivalent [SERVER_COMPRESSION_STATIC](./../configuration/env#server_compression_static) env. It is **disabled by default**. +This feature can be controlled by the boolean `--compression-static` option or the equivalent [SERVER_COMPRESSION_STATIC](./../configuration/env#server-compression-static) env. It is **enabled by default**. > [!TIP] Independent from dynamic compression > @@ -19,12 +19,12 @@ Otherwise, if the pre-compressed file is not found then SWS just continues the n > [!INFO] Compressed file type > -> The pre-compressed file type is determined by the [`Accept-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header value. +> The pre-compressed file type is determined by the [`Accept-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header value. A `.gz` file is served only to clients accepting `gzip`, `.br` for `br` and `.zst` for `zstd`. Encodings with `q=0` are not acceptable. Here is an example: ```sh -static-web-server --port 8787 --root /public --compression-static -g trace +static-web-server --port 8080 --root /public --compression-static -g trace ``` Below are some relevant log entries to show how the feature works. diff --git a/src/v3/features/compression.md b/src/v3/features/compression.md index 9bedb91..36c9541 100644 --- a/src/v3/features/compression.md +++ b/src/v3/features/compression.md @@ -6,27 +6,38 @@ outline: deep **`SWS`** provides [`Gzip`](https://datatracker.ietf.org/doc/html/rfc1952), [`Deflate`](https://datatracker.ietf.org/doc/html/rfc1950), [`Brotli`](https://www.ietf.org/rfc/rfc7932.txt) and [`Zstandard` (zstd)](https://datatracker.ietf.org/doc/html/rfc8878) compression of HTTP responses on the fly (dynamically). -This feature is enabled by default and can be controlled by the boolean `-x, --compression` option or the equivalent [SERVER_COMPRESSION](../configuration/env#server_compression) env. +This feature is enabled by default and can be controlled by the boolean `-x, --compression` option or the equivalent [SERVER_COMPRESSION](../configuration/env#server-compression) env. > [!TIP] Independent from static compression > > Dynamic (on-the-fly) compression operates **independently** from [pre-compressed files serving](compression-static.md). Both features can be enabled individually or combined: static compression is tried first (zero CPU cost), and if no pre-compressed variant is found, dynamic compression kicks in automatically. ```sh -static-web-server --port 8787 --root ./my-public-dir --compression +static-web-server --port 8080 --root ./my-public-dir --compression ``` ## Choice of compression algorithm The compression algorithm is determined by the [`Accept-Encoding`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header and the compression support built into SWS. By default SWS builds with support for `Gzip`, `Deflate`, `Brotli` and `Zstandard` algorithms. -SWS honors the qualities specified by the client to choose the algorithm. In case of equal quality for several algorithms (or no qualities at all), the internal priority will be selected according to this list: +SWS honors the qualities specified by the client to choose the algorithm, and never uses one marked with `q=0`. In case of equal quality for several algorithms (or no qualities at all), the internal priority will be selected according to this list: 1. `Zstandard` 2. `Brotli` 3. `Gzip` 4. `Deflate` +## When responses are not compressed + +SWS sends a response uncompressed when: + +- its MIME type is not in the [list below](#mime-types-compressed), +- it is smaller than `200` bytes, where compression saves little, +- it is partial content (`206` or a `Content-Range` header), so byte ranges stay valid, +- it has no content (`1xx`, `204`, `304` or an empty body). + +A `HEAD` response gets the same `Content-Encoding` and `Vary` headers as the matching `GET`. + ## MIME types compressed Compression is only applied to files with the MIME types listed below, indicating text and similarly well compressing formats. @@ -51,4 +62,4 @@ Compression is only applied to files with the MIME types listed below, indicatin ## Compression level -SWS allows selecting the compression level via `--compression-level` command line option or the equivalent [SERVER_COMPRESSION_LEVEL](../configuration/env#server_compression_level) env. The available values are `fastest`, `best` and `default`. `fastest` will result in the lowest CPU load but also the worst compression factor. `best` will attempt to compress the data as much as possible (not recommended with `Brotli` or `Zstandard` compression, will be very slow). `default` tries to strike a balance, choosing a compression level where compression factor is already fairly good but the CPU load is still low. +SWS allows selecting the compression level via `--compression-level` command line option or the equivalent [SERVER_COMPRESSION_LEVEL](../configuration/env#server-compression-level) env. The available values are `fastest`, `best` and `default`. `fastest` will result in the lowest CPU load but also the worst compression factor. `best` will attempt to compress the data as much as possible (not recommended with `Brotli` or `Zstandard` compression, will be very slow). `default` tries to strike a balance, choosing a compression level where compression factor is already fairly good but the CPU load is still low. diff --git a/src/v3/features/cors.md b/src/v3/features/cors.md index db163fc..bf69e87 100644 --- a/src/v3/features/cors.md +++ b/src/v3/features/cors.md @@ -9,13 +9,13 @@ outline: deep A list of allowed origin hosts (URLs) should be specified and separated by commas. Or an asterisk (\*) can be used to allow any host. -This feature is disabled by default and can be controlled by the string `-c, --cors-allow-origins` option or the equivalent [SERVER_CORS_ALLOW_ORIGINS](../configuration/env#server_cors_allow_origins) env. +This feature is disabled by default and can be controlled by the string `-c, --cors-allow-origins` option or the equivalent [SERVER_CORS_ALLOW_ORIGINS](../configuration/env#server-cors-allow-origins) env. Below is an example of how to enable CORS. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./my-public-dir \ --cors-allow-origins "https://domain.com" @@ -27,7 +27,7 @@ static-web-server \ The server also supports a list of [CORS allowed headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers) separated by commas. -This feature depends on `--cors-allow-origins` to be used along with this feature. It can be controlled by the string `-j, --cors-allow-headers` option or the equivalent [SERVER_CORS_ALLOW_HEADERS](../configuration/env#server_cors_allow_headers) env. +This feature depends on `--cors-allow-origins` to be used along with this feature. It can be controlled by the string `-j, --cors-allow-headers` option or the equivalent [SERVER_CORS_ALLOW_HEADERS](../configuration/env#server-cors-allow-headers) env. > [!INFO] Tips > @@ -38,7 +38,7 @@ Below is an example of how to CORS. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --cors-allow-origins "https://domain.com" \ --cors-allow-headers "origin, content-type, x-requested-with" @@ -48,7 +48,7 @@ static-web-server \ The server also supports a list of [CORS-exposed headers to scripts](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers) separated by commas. -This feature depends on `--cors-allow-origins` to be used along with this feature. It can be controlled by the string `--cors-expose-headers` option or the equivalent [SERVER_CORS_EXPOSE_HEADERS](../configuration/env#server_cors_expose_headers) env. +This feature depends on `--cors-allow-origins` to be used along with this feature. It can be controlled by the string `--cors-expose-headers` option or the equivalent [SERVER_CORS_EXPOSE_HEADERS](../configuration/env#server-cors-expose-headers) env. > [!INFO] Tips > @@ -59,7 +59,7 @@ Below is an example of how to CORS. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --cors-allow-origins "https://domain.com" \ --cors-expose-headers "origin, content-type, x-requested-with" diff --git a/src/v3/features/directory-listing.md b/src/v3/features/directory-listing.md index 35bfffd..1b285ce 100644 --- a/src/v3/features/directory-listing.md +++ b/src/v3/features/directory-listing.md @@ -6,11 +6,11 @@ outline: deep **`SWS`** provides a directory listing feature to display the content of directories. -This feature is disabled by default and can be controlled by the boolean `-z, --directory-listing` option or the equivalent [SERVER_DIRECTORY_LISTING](./../configuration/env#server_directory_listing) env. +This feature is disabled by default and can be controlled by the boolean `-z, --directory-listing` option or the equivalent [SERVER_DIRECTORY_LISTING](./../configuration/env#server-directory-listing) env. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --directory-listing ``` @@ -35,7 +35,7 @@ You can also use the `sort` query parameter to sort manually by certain attribut ### Sorting by default -Sometimes one wants to sort by a certain attribute but by **default**. In that case, the default ascending or descending ordering of files/dirs by their attributes is provided by the numeric `--directory-listing-order` option or the equivalent [SERVER_DIRECTORY_LISTING_ORDER](./../configuration/env#server_directory_listing_order) env. +Sometimes one wants to sort by a certain attribute but by **default**. In that case, the default ascending or descending ordering of files/dirs by their attributes is provided by the numeric `--directory-listing-order` option or the equivalent [SERVER_DIRECTORY_LISTING_ORDER](./../configuration/env#server-directory-listing-order) env. To do so you have to pass a [code sorting number](#code-numbers-for-sorting). E.g `--directory-listing-order=2`. @@ -71,7 +71,7 @@ Example: ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --directory-listing \ # E.g Sorting file/dir names in descending order @@ -80,7 +80,11 @@ static-web-server \ ## Output format -**`SWS`** provides support for specifying an output format either HTML (default) or JSON for the directory listing entries via the string `--directory-listing-format` option or the equivalent [SERVER_DIRECTORY_LISTING_FORMAT](./../configuration/env#server_directory_listing_format) env. +**`SWS`** provides support for specifying an output format for the directory listing entries via the string `--directory-listing-format` option or the equivalent [SERVER_DIRECTORY_LISTING_FORMAT](./../configuration/env#server-directory-listing-format) env: + +- `html` (default): an HTML page. +- `json`: a JSON array, see [JSON format](#json-format). +- `auto`: chosen per request from the `Accept` header, see [Automatic format](#automatic-format). > [!TIP] Tips > @@ -114,7 +118,7 @@ Here is an example of serving the directory listing in JSON format. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --directory-listing \ --directory-listing-format="json" @@ -123,26 +127,39 @@ static-web-server \ And below is a client request example to illustrate how the feature works. ```sh -curl -iH "content-type: application/json" http://localhost:8787 +curl -i http://localhost:8080 # HTTP/1.1 200 OK # content-type: application/json # content-length: 163 -# cache-control: max-age=86400 +# cache-control: no-cache # date: Tue, 11 Oct 2022 23:24:55 GMT # [{"name":"spécial directöry","type":"directory","mtime":"2022-10-07T00:53:50Z"},{"name":"index.html.gz","type":"file","mtime":"2022-09-27T22:44:34Z","size":332}]⏎ ``` +### Automatic format + +With `--directory-listing-format=auto`, SWS picks the format from the request's `Accept` header, following its quality values: `application/json` gets JSON, and `text/html` gets HTML. HTML is also used when the header is missing or lists neither type. The response includes `Vary: Accept` so caches store each format separately. + +```sh +static-web-server --root ./public --directory-listing --directory-listing-format=auto + +curl -H "Accept: application/json" http://localhost:8080/ # JSON +curl http://localhost:8080/ # HTML +``` + ## Directory Download -**`SWS`** supports downloading the content of a directory as a single file when **Directory Listing** feature is enabled. To activate, specify the list of download format to enable using the `--directory-listing-download` flag or the equivalent [SERVER_DIRECTORY_LISTING_DOWNLOAD](./../configuration/env#server_directory_listing_download) env. Currently, `targz` format is supported. +**`SWS`** supports downloading the content of a directory as a single file when **Directory Listing** feature is enabled. To activate, specify the list of download format to enable using the `--directory-listing-download` flag or the equivalent [SERVER_DIRECTORY_LISTING_DOWNLOAD](./../configuration/env#server-directory-listing-download) env. Currently, `targz` format is supported. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --directory-listing \ --directory-listing-download="targz" ``` When **Directory Download** is enabled, append `?download` to a directory URL to download it. A link will also be added to the top part of **HTML** output format. + +When [symlinks are followed](./follow-symlinks.md), the archive skips entries whose target resolves outside the root directory. diff --git a/src/v3/features/docker.md b/src/v3/features/docker.md index 52ce3fb..5da7642 100644 --- a/src/v3/features/docker.md +++ b/src/v3/features/docker.md @@ -63,25 +63,25 @@ To run SWS, there are several Docker image variants that you can use. ### Scratch (just the binary) ```sh -docker run --rm -it -p 8787:8787 joseluisq/static-web-server:3.0.0-beta.1 -g info +docker run --rm -it -p 8080:8080 joseluisq/static-web-server:3.0.0-beta.2 -g info # or -docker run --rm -it -p 8787:8787 ghcr.io/static-web-server/static-web-server:3.0.0-beta.1 -g info +docker run --rm -it -p 8080:8080 ghcr.io/static-web-server/static-web-server:3.0.0-beta.2 -g info ``` ### Alpine ```sh -docker run --rm -it -p 8787:8787 joseluisq/static-web-server:3.0.0-beta.1-alpine -g info +docker run --rm -it -p 8080:8080 joseluisq/static-web-server:3.0.0-beta.2-alpine -g info # or -docker run --rm -it -p 8787:8787 ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-alpine -g info +docker run --rm -it -p 8080:8080 ghcr.io/static-web-server/static-web-server:3.0.0-beta.2-alpine -g info ``` ### Debian ```sh -docker run --rm -it -p 8787:8787 joseluisq/static-web-server:3.0.0-beta.1-debian -g info +docker run --rm -it -p 8080:8080 joseluisq/static-web-server:3.0.0-beta.2-debian -g info # or -docker run --rm -it -p 8787:8787 ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-debian -g info +docker run --rm -it -p 8080:8080 ghcr.io/static-web-server/static-web-server:3.0.0-beta.2-debian -g info ``` ## FIPS-validated TLS @@ -96,7 +96,7 @@ Platforms supported by FIPS images are `linux/amd64` and `linux/arm64` only. To verify that the image supports FIPS mode, check the output of the following command: ```sh{3} -docker run --rm ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-fips -V | grep -i "fips" +docker run --rm ghcr.io/static-web-server/static-web-server:3.0.0-beta.2-fips -V | grep -i "fips" # FIPS Mode: # Module Version: AWS-LC-FIPS 3.0.x # Crypto Provider: aws-lc-rs (via aws-lc-fips-sys) @@ -106,25 +106,25 @@ docker run --rm ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-fips -V ```sh # Scratch (just the binary) -docker run --rm -it -p 8787:8787 joseluisq/static-web-server:3.0.0-beta.1-fips -g info +docker run --rm -it -p 8080:8080 joseluisq/static-web-server:3.0.0-beta.2-fips -g info # or -docker run --rm -it -p 8787:8787 ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-fips -g info +docker run --rm -it -p 8080:8080 ghcr.io/static-web-server/static-web-server:3.0.0-beta.2-fips -g info ``` ### Alpine {#fips-alpine} ```sh -docker run --rm -it -p 8787:8787 joseluisq/static-web-server:3.0.0-beta.1-fips-alpine -g info +docker run --rm -it -p 8080:8080 joseluisq/static-web-server:3.0.0-beta.2-fips-alpine -g info # or -docker run --rm -it -p 8787:8787 ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-fips-alpine -g info +docker run --rm -it -p 8080:8080 ghcr.io/static-web-server/static-web-server:3.0.0-beta.2-fips-alpine -g info ``` ### Debian {#fips-debian} ```sh -docker run --rm -it -p 8787:8787 joseluisq/static-web-server:3.0.0-beta.1-fips-debian -g info +docker run --rm -it -p 8080:8080 joseluisq/static-web-server:3.0.0-beta.2-fips-debian -g info # or -docker run --rm -it -p 8787:8787 ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-fips-debian -g info +docker run --rm -it -p 8080:8080 ghcr.io/static-web-server/static-web-server:3.0.0-beta.2-fips-debian -g info ``` ### Development @@ -133,9 +133,9 @@ Additionally, we publish **development** Docker images based on `master` branch ```sh # Scratch (just the binary) -docker run --rm -it -p 8787:8787 ghcr.io/static-web-server/static-web-server:devel -g info +docker run --rm -it -p 8080:8080 ghcr.io/static-web-server/static-web-server:devel -g info # Debian -docker run --rm -it -p 8787:8787 ghcr.io/static-web-server/static-web-server:devel-debian -g info +docker run --rm -it -p 8080:8080 ghcr.io/static-web-server/static-web-server:devel-debian -g info ``` ## Dockerfile @@ -145,27 +145,27 @@ SWS Docker images can be extended as needed. Extending the **Scratch** Docker image (just the binary) ```Dockerfile -FROM joseluisq/static-web-server:3.0.0-beta.1 +FROM joseluisq/static-web-server:3.0.0-beta.2 # or -FROM ghcr.io/static-web-server/static-web-server:3.0.0-beta.1 +FROM ghcr.io/static-web-server/static-web-server:3.0.0-beta.2 # do stuff... ``` Or the **Alpine** ```Dockerfile -FROM joseluisq/static-web-server:3.0.0-beta.1-alpine +FROM joseluisq/static-web-server:3.0.0-beta.2-alpine # or -FROM ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-alpine +FROM ghcr.io/static-web-server/static-web-server:3.0.0-beta.2-alpine # do stuff... ``` Or the **Debian** ```Dockerfile -FROM joseluisq/static-web-server:3.0.0-beta.1-debian +FROM joseluisq/static-web-server:3.0.0-beta.2-debian # or -FROM ghcr.io/static-web-server/static-web-server:3.0.0-beta.1-debian +FROM ghcr.io/static-web-server/static-web-server:3.0.0-beta.2-debian # do stuff... ``` @@ -178,10 +178,10 @@ version: '3.3' services: website: - image: joseluisq/static-web-server:3.0.0-beta.1-alpine + image: joseluisq/static-web-server:3.0.0-beta.2-alpine container_name: 'website' ports: - - 8787:8787 + - 8080:8080 restart: unless-stopped environment: # Note: those envs are customizable but also optional @@ -220,7 +220,7 @@ services: - '/var/run/docker.sock:/var/run/docker.sock:ro' website: - image: joseluisq/static-web-server:3.0.0-beta.1 + image: joseluisq/static-web-server:3.0.0-beta.2 environment: # Note: those envs are customizable but also optional - SERVER_ROOT=/public @@ -229,14 +229,14 @@ services: # Or use an existing Docker volume # - website_data:/public ports: - - '8787:8787' + - '8080:8080' labels: - 'traefik.enable=true' - 'traefik.docker.network=traefik_net' - 'traefik.http.routers.website.entrypoints=web' - 'traefik.http.routers.website.rule=Host(`website.localhost`)' - 'traefik.http.routers.website.priority=1' - - 'traefik.http.services.website.loadbalancer.server.port=8787' + - 'traefik.http.services.website.loadbalancer.server.port=8080' networks: - traefik_net @@ -261,13 +261,13 @@ metadata: spec: containers: - name: sws - image: ghcr.io/static-web-server/static-web-server:3.0.0-beta.1 + image: ghcr.io/static-web-server/static-web-server:3.0.0-beta.2 command: - static-web-server - --root=/public - --health ports: - - containerPort: 8787 + - containerPort: 8080 name: http livenessProbe: httpGet: diff --git a/src/v3/features/error-pages.md b/src/v3/features/error-pages.md index 54fa704..3f90606 100644 --- a/src/v3/features/error-pages.md +++ b/src/v3/features/error-pages.md @@ -6,7 +6,7 @@ outline: deep **`SWS`** provides custom HTML error pages for the HTTP `404` and `50x` status errors. -This feature is enabled by default and can be controlled either by the string `--page404` ([SERVER_ERROR_PAGE_404](./../configuration/env.md#server_error_page_404)) or the `--page50x` ([SERVER_ERROR_PAGE_50X](./../configuration/env.md#server_error_page_50x)) arguments. +This feature is enabled by default and can be controlled either by the string `--page404` ([SERVER_ERROR_PAGE_404](./../configuration/env.md#server-error-page-404)) or the `--page50x` ([SERVER_ERROR_PAGE_50X](./../configuration/env.md#server-error-page-50x)) arguments. > [!INFO] Tip > @@ -16,7 +16,7 @@ Below is an example of how to customize those HTML pages. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --page404 ./my-page-404.html \ --page50x ./my-page-50x.html @@ -40,7 +40,7 @@ It can be set with the `SERVER_FALLBACK_PAGE` environment variable or with the C ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --page-fallback ./public/index.html ``` diff --git a/src/v3/features/etag.md b/src/v3/features/etag.md index dd181de..a4d7ba9 100644 --- a/src/v3/features/etag.md +++ b/src/v3/features/etag.md @@ -16,10 +16,10 @@ W/"-" > > A weak ETag indicates that two representations are _semantically equivalent_ even if byte-for-byte different. This is correct for content negotiation scenarios. For example, a pre-compressed `.br` variant of a file shares the same weak ETag as the original. SWS emits `Vary: Accept-Encoding` so that intermediary caches key variants separately. -This feature is enabled by default and can be controlled by the boolean `--etag` option or the equivalent [SERVER_ETAG](../configuration/env#server_etag) env. +This feature is enabled by default and can be controlled by the boolean `--etag` option or the equivalent [SERVER_ETAG](../configuration/env#server-etag) env. ```sh -static-web-server --port 8787 --root ./public --etag +static-web-server --port 8080 --root ./public --etag ``` ## How it works @@ -115,7 +115,7 @@ When the [In-Memory Cache](memory-cache.md) is enabled, the ETag `HeaderValue` i To omit the `ETag` header from all responses: ```sh -static-web-server --port=8787 --root=./public --etag=false +static-web-server --port=8080 --root=./public --etag=false ``` When disabled, SWS falls back to date-based validation via `Last-Modified`, `If-Modified-Since`, and `If-Unmodified-Since`. diff --git a/src/v3/features/file-descriptor-socket-passing.md b/src/v3/features/file-descriptor-socket-passing.md index fba2308..8184efc 100644 --- a/src/v3/features/file-descriptor-socket-passing.md +++ b/src/v3/features/file-descriptor-socket-passing.md @@ -10,7 +10,7 @@ outline: deep > > The [Socket Activation](http://0pointer.de/blog/projects/socket-activation.html) model is an alternative to TCP port binding. -Socket activation is supported by the `-f, --fd` option or the equivalent [SERVER_LISTEN_FD](./../configuration/env.md#server_listen_fd) env. +Socket activation is supported by the `-f, --fd` option or the equivalent [SERVER_LISTEN_FD](./../configuration/env.md#server-listen-fd) env. If you are using `inetd`, its "`wait`" option should be used in conjunction with static-web-server's `--fd 0` option. diff --git a/src/v3/features/follow-symlinks.md b/src/v3/features/follow-symlinks.md index efb889d..76ce262 100644 --- a/src/v3/features/follow-symlinks.md +++ b/src/v3/features/follow-symlinks.md @@ -8,7 +8,7 @@ outline: deep As a result, SWS will respond with a `403 Forbidden` status if a symlink is requested and that symlink won't also be shown in the [directory listing](./../features/directory-listing.md) (if enabled). -This behaviour is controlled by the boolean `--follow-symlinks` option (disabled by default) or the equivalent [SERVER_FOLLOW_SYMLINKS](./../configuration/env#server_follow_symlinks) env. +This behaviour is controlled by the boolean `--follow-symlinks` option (disabled by default) or the equivalent [SERVER_FOLLOW_SYMLINKS](./../configuration/env#server-follow-symlinks) env. > [!INFO] Important > @@ -22,6 +22,8 @@ SWS applies a **layered security model** on every request, in this order: 2. **Symlink policy**: When `--follow-symlinks` is disabled (the default), walks each component of the requested path and checks whether any of them is a symbolic link. 3. **Hidden file policy**: When `--include-hidden` is disabled (the default), rejects dotfiles with a `404 Not Found`. +These checks run on the file actually served, not only the requested path: a [pre-compressed variant](./compression-static.md), a directory's index file or a `.html` sibling (e.g. `/about` served from `about.html`). + > [!NOTE] Note > Regardless of the `--follow-symlinks` setting, SWS **always** refuses to serve symlinks that resolve outside the web root. The containment check is always enforced. @@ -58,7 +60,7 @@ In practice, the performance impact is **negligible** for almost all deployments ## Example ```sh -static-web-server --port 8787 --root ./public --follow-symlinks +static-web-server --port 8080 --root ./public --follow-symlinks ``` [symlink_metadata]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html diff --git a/src/v3/features/graceful-shutdown.md b/src/v3/features/graceful-shutdown.md index a978b0a..3979d7b 100644 --- a/src/v3/features/graceful-shutdown.md +++ b/src/v3/features/graceful-shutdown.md @@ -15,7 +15,7 @@ Sometimes one wants to control the graceful shutdown process for different reaso In these situations, SWS allows delaying the graceful shutdown process right after a `SIGTERM` providing a _grace period_ in seconds. -The feature is disabled by default and can be controlled by the numeric `-q, --grace-period` option or its equivalent [SERVER_GRACE_PERIOD](./../configuration/env.md#server_grace_period) env. +The feature is disabled by default and can be controlled by the numeric `-q, --grace-period` option or its equivalent [SERVER_GRACE_PERIOD](./../configuration/env.md#server-grace-period) env. > [!TIP] Tip > @@ -24,7 +24,7 @@ The feature is disabled by default and can be controlled by the numeric `-q, --g Here is an example of delaying the graceful shutdown process by `10` seconds after a `SIGTERM`. ```sh -static-web-server -p 8787 -d ./public/ -g trace --grace-period 10 +static-web-server -p 8080 -d ./public/ -g trace --grace-period 10 ``` With the [health endpoints](./health-endpoint.md) enabled, `/readyz` returns `503 Service Unavailable` during the grace period. diff --git a/src/v3/features/health-endpoint.md b/src/v3/features/health-endpoint.md index ce8fab3..f33792f 100644 --- a/src/v3/features/health-endpoint.md +++ b/src/v3/features/health-endpoint.md @@ -7,7 +7,7 @@ SWS provides optional health endpoints for the Kubernetes-style [liveness and re | `/livez` | Liveness | | `/readyz` | Readiness | -This feature is disabled by default and can be controlled by the boolean `--health` option, the equivalent [SERVER_HEALTH](../configuration/env.md#server_health) env, or the `health` key in the [config file](../configuration/file.md). +This feature is disabled by default and can be controlled by the boolean `--health` option, the equivalent [SERVER_HEALTH](../configuration/env.md#server-health) env, or the `health` key in the [config file](../configuration/file.md). ```sh static-web-server --root ./public --health @@ -33,10 +33,15 @@ When the feature is enabled, SWS answers `GET` and `HEAD` requests to `/livez` a - Other methods are not handled by the health endpoints: `OPTIONS` goes on to the rest of the request pipeline (e.g. [CORS](./cors.md)), and methods SWS does not support, such as `POST`, return `405 Method Not Allowed`. - Requests are logged at `debug` level instead of the usual `info` level, to avoid noise from frequent probes. See [Logging](./logging.md#per-request-logging). -Once shutdown starts (`SIGTERM`, `SIGINT`, `SIGQUIT` or CTRL + C on Windows), `/readyz` returns `503 Service Unavailable` with the body `shutting down`, while `/livez` keeps returning `200 OK`. With a [grace period](./graceful-shutdown.md), SWS keeps serving requests during that time, so load balancers stop routing new traffic before connections close. +`/readyz` returns `503 Service Unavailable` instead, while `/livez` keeps returning `200 OK`: + +- Once shutdown starts (`SIGTERM`, `SIGINT`, `SIGQUIT` or CTRL + C on Windows), with the body `shutting down`. With a [grace period](./graceful-shutdown.md), SWS keeps serving requests during that time, so load balancers stop routing new traffic before connections close. +- While the root directory is missing or not a directory, with the body `root directory unavailable`, e.g. before a mounted volume is ready. It recovers on its own once the directory is back. + +[Metrics](./metrics.md) don't count requests to the health endpoints. ```sh -curl -i http://localhost:8787/livez +curl -i http://localhost:8080/livez ``` ```text @@ -72,7 +77,7 @@ spec: - --log-level=info - --health ports: - - containerPort: 8787 + - containerPort: 8080 name: http livenessProbe: httpGet: diff --git a/src/v3/features/http-https-redirect.md b/src/v3/features/http-https-redirect.md index 5a2fcf0..29cb5c4 100644 --- a/src/v3/features/http-https-redirect.md +++ b/src/v3/features/http-https-redirect.md @@ -6,7 +6,7 @@ outline: deep **`SWS`** provides support for redirecting HTTP requests to HTTPS via a [301 Moved Permanently](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301) redirect status response code. -This feature is disabled by default and can be controlled by the boolean `--https-redirect` option or the equivalent [SERVER_HTTPS_REDIRECT](./../configuration/env#server_https_redirect) env. +This feature is disabled by default and can be controlled by the boolean `--https-redirect` option or the equivalent [SERVER_HTTPS_REDIRECT](./../configuration/env#server-https-redirect) env. > [!INFO] TLS required > @@ -24,7 +24,7 @@ It depends on "https-redirect" option to be enabled. The default is `localhost`. ## HTTPS redirect from port -The string `--https-redirect-from-port` option represents the HTTP host port where the redirect server will listen for requests (source) to redirect them to HTTPS. It depends on "https-redirect" option to be enabled. The default is `80`. +The string `--https-redirect-from-port` option represents the HTTP host port where the redirect server will listen for requests (source) to redirect them to HTTPS. It depends on "https-redirect" option to be enabled. The default is `8080`, the same as the default `--port`, so set a different port for one of them (e.g. `--port 8443`). ## HTTPS redirect from hosts @@ -49,7 +49,7 @@ static-web-server -p 4433 -d public/ -g trace \ # HTTPS redirect options --https-redirect=true \ --https-redirect-host="localhost" \ - --https-redirect-from-port=8787 \ + --https-redirect-from-port=8080 \ --https-redirect-from-hosts="localhost" # or using multiple hostnames/IPs: # --https-redirect-from-hosts = "localhost,127.0.0.1" @@ -61,11 +61,11 @@ After running the server, the logs should look as follows. ....... 2023-06-01T22:30:17.555338Z INFO static_web_server::server: http to https redirect: enabled=true 2023-06-01T22:30:17.555349Z INFO static_web_server::server: http to https redirect host: localhost -2023-06-01T22:30:17.555359Z INFO static_web_server::server: http to https redirect from port: 8787 +2023-06-01T22:30:17.555359Z INFO static_web_server::server: http to https redirect from port: 8080 2023-06-01T22:30:17.555368Z INFO static_web_server::server: http to https redirect from hosts: localhost 2023-06-01T22:30:17.557507Z INFO Server::start_server{addr_str="[::]:4433" threads=8}: static_web_server::server: close time.busy=0.00ns time.idle=3.00µs 2023-06-01T22:30:17.557547Z INFO static_web_server::server: http2 server is listening on https://[::]:4433 -2023-06-01T22:30:17.557583Z INFO Server::start_server{addr=[::]:8787 threads=8}: static_web_server::server: close time.busy=0.00ns time.idle=1.92µs -2023-06-01T22:30:17.557596Z INFO static_web_server::server: http1 redirect server is listening on http://[::]:8787 +2023-06-01T22:30:17.557583Z INFO Server::start_server{addr=[::]:8080 threads=8}: static_web_server::server: close time.busy=0.00ns time.idle=1.92µs +2023-06-01T22:30:17.557596Z INFO static_web_server::server: http1 redirect server is listening on http://[::]:8080 2023-06-01T22:30:17.557768Z INFO static_web_server::server: press ctrl+c to shut down the servers ``` diff --git a/src/v3/features/http-methods.md b/src/v3/features/http-methods.md index e87a1d7..a29c75b 100644 --- a/src/v3/features/http-methods.md +++ b/src/v3/features/http-methods.md @@ -15,7 +15,7 @@ The HTTP [OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPT Example using an HTTP client. ```sh -curl -I -X OPTIONS http://localhost:8787/assets/main.js +curl -I -X OPTIONS http://localhost:8080/assets/main.js # HTTP/1.1 204 No Content # allow: OPTIONS, HEAD, GET # accept-ranges: bytes @@ -34,13 +34,13 @@ The HTTP [OPTIONS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPT Example using an HTTP client. ```sh -curl http://localhost:8787/assets/main.js \ +curl http://localhost:8080/assets/main.js \ -I -X OPTIONS \ -H "Access-Control-Request-Method: HEAD" \ -H "Access-Control-Request-Headers: content-type" \ - -H "Origin: http://localhost:8787" + -H "Origin: http://localhost:8080" # HTTP/1.1 204 No Content -# access-control-allow-origin: http://localhost:8787 +# access-control-allow-origin: http://localhost:8080 # accept-ranges: bytes # access-control-allow-headers: content-type, origin # access-control-allow-methods: GET, OPTIONS, HEAD diff --git a/src/v3/features/http1.md b/src/v3/features/http1.md index 3c17fa7..ba0168c 100644 --- a/src/v3/features/http1.md +++ b/src/v3/features/http1.md @@ -1,6 +1,6 @@ # HTTP/1 -The HTTP/1.1 is the protocol by default and can be used by specifying a host address via the `-a, --host` ([SERVER_HOST](./../configuration/env#server_host)) argument, the port of the host via `-p, --port` ([SERVER_PORT](./../configuration/env#server_port)) and the directory of the static files using the `-d, --root` ([SERVER_ROOT](./../configuration/env#server_root)) argument. +The HTTP/1.1 is the protocol by default and can be used by specifying a host address via the `-a, --host` ([SERVER_HOST](./../configuration/env#server-host)) argument, the port of the host via `-p, --port` ([SERVER_PORT](./../configuration/env#server-port)) and the directory of the static files using the `-d, --root` ([SERVER_ROOT](./../configuration/env#server-root)) argument. > [!INFO] Tips > @@ -13,6 +13,6 @@ Below is an example of how to run the server using HTTP/1. ```sh static-web-server \ --host 127.0.0.1 \ - --port 8787 \ + --port 8080 \ --root ./my-public-dir ``` diff --git a/src/v3/features/http2.md b/src/v3/features/http2.md index 600abc1..6963e63 100644 --- a/src/v3/features/http2.md +++ b/src/v3/features/http2.md @@ -19,7 +19,7 @@ Below is an example of how to run the server with HTTP/2 over TLS. ```sh static-web-server \ --host 127.0.0.1 \ - --port 8787 \ + --port 8080 \ --root ./my-public-dir \ --tls \ --tls-cert ./my-tls.cert \ diff --git a/src/v3/features/include-hidden.md b/src/v3/features/include-hidden.md index c65ae70..89e00a2 100644 --- a/src/v3/features/include-hidden.md +++ b/src/v3/features/include-hidden.md @@ -8,7 +8,7 @@ outline: deep As a result, SWS will respond with a `404 Not Found` status for hidden files, and they will not appear in the [directory listing](./directory-listing.md) if enabled. -This behaviour is controlled by the boolean `--include-hidden` option (disabled by default) or the equivalent [SERVER_INCLUDE_HIDDEN](./../configuration/env#server_include_hidden) env. +This behaviour is controlled by the boolean `--include-hidden` option (disabled by default) or the equivalent [SERVER_INCLUDE_HIDDEN](./../configuration/env#server-include-hidden) env. ## Security considerations @@ -42,7 +42,7 @@ Enable serving of hidden files: ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./public \ --directory-listing \ --include-hidden diff --git a/src/v3/features/logging.md b/src/v3/features/logging.md index 214af4f..90d6c69 100644 --- a/src/v3/features/logging.md +++ b/src/v3/features/logging.md @@ -8,13 +8,13 @@ outline: deep ## Log Level -Set the level with the `-g, --log-level` option or the equivalent [SERVER_LOG_LEVEL](./../configuration/env#server_log_level) env. +Set the level with the `-g, --log-level` option or the equivalent [SERVER_LOG_LEVEL](./../configuration/env#server-log-level) env. Values are `error`, `warn`, `info`, `debug` and `trace`. The default is `error`, which is suited for production. ```sh static-web-server \ - --port 8787 \ + --port 8080 \ --root ./my-public-dir \ --log-level "info" ``` @@ -23,20 +23,20 @@ The default `error` level keeps production output limited to failures. Use `info ## Log Format -SWS writes logs as single-line JSON by default. Set the format with the `--log-format` option or the equivalent [SERVER_LOG_FORMAT](./../configuration/env#server_log_format) env. +SWS writes logs as single-line JSON by default. Set the format with the `--log-format` option or the equivalent [SERVER_LOG_FORMAT](./../configuration/env#server-log-format) env. ### JSON format The `json` format writes one JSON object per line to standard error. Every entry contains `timestamp`, `level`, `message` and `target`. Additional fields depend on the event. ```sh -static-web-server -p 8787 -d ./public/ -g info --log-format json +static-web-server -p 8080 -d ./public/ -g info --log-format json ``` ```json {"timestamp":"2026-05-29T23:42:41.505241+02:00","level":"INFO","message":"starting Static Web Server","name":"static-web-server","version":"2.42.0","target":"static_web_server::server"} {"timestamp":"2026-05-29T23:42:41.506488+02:00","level":"INFO","message":"log level","log_level":"info","target":"static_web_server::server"} -{"timestamp":"2026-05-29T23:42:41.506629+02:00","level":"INFO","message":"server bound to tcp socket","addr":"[::]:8787","target":"static_web_server::server"} +{"timestamp":"2026-05-29T23:42:41.506629+02:00","level":"INFO","message":"server bound to tcp socket","addr":"[::]:8080","target":"static_web_server::server"} {"timestamp":"2026-05-29T23:42:41.506659+02:00","level":"INFO","message":"runtime worker threads","worker_threads":4,"target":"static_web_server::server"} ``` @@ -52,7 +52,7 @@ Feature configuration logs share a common `enabled` boolean field: The `pretty` format writes human-readable text, suited for local development. Fields appear after the message as `key=value` pairs. ```sh -static-web-server -p 8787 -d ./public/ -g info --log-format pretty +static-web-server -p 8080 -d ./public/ -g info --log-format pretty ``` ```log @@ -82,7 +82,7 @@ $ TZ="America/Lima" \ ## Log output with ANSI -The `pretty` format does not output ANSI escape codes by default. Enable colors and other text formatting with the boolean `--log-with-ansi` option or the equivalent [SERVER_LOG_WITH_ANSI](./../configuration/env#server_log_with_ansi) env. This option has no effect on the `json` format. +The `pretty` format does not output ANSI escape codes by default. Enable colors and other text formatting with the boolean `--log-with-ansi` option or the equivalent [SERVER_LOG_WITH_ANSI](./../configuration/env#server-log-with-ansi) env. This option has no effect on the `json` format. ```sh static-web-server -p 8788 -d ./public/ -g trace --log-format pretty --log-with-ansi @@ -110,7 +110,7 @@ No line is logged when SWS fails to build a response. The `remote_addr`, `x_real SWS adds the client's _Remote Address (IP)_ to each request log. -This feature is disabled by default. Enable it with the boolean `--log-remote-address` option or the equivalent [SERVER_LOG_REMOTE_ADDRESS](./../configuration/env#server_log_remote_address) env. When enabled, request logs include a `remote_addr` field. +This feature is disabled by default. Enable it with the boolean `--log-remote-address` option or the equivalent [SERVER_LOG_REMOTE_ADDRESS](./../configuration/env#server-log-remote-address) env. When enabled, request logs include a `remote_addr` field. ```sh static-web-server -a "0.0.0.0" -p 8080 -d docker/public/ -g info --log-remote-address=true @@ -125,7 +125,7 @@ static-web-server -a "0.0.0.0" -p 8080 -d docker/public/ -g info --log-remote-ad Some upstream proxies report the client's real IP address in the `X-Real-IP` header. -Enable logging of the `X-Real-IP` header with the `--log-x-real-ip` option or the equivalent [SERVER_LOG_X_REAL_IP](../configuration/env#server_log_x_real_ip) env. When enabled, request logs include an `x_real_ip` field. +Enable logging of the `X-Real-IP` header with the `--log-x-real-ip` option or the equivalent [SERVER_LOG_X_REAL_IP](../configuration/env#server-log-x-real-ip) env. When enabled, request logs include an `x_real_ip` field. ```json { @@ -142,17 +142,19 @@ Enable logging of the `X-Real-IP` header with the `--log-x-real-ip` option or th If the value of the `X-Real-IP` header does not parse as an IP address, the `x_real_ip` field is omitted. -To restrict logging to requests that originate from trusted proxy IPs, use the `--trusted-proxies` option or the equivalent [SERVER_TRUSTED_PROXIES](../configuration/env#server_trusted_proxies) env. This is a comma-separated list of IPs. An empty list (the default) trusts all IPs. +The header is logged only for requests from a [trusted proxy](#trusted-proxies). -## Logging Client IP from X-Forwarded-For header +## Trusted proxies -> Note: Trust this header only when the upstream handles `X-Forwarded-For` securely and the `--trusted-proxies` option is set. +Any client can send `X-Real-IP` or `X-Forwarded-For`, so SWS logs these headers only for requests coming from the proxy IPs listed in the `--trusted-proxies` option or the equivalent [SERVER_TRUSTED_PROXIES](../configuration/env#server-trusted-proxies) env, a comma-separated list of IPs. -When SWS runs behind a reverse proxy, the `remote_addr` field reports the proxy's IP address, not the client's. The proxy can be configured to send the [X-Forwarded-For header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For), a comma-separated list of IP addresses that starts with the _real remote client IP_ followed by intermediate proxies. +The list is empty by default, which trusts no one: the headers are never logged until it is set, and SWS logs a warning at startup when they are set to be logged without trusted proxies. IPv4 entries also match their IPv4-mapped IPv6 form (`::ffff:a.b.c.d`), as seen on the default dual-stack `::` listener. Over a [Unix domain socket](./unix-domain-socket.md) peers have no IP address, so the headers are never logged. -Enable logging of the real remote IP with the `--log-forwarded-for` option or the equivalent [SERVER_LOG_FORWARDED_FOR](../configuration/env#server_log_forwarded_for) env. By default this logs every request with a correctly formatted `X-Forwarded-For` header, adding a `real_remote_ip` field. +## Logging Client IP from X-Forwarded-For header + +When SWS runs behind a reverse proxy, the `remote_addr` field reports the proxy's IP address, not the client's. The proxy can be configured to send the [X-Forwarded-For header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For), a comma-separated list of IP addresses that starts with the _real remote client IP_ followed by intermediate proxies. -Because any proxy in the chain can change the `X-Forwarded-For` header, the reported IP may not be trustworthy. Restrict logging to trusted proxy IPs with the `--trusted-proxies` option or the equivalent [SERVER_TRUSTED_PROXIES](../configuration/env#server_trusted_proxies) env. This is a comma-separated list of IPs. An empty list (the default) trusts all IPs. +Enable logging of the real remote IP with the `--log-forwarded-for` option or the equivalent [SERVER_LOG_FORWARDED_FOR](../configuration/env#server-log-forwarded-for) env. Requests from a [trusted proxy](#trusted-proxies) with a correctly formatted `X-Forwarded-For` header then get a `real_remote_ip` field with its first address. Command used for the following examples: @@ -165,7 +167,7 @@ Look for these lines in the log output: ```json {"timestamp":"2026-05-29T23:43:47.071681+02:00","level":"INFO","message":"log level","log_level":"info","target":"static_web_server::server"} {"timestamp":"2026-05-29T23:43:47.071700+02:00","level":"INFO","message":"log X-Forwarded-For header","enabled":true,"target":"static_web_server::log_addr"} -{"timestamp":"2026-05-29T23:43:47.071710+02:00","level":"INFO","message":"trusted IPs for X-Forwarded-For","trusted_proxies":"[::1]","target":"static_web_server::log_addr"} +{"timestamp":"2026-05-29T23:43:47.071710+02:00","level":"INFO","message":"trusted IPs for X-Real-IP and X-Forwarded-For","trusted_proxies":"[::1]","target":"static_web_server::log_addr"} ``` Simulate a request from behind a reverse proxy with an additional intermediate proxy: @@ -239,10 +241,10 @@ curl "http://[::1]:8080" --header "X-Forwarded-For: