This beta adds Kubernetes-style health probes, configurable access logs, custom headers for early responses, connection and request header limits, alongside memory cache, compression and content negotiation fixes. It also includes security fixes. The related advisories will be published separately soon.
Added
- Configurable access log format. Added
--access-log-formatto log one line per request from%{token}placeholders such as%{remote_addr},%{status},%{bytes}and%{duration}, with environment variable and TOML support. An invalid format fails at startup. #785 by @frenzzy and @chrissnell - Access log line on body completion. The access log line is now written once the response body finishes, so
%{bytes}counts the bytes sent, including on-the-fly compression, and%{duration}covers the whole transfer. Aborted downloads log the bytes sent so far. (#795 by @joseluisq) - Custom headers for early responses. Added an optional
scopeto[[advanced.headers]]rules.scope = "all"also applies headers to responses returned before the static file handler, such as redirects,405, health probes, metrics, Basic auth challenges and the maintenance page. The defaultscope = "files"keeps the current behavior. (#784 by @frenzzy) - Connection limit. Added
--max-connectionsto limit the connections served at the same time across all listeners. New connections wait for a free slot instead of being rejected. The default0keeps connections unlimited. Connections closed by the idle or request header timeout are now logged atdebug. (#799 by @joseluisq) - Request header size limit. Added
--max-header-sizeto limit the size of request headers in bytes,16384by default, with environment variable and TOML support. For HTTP/1 it includes the request line, for HTTP/2 it applies to the header list. Larger requests get431 Request Header Fields Too Large, and a value of0fails at startup. (by @joseluisq) - Readiness check for the root directory.
/readyznow returns503while the--rootdirectory is missing or not a directory, for example on an unmounted volume, while/livezstays200. (#793 by @joseluisq)
Changed
- Breaking: Kubernetes-style health probes.
--healthnow serves/livez(liveness) and/readyz(readiness) instead of/health./readyzreturns503once a shutdown signal arrives so load balancers can drain connections during--grace-period. (#751 by @TheBigUserOne) - Breaking: Dual-stack default host. The default
--host ::now accepts IPv4 connections too on Windows, FreeBSD and Linux withnet.ipv6.bindv6only=1, matching Linux and macOS. SWS fails to start if another service already listens on0.0.0.0with the same port, and logs a warning where the OS refuses dual-stack sockets. (#804 by @joseluisq) - Breaking: HSTS only over TLS.
Strict-Transport-Securityis now sent only when--tlsis enabled, as RFC 6797 requires, and its value no longer includespreload. Behind a TLS-terminating proxy, add the header at the proxy or with an[[advanced.headers]]rule. (# by @joseluisq) - Breaking: Proxy headers require trusted proxies.
--log-x-real-ip,--log-forwarded-forand the%{x_real_ip}and%{forwarded_for}access log tokens now use the headers only for requests from--trusted-proxies, which trusts no proxy by default. SWS warns at startup when these headers are set to be logged without trusted proxies or on a Unix socket. (# by @joseluisq) - Metrics label values. Request metrics now label the host with the matching
[advanced.virtual-hosts]name, without the port, orotherwhen none matches, and the method asGET,HEAD,OPTIONSorother. (# by @joseluisq) - One log line per request. Without
--access-log-format, each request now logs a singleresponseline with itsstatusafter the response, replacing theincoming requestline. Health probes stay at thedebuglevel. (#786 by @frenzzy and @TheBigUserOne) - Health probes excluded from metrics.
/livezand/readyzrequests no longer count toward thesws_http_*request metrics. (#792 by @joseluisq) - Dependency updates. Updated dependencies and made
chronooptional again (directory-listing), reducing the--no-default-featuresbinary by about 33 KB. (#795, #806 by @joseluisq) - Docker base image updates. Updated Alpine images to
3.24.4. (#801 by @joseluisq) - AI-assisted contributor guidance. Improved the project skill files for AI-assisted tools. (#800 by @joseluisq)
Fixed
- Memory cache with directory requests. With
[advanced.memory-cache], directory listings, other--index-filesentries and<dir>.htmlsiblings no longer return404. Cached directory lookups now follow the--index-filesorder. (#802 by @joseluisq) - Content-Length for pre-compressed variants. Pre-compressed files served from disk now include their
Content-Length, also for byte ranges andHEADrequests, and count towardsws_http_response_bytes_total. (#803 by @joseluisq) - Fallback page for missing assets.
--page-fallbackno longer replaces404responses for paths with a known non-HTML extension, such as/favicon.ico, unless theAcceptheader asks for HTML. Client-side routes and browser navigations still get the fallback page. (#787 by @jadhavgaurav) - Vary header with markdown negotiation. With
--accept-markdown, HTML and markdown responses now includeVary: Accept, and existingVary: Accept-Encodingvalues no longer prevent adding it. (#789 by @davlgd) - Vary header on directory listings. With
--accept-markdown, directory listings in any format now includeVary: Accept, so shared caches keep JSON listings and markdown responses apart. (#798 by @joseluisq) - Access log filtering and CONNECT targets. The access log is built only when its log level is enabled, and
%{uri}logs the authority ofCONNECTrequests instead of/. (#794 by @joseluisq) - Compression headers on empty responses. On-the-fly compression no longer sets
Content-Encodingon1xx,204,304or empty responses, and pre-compressed files set it only on successful responses. HTTP/2 clients no longer reject304responses with compression enabled. (# by @joseluisq) - Explicit security headers setting.
--security-headers=falseis now respected together with--tls, and a config file withoutsecurity-headersno longer turns the headers back on. (# by @joseluisq) - IPv4 trusted proxies on dual-stack listeners. IPv4
--trusted-proxiesentries now match clients connecting through the default::host, which appear as IPv4-mapped IPv6 addresses. (# by @joseluisq)
Security
- Request handling hardening. Hardened request handling in Basic auth, the memory cache, static file serving and markdown negotiation. Details will follow with the related advisories. (by @joseluisq)
For more details see the v3.0.0-beta.3 milestone and the full changelog v3.0.0-beta.2...v3.0.0-beta.3.