# Static Web Server: a 4 MB Rust file server that takes itself surprisingly seriously

> Hyper and Tokio under a roughly four megabyte static binary, with basic auth, metrics, rewrites and TLS, shipping on a v2 LTS line while v3 sits in beta.

**static-web-server/static-web-server** — A cross-platform, high-performance and asynchronous web server for static files-serving. ⚡

- Repository: https://github.com/static-web-server/static-web-server
- Website: https://static-web-server.net
- Stars: 2,372 · Forks: 133
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-07 · Updated: 2026-10-07 · Language: en
- Canonical page: https://hysenlabs.com/projects/static-web-server-static-web-server

## The default branch is v3 and the supported line is v2

The first thing the README says is a note, and it is the most operationally important line in the file: this is the upcoming v3, currently in development, and for production use you should see v2 LTS on the 2.x branch.

That note creates a version picture worth spelling out, because the release history and the branch are telling you different things. The v2 line reached v2.44.0 on 2026-07-31, which includes four security advisories and announces that v2 is now LTS. The v3 line reached v3.0.0-beta.1 on 2026-07-21, ten days earlier.

So the stable, long-term-support line got a release after the next major version's first beta. That is a deliberate pattern rather than an accident, and it is the right one: v2 continues to receive fixes while v3 accumulates breaking changes, and the branch called master is where the churn is. Anyone who clones the repository and builds from the default branch gets a beta, despite the binary being describable as small, fast and production-ready.

The manifest confirms which version the tree represents. The version field reads 3.0.0-beta.1, the edition is 2024 and the minimum supported Rust version is 1.88.0, which is a recent toolchain floor.

```toml
[package]
name = "static-web-server"
version = "3.0.0-beta.1"
edition = "2024"
rust-version = "1.88.0"
```

## A roughly four megabyte binary with zero runtime dependencies

The distribution claim is specific: a single approximately 4 MB static binary with zero runtime dependencies, built against the musl libc standard so it runs on any Linux distribution or in a container with nothing installed.

That figure is the project's clearest competitive argument. The comparison people actually make is against nginx, which is excellent and which you cannot simply copy into a container as one file, or against a language runtime's built-in file server, which drags an interpreter along. Static Web Server takes a directory of files and becomes the whole deployment artefact.

The binary is not limited to serving files over HTTP either. The Cargo manifest exposes both a library target and a binary target, with the library named `static_web_server`, which means the compression, caching and file-handling logic can be embedded in a Rust application rather than only consumed as a server. Features are opt-in and default-on as a group:

```toml
default = ["compression", "http2", "tls-ring", "directory-listing", "directory-listing-download", "basic-auth", "fallback-page", "metrics", "mem-cache"]
```

Two naming details in that list are worth pausing on. There is a separate tls base feature that gates all TLS code without picking a crypto provider, then `tls-ring` as the default choice, with FIPS variants that swap in a different provider. A file server having a FIPS-capable build option tells you how much of its user base is regulated infrastructure.

Pre-built binaries are published for Linux, macOS, Windows, FreeBSD, NetBSD and Android on both x86_64 and ARM64, and container images are offered for Scratch, Alpine and Debian. Scratch is the interesting one for image size, since it is the base that has nothing in it at all.

## Compression is negotiated, and pre-compressed files are served straight off disk

On-the-fly compression covers Gzip, Deflate, Brotli and Zstandard for text-based files, driven by the `Accept-Encoding` header. That is the standard approach and it costs CPU per request for files that were already compressed at build time.

So the project also does the other thing: serving pre-compressed files directly from disk. If a `.br`, `.gz` or `.zst` variant sits next to the original, the server hands it over rather than recompressing. For a static site built by a bundler that already emits compressed assets, that is the difference between doing the work once at build time and doing it on every request forever.

Two other content-negotiation behaviours are unusual enough to notice. Markdown content negotiation means a `.md` file is served to clients that accept `text/markdown`, so a documentation directory can serve HTML or Markdown depending on what the client asks for. And there is byte-range serving for large file delivery, with what the v2.44.0 notes describe as improved byte-range suffix detection.

Caching is handled with optional Cache-Control headers and ETags with sensible defaults per file type, and the v3 release notes mention weak ETag validation as new. Defaults per file type matter more than they sound: serving a fingerprinted asset and a favicon with the same cache policy wastes bandwidth in one direction and causes staleness in the other.

## The features that put it above a plain file server

The README's feature list is long, and the honest way to read it is as a list of reasons people ended up using nginx instead. Each of these exists because something was missing.

For serving a single-page application there is a fallback page for 404 errors plus URL rewrites and redirects with placeholder replacement. For multiple sites on one host there is virtual hosting with per-host root directories. For deployment there is a health check endpoint on GET and HEAD, socket activation for systemd, and a Windows Service mode.

For operations there is structured JSON logging via a log-format option and file-based log output via a log-file option, plus a configurable thread pool for worker and blocking threads, graceful shutdown with a grace period, and a Prometheus metrics endpoint with request counts, latency histograms and connection tracking. For access control there is basic HTTP authentication using BCrypt, custom response headers per file via glob patterns, and CORS with preflight support.

That last cluster is the real differentiator for anything serving over the internet rather than on localhost. A file server that does not terminate TLS, cannot restrict access and emits no metrics is a liability in production, and each of these features closes one of those gaps.

Configuration is accepted through CLI arguments, environment variables or a TOML file, which matters for container deployments where environment variables are often the only writable surface. Maintenance mode with a configurable status is included, which is useful during a deploy where the new build is not ready yet.

## Security advisories in the current release and what they were about

The v2.44.0 release is worth reading as a document about how a static file server gets exploited, because it bundles four advisories ordered by severity and names the feature each one belongs to.

Two are moderate: pre-compressed and metrics. Two are low: basic authentication and markdown. Three of the four are in features that exist to be convenient, which is a pattern worth noticing. Pre-compressed serving trusts files sitting on disk next to the originals, metrics exposes operational data, markdown content negotiation serves a different representation than you might expect, and basic auth is a feature that is easy to misconfigure.

The same release adds a `--use-relative-root` option to resolve the webroot at request time, preserves query strings on URL redirects, and fixes an ordering issue in metrics endpoint authentication. Query string preservation on redirects sounds cosmetic until you follow a link through a redirect and lose every parameter.

The v2.43.0 release adds FIPS-capable TLS through a new Cargo feature with prebuilt binaries, several performance optimisations, hardening across several modules and extract normalisation. The v3.0.0-beta.1 release upgrades the HTTP stack to Hyper v1, introduces weak ETag validation, structured JSON logging and file logging, stabilises the in-memory cache and adds FIPS-capable TLS binaries.

The repository also carries the tooling that suggests how these get found: a `fuzz/` directory for fuzz targets, `benches/` for the CodSpeed benchmarks the README badges, and `proptest-regressions/` for property testing failures. A file server parsing path traversal input from the internet is exactly the kind of program where that combination pays for itself.

## The build targets musl and leans on Docker for cross builds

The Makefile is short and shows how releases are produced. The default package target is x86_64-unknown-linux-musl, with a Darwin target alongside it, and release names are assembled by parsing the name and version out of Cargo.toml with sed.

```makefile
PKG_TARGET=x86_64-unknown-linux-musl
PKG_TARGET_DARWIN=x86_64-apple-darwin
RUST_VERSION ?= $(shell rustc --version | cut -d ' ' -f2)
```

The lint and build targets are conventional: clippy with all features and warnings denied, and a release build against the musl target. The install target adds the musl target through rustup and installs two tools, cargo-make and cargo-audit, which is a reasonable pair for a project that publishes binaries for a dozen platforms.

Cross-compilation runs inside Docker. The `test.release`, `linux` and similar targets mount the working directory, mount three named volumes for the git cache, the registry cache and the target directory, then run inside a builder image with the detected Rust version. Caching cargo's git and registry directories across builds is the difference between a two-minute rebuild and a twenty-minute one when you are cutting a release for every supported platform.

The rest of the tree supports that workflow: `Cross.toml` for cross-compilation configuration, `docker/` for image definitions, `contrib/` and `scripts/` for packaging helpers, `systemd/` for the socket activation unit, and a `SECURITY.md` for reporting. Both LICENSE-MIT and LICENSE-APACHE are present, and the manifest declares the terms as MIT OR Apache-2.0, which is the same dual-licensing pattern used by the Rust ecosystem generally, and a more precise statement than the single Apache-2.0 value GitHub reports for the repository.

## Conclusion

Static Web Server is at its best where a CDN is too much and Python's http.server is too little: a build artifact directory on a small VM or inside a container, needing TLS, basic auth, a health endpoint and structured logs, served by one file with no runtime to install. Its feature list is longer than that of most servers in its class because each item was a reason someone reached for nginx or Caddy instead. The versioning is the part to sort out before you deploy, since the default branch is v3 in development while the supported line is v2. Start from the 2.x branch as the README directs, configure through the TOML file or environment variables rather than CLI flags alone, and turn the Prometheus endpoint on early so you can see what the server is actually doing.

## FAQ

### Should I use the v2 or v3 branch of Static Web Server in production?

Use v2 from the 2.x branch. The README's note says the default branch is the upcoming v3, currently in development, and points production users at v2 LTS. The releases make the same point from the other direction: v2.44.0 shipped on 2026-07-31, ten days after the v3.0.0-beta.1 release, and announces that v2 is now the long-term-support line.

### How does Static Web Server handle compression?

Both ways. It compresses text-based files on the fly using Gzip, Deflate, Brotli or Zstandard based on the Accept-Encoding header, and it serves pre-compressed .br, .gz and .zst files straight from disk when they exist next to the original. For a bundler that already emits compressed assets, the second path avoids recompressing on every request.

### What license is Static Web Server released under?

Dual licensed as MIT OR Apache-2.0, declared in Cargo.toml with both LICENSE-MIT and LICENSE-APACHE present in the repository. GitHub's repository field reports only Apache-2.0, so the manifest is the more complete statement of the terms.

### Does Static Web Server support HTTPS and authentication?

Yes, both. It provides HTTP/2 with TLS and automatic security headers, and basic HTTP authentication using BCrypt. There is also a built-in HTTP to HTTPS redirect, a Prometheus metrics endpoint, and FIPS-capable build variants for environments that require them.

## Sources

- [License: Apache-2.0](https://github.com/static-web-server/static-web-server/blob/master/LICENSE)
- [Project website](https://static-web-server.net)
- [README](https://github.com/static-web-server/static-web-server/blob/master/README.md)
- [Releases](https://github.com/static-web-server/static-web-server/releases)
- [static-web-server/static-web-server on GitHub](https://github.com/static-web-server/static-web-server)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/static-web-server-static-web-server
