From 440ab497da4dc1b88b6fbdc01b3a91335a6683b2 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Sat, 31 Oct 2026 12:33:42 +0100 Subject: [PATCH 1/4] docs: replace /health with /livez and /readyz probes (v3) Document the Kubernetes-style `/livez` (liveness) and `/readyz` (readiness) health endpoints that replace `/health` in v3, and add the change to the v2 to v3 migration guide. Co-Authored-By: Claude Opus 5.5 --- src/v3/configuration/cli.md | 2 +- src/v3/configuration/env.md | 2 +- src/v3/configuration/file.md | 2 +- src/v3/defaults.md | 2 +- src/v3/features/custom-http-headers.md | 2 +- src/v3/features/docker.md | 7 ++- src/v3/features/health-endpoint.md | 66 ++++++++++++++++++++++---- src/v3/features/logging.md | 2 +- src/v3/index.md | 2 +- src/v3/migration-guide.md | 15 ++++++ 10 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/v3/configuration/cli.md b/src/v3/configuration/cli.md index 4642d32..c401d2b 100644 --- a/src/v3/configuration/cli.md +++ b/src/v3/configuration/cli.md @@ -121,7 +121,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 a /health endpoint that doesn't generate any log entry and returns a 200 status code. This is especially useful with Kubernetes liveness and readiness probes [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` 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 1a85782..c8daeba 100644 --- a/src/v3/configuration/env.md +++ b/src/v3/configuration/env.md @@ -213,7 +213,7 @@ Set a default `charset=utf-8` parameter on limited set of `text` responses that ## SERVER_HEALTH -Add a `/health` endpoint that doesn't generate any log entry and returns a `200` status code. This is especially useful with Kubernetes liveness and readiness probes. Default `false` (disabled). +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). ## SERVER_METRICS diff --git a/src/v3/configuration/file.md b/src/v3/configuration/file.md index 38ab096..e3b4335 100644 --- a/src/v3/configuration/file.md +++ b/src/v3/configuration/file.md @@ -98,7 +98,7 @@ use-relative-root = false #### Check for existing pre-compressed files compression-static = true -#### Health-check endpoint (GET or HEAD `/health`) +#### Health-check endpoints (GET or HEAD `/livez` and `/readyz`) health = false #### Markdown content negotiation diff --git a/src/v3/defaults.md b/src/v3/defaults.md index 207d3a7..c4b38f1 100644 --- a/src/v3/defaults.md +++ b/src/v3/defaults.md @@ -104,7 +104,7 @@ The types considered compressible include all `text/*` MIME types, plus `applica | Setting | Default | Rationale | | ---------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------ | -| Health endpoint | disabled | Opt-in via `--health`. Returns `200 OK` at `/health` with no log noise. Designed for Kubernetes probes. | +| Health endpoints | disabled | Opt-in via `--health`. Returns `200 OK` at `/livez` and `/readyz` with no log noise. For Kubernetes probes. | | 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/features/custom-http-headers.md b/src/v3/features/custom-http-headers.md index 451acea..d79df35 100644 --- a/src/v3/features/custom-http-headers.md +++ b/src/v3/features/custom-http-headers.md @@ -52,7 +52,7 @@ SWS validates the codes at startup and fails to start on an empty array or a cod An optional string that selects which responses the entry applies to: - `files` (default): responses of the static file handler only, including error responses such as `404 Not Found` and plain [URL rewrites](url-rewrites.md), which are served as files. -- `all`: also the responses SWS returns before serving a file: [URL redirects](url-redirects.md), [URL rewrites](url-rewrites.md) with `redirect`, the [health endpoint](health-endpoint.md), the [metrics endpoint](metrics.md), CORS requests rejected with `403 Forbidden`, [Basic Authentication](basic-authentication.md) challenges, [maintenance mode](maintenance-mode.md) and `405 Method Not Allowed`. +- `all`: also the responses SWS returns before serving a file: [URL redirects](url-redirects.md), [URL rewrites](url-rewrites.md) with `redirect`, the [health endpoints](health-endpoint.md), the [metrics endpoint](metrics.md), CORS requests rejected with `403 Forbidden`, [Basic Authentication](basic-authentication.md) challenges, [maintenance mode](maintenance-mode.md) and `405 Method Not Allowed`. These early responses serve no file, so `source` is matched against the request path as is. `scope` can be combined with `status`. diff --git a/src/v3/features/docker.md b/src/v3/features/docker.md index 6bddbf5..52ce3fb 100644 --- a/src/v3/features/docker.md +++ b/src/v3/features/docker.md @@ -268,9 +268,14 @@ spec: - --health ports: - containerPort: 8787 + name: http livenessProbe: httpGet: - path: /health + path: /livez + port: http + readinessProbe: + httpGet: + path: /readyz port: http ``` diff --git a/src/v3/features/health-endpoint.md b/src/v3/features/health-endpoint.md index ee6f4f7..4aa03af 100644 --- a/src/v3/features/health-endpoint.md +++ b/src/v3/features/health-endpoint.md @@ -1,15 +1,61 @@ # Health endpoint -SWS provides an optional `/health` endpoint that can be used to check if it is running properly. -When the `/health` is requested, SWS will generate a log only at the `debug` level instead of the usual `info` level for a regular file. +SWS provides optional health endpoints for the Kubernetes-style [liveness and readiness probes](https://kubernetes.io/docs/reference/using-api/health-checks/): -The HTTP methods supported are `GET` and `HEAD`. +| Path | Probe | +| --------- | --------- | +| `/livez` | Liveness | +| `/readyz` | Readiness | -This feature is disabled by default and can be controlled by the boolean `--health` option or the equivalent [SERVER_HEALTH](../configuration/env.md#server_health) env. +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). -## Usage with Kubernetes liveness probe +```sh +static-web-server --root ./public --health +SERVER_HEALTH=true static-web-server --root ./public +``` + +```toml +[general] +health = true +``` + +> [!WARNING] `/health` was removed in v3 +> SWS v2 served a single `/health` endpoint. SWS v3 serves `/livez` and `/readyz` instead, and requests to `/health` go to the static file handler like any other path. See the [migration guide](../migration-guide.md#_10-health-endpoint-replaced-by-livez-and-readyz). + +## Behavior + +When the feature is enabled, SWS answers `GET` and `HEAD` requests to `/livez` and `/readyz` with `200 OK`: + +- `GET` returns the body `OK` with `Content-Type: text/plain; charset=utf-8`. +- `HEAD` returns the same headers with an empty body. +- The query string is ignored, so `/livez?verbose` and `/readyz?verbose` also return `200 OK`. +- Only the exact paths match: `/livez/`, `/readyz/` or `/LIVEZ` go to the static file handler. +- 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). + +SWS keeps no state that could make it alive but not ready, so both endpoints return the same response. + +```sh +curl -i http://localhost:8787/livez +``` -The health endpoint is well suited for the Kubernetes liveness probe: +```text +HTTP/1.1 200 OK +content-type: text/plain; charset=utf-8 +content-length: 2 + +OK +``` + +When the feature is disabled, `/livez` and `/readyz` are regular paths served from the root directory. + +## Interaction with other features + +The health endpoints run before [CORS](./cors.md), [Basic Authentication](./basic-authentication.md), [maintenance mode](./maintenance-mode.md), [URL redirects](./url-redirects.md) and [URL rewrites](./url-rewrites.md), so probes succeed without credentials and while maintenance mode is on. + +[Custom HTTP headers](./custom-http-headers.md) apply to the health endpoints only with `scope = "all"`. + +## Usage with Kubernetes probes ```yaml apiVersion: v1 @@ -26,10 +72,14 @@ spec: - --log-level=info - --health ports: - - containerPort: 80 + - containerPort: 8787 name: http livenessProbe: httpGet: - path: /health + path: /livez + port: http + readinessProbe: + httpGet: + path: /readyz port: http ``` diff --git a/src/v3/features/logging.md b/src/v3/features/logging.md index 781ebee..612d443 100644 --- a/src/v3/features/logging.md +++ b/src/v3/features/logging.md @@ -90,7 +90,7 @@ static-web-server -p 8788 -d ./public/ -g trace --log-format pretty --log-with-a ## Per-request logging -When the level is `info` or lower, SWS logs each request with the `incoming request` message. Requests to the health endpoint are logged at `debug` level instead, to avoid noise from frequent probes. +When the level is `info` or lower, SWS logs each request with the `incoming request` message. Requests to the [health endpoints](./health-endpoint.md) (`/livez` and `/readyz`) are logged at `debug` level instead, to avoid noise from frequent probes. ```json { diff --git a/src/v3/index.md b/src/v3/index.md index f51e5bb..e6377e1 100644 --- a/src/v3/index.md +++ b/src/v3/index.md @@ -43,7 +43,7 @@ SWS is designed to be **simple by default, powerful when needed**. You can point | [**Trailing Slash Redirect**](./features/trailing-slash-redirect) | Enforce or remove trailing slashes on directory URLs. | | [**Error Pages**](./features/error-pages) | Replace the default 404/5xx pages with your own HTML files. | | [**Logging**](./features/logging) | Structured JSON or human-readable text combined logs to stdout or a file. | -| [**Health Endpoint**](./features/health-endpoint) | A dedicated `/health` route for load balancer and orchestrator checks. | +| [**Health Endpoint**](./features/health-endpoint) | The `/livez` and `/readyz` routes for load balancer and orchestrator checks. | | [**Metrics**](./features/metrics) | Prometheus-compatible metrics endpoint for monitoring and alerting. | | [**Virtual Hosting**](./features/virtual-hosting) | Serve multiple domains from a single SWS instance by host header routing. | | [**Multiple Index Files**](./features/multiple-index-files) | Fallback chain of index documents (`index.html`, `index.htm`, etc.). | diff --git a/src/v3/migration-guide.md b/src/v3/migration-guide.md index 958517a..ed3d3a0 100644 --- a/src/v3/migration-guide.md +++ b/src/v3/migration-guide.md @@ -116,6 +116,20 @@ The v2 supports running SWS as a WASM/WASI binary (via Wasmer). However, v3 stil **Action:** For now, if you run SWS on WASM/WASI, stay on v2 or migrate to a native binary (Linux, macOS, Windows). +### 10. Health endpoint replaced by `/livez` and `/readyz` + +| | v2 | v3 | +| --------------- | ---------- | ---------------------- | +| Liveness probe | `/health` | `/livez` | +| Readiness probe | `/health` | `/readyz` | +| Enable | `--health` | `--health` (unchanged) | + +v3 no longer serves `/health`. With `--health` enabled, requests to `/health` go to the static file handler like any other path, which usually returns `404 Not Found`. The `--health` flag, the `SERVER_HEALTH` env var and the `health` TOML key are unchanged. + +**Action:** Point liveness probes at `/livez` and readiness probes at `/readyz`. Update any load balancer or monitoring check that requests `/health`. + +See the [Health endpoint](./features/health-endpoint.md) page for more details. + ## Quick migration checklist 1. **Port:** Add `--port 80` if you were relying on the default. @@ -127,3 +141,4 @@ The v2 supports running SWS as a WASM/WASI binary (via Wasmer). However, v3 stil 7. **Logging:** Add `--log-format pretty` if you prefer old-style human-readable logs. 8. **TOML `text-charset`:** Change `"utf-8"` string to `true` boolean. 9. **WebAssembly:** Migrate to a native binary — Wasm is no longer supported. +10. **Health endpoint:** Replace `/health` with `/livez` (liveness) and `/readyz` (readiness) in probes and checks. From c43560209ec4f0c8b73789f52fe52e41d3c78610 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Sat, 31 Oct 2026 12:41:28 +0100 Subject: [PATCH 2/4] docs: align health endpoint row in v3 features table Co-Authored-By: Claude Opus 5.5 --- src/v3/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v3/index.md b/src/v3/index.md index e6377e1..5ba1300 100644 --- a/src/v3/index.md +++ b/src/v3/index.md @@ -43,7 +43,7 @@ SWS is designed to be **simple by default, powerful when needed**. You can point | [**Trailing Slash Redirect**](./features/trailing-slash-redirect) | Enforce or remove trailing slashes on directory URLs. | | [**Error Pages**](./features/error-pages) | Replace the default 404/5xx pages with your own HTML files. | | [**Logging**](./features/logging) | Structured JSON or human-readable text combined logs to stdout or a file. | -| [**Health Endpoint**](./features/health-endpoint) | The `/livez` and `/readyz` routes for load balancer and orchestrator checks. | +| [**Health Endpoint**](./features/health-endpoint) | The `/livez` and `/readyz` routes for load balancer and orchestrator checks. | | [**Metrics**](./features/metrics) | Prometheus-compatible metrics endpoint for monitoring and alerting. | | [**Virtual Hosting**](./features/virtual-hosting) | Serve multiple domains from a single SWS instance by host header routing. | | [**Multiple Index Files**](./features/multiple-index-files) | Fallback chain of index documents (`index.html`, `index.htm`, etc.). | From ec9ad3afc79565be7464cff154cdb474b20a3d45 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Sat, 31 Oct 2026 13:56:24 +0100 Subject: [PATCH 3/4] docs: document /readyz 503 during the grace period Co-Authored-By: Claude Opus 5.5 --- src/v3/features/graceful-shutdown.md | 2 ++ src/v3/features/health-endpoint.md | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/v3/features/graceful-shutdown.md b/src/v3/features/graceful-shutdown.md index 87de5c1..a978b0a 100644 --- a/src/v3/features/graceful-shutdown.md +++ b/src/v3/features/graceful-shutdown.md @@ -26,3 +26,5 @@ Here is an example of delaying the graceful shutdown process by `10` seconds aft ```sh static-web-server -p 8787 -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 4aa03af..1dac960 100644 --- a/src/v3/features/health-endpoint.md +++ b/src/v3/features/health-endpoint.md @@ -33,7 +33,7 @@ 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). -SWS keeps no state that could make it alive but not ready, so both endpoints return the same response. +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. ```sh curl -i http://localhost:8787/livez @@ -83,3 +83,5 @@ spec: path: /readyz port: http ``` + +Add `--grace-period` (e.g. `--grace-period=10`) so `/readyz` fails while SWS still serves in-flight and new requests during a rollout. Keep it below the pod's `terminationGracePeriodSeconds` (default `30`). From 66bf88bc65d165b79bfe09406c43d62fbaffcf32 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Sat, 31 Oct 2026 13:56:35 +0100 Subject: [PATCH 4/4] docs: describe health probes with TLS Co-Authored-By: Claude Opus 5.5 --- src/v3/features/health-endpoint.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/v3/features/health-endpoint.md b/src/v3/features/health-endpoint.md index 1dac960..ce8fab3 100644 --- a/src/v3/features/health-endpoint.md +++ b/src/v3/features/health-endpoint.md @@ -85,3 +85,17 @@ spec: ``` Add `--grace-period` (e.g. `--grace-period=10`) so `/readyz` fails while SWS still serves in-flight and new requests during a rollout. Keep it below the pod's `terminationGracePeriodSeconds` (default `30`). + +### With TLS + +With [`--tls`](./tls.md), point the probes at the TLS port and set `scheme: HTTPS`. The kubelet skips certificate verification for HTTPS probes. + +```yaml +readinessProbe: + httpGet: + path: /readyz + port: https + scheme: HTTPS +``` + +Do not probe the [`--https-redirect`](./http-https-redirect.md) port: it accepts only `Host` values listed in `--https-redirect-from-hosts` (default `localhost`), and the kubelet sends the pod IP, so probes get `400 Bad Request`.