From bf0302d7a210ca7fdc760dc195c404efec577b45 Mon Sep 17 00:00:00 2001 From: Jose Quintana Date: Thu, 8 Jan 2026 19:24:17 +0100 Subject: [PATCH] docs: automatic documentation updates [skip ci] --- docs/download-and-install.template/index.html | 5 +- docs/download-and-install/index.html | 7 +- docs/search/search_index.json | 2 +- docs/sitemap.xml | 94 +++++++++--------- docs/sitemap.xml.gz | Bin 678 -> 678 bytes 5 files changed, 55 insertions(+), 53 deletions(-) diff --git a/docs/download-and-install.template/index.html b/docs/download-and-install.template/index.html index a26b4e43..348b0540 100644 --- a/docs/download-and-install.template/index.html +++ b/docs/download-and-install.template/index.html @@ -1,4 +1,5 @@ - Download and Install - Static Web Server
Skip to content

Download and Install

Latest {{RELEASE_VERSION}} release {{RELEASE_DATE}} (changelog, sha256sum)

See also the release history on GitHub.

Docker

If you are working with Docker containers then check out the Docker feature page.

Installation methods

Binary installer (Linux/BSDs)

Use the binary installer if your package manager is not supported.

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh
+ Download and Install - Static Web Server      

Download and Install

Latest {{RELEASE_VERSION}} release {{RELEASE_DATE}} (changelog, sha256sum)

See also the release history on GitHub.

Docker

If you are working with Docker containers then check out the Docker feature page.

Installation methods

Binary installer (Linux/BSDs)

Use the binary installer if your package manager is not supported.

With curl.

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh
+

Or with GNU wget (Busybox wget is not supported).

wget --https-only --secure-protocol=TLSv1_2 -qO- https://get.static-web-server.net | sh
 

static-web-server will be installed by default under the /usr/local/bin directory.

Alternatively, you can install a specific version of SWS to a custom location by setting environment variables.

export SWS_INSTALL_VERSION="{{RELEASE_VERSION_NUM}}" # full list at https://github.com/static-web-server/static-web-server/tags
 export SWS_INSTALL_DIR="~/.local/bin"
 curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh
@@ -21,4 +22,4 @@
 brew install static-web-server
 

Windows

Via Scoop

scoop install static-web-server
 

WebAssembly

Via Wasmer

wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787
-

Binaries

Pre-compiled binaries grouped by CPU architectures.

x86_64

ARM64

x86

ARM

PowerPC

S390X

Source files

\ No newline at end of file +

Binaries

Pre-compiled binaries grouped by CPU architectures.

x86_64

ARM64

x86

ARM

PowerPC

S390X

Source files

\ No newline at end of file diff --git a/docs/download-and-install/index.html b/docs/download-and-install/index.html index 14fcaf5b..db1c624a 100644 --- a/docs/download-and-install/index.html +++ b/docs/download-and-install/index.html @@ -1,5 +1,6 @@ - Download and Install - Static Web Server
Skip to content

Download and Install

Latest v2.40.1 release 2025-12-08 (changelog, sha256sum)

See also the release history on GitHub.

Docker

If you are working with Docker containers then check out the Docker feature page.

Installation methods

Binary installer (Linux/BSDs)

Use the binary installer if your package manager is not supported.

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh
-

static-web-server will be installed by default under the /usr/local/bin directory.

Alternatively, you can install a specific version of SWS to a custom location by setting environment variables.

export SWS_INSTALL_VERSION="2.40.1" # full list at https://github.com/static-web-server/static-web-server/tags
+ Download and Install - Static Web Server      

Download and Install

Latest v2.40.1 release 2025-12-08 (changelog, sha256sum)

See also the release history on GitHub.

Docker

If you are working with Docker containers then check out the Docker feature page.

Installation methods

Binary installer (Linux/BSDs)

Use the binary installer if your package manager is not supported.

With curl.

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh
+

Or with GNU wget (Busybox wget is not supported).

wget --https-only --secure-protocol=TLSv1_2 -qO- https://get.static-web-server.net | sh
+

The latest static-web-server version will be installed by default under the /usr/local/bin directory.

Alternatively, you can install a specific version of SWS to a custom location by setting environment variables.

export SWS_INSTALL_VERSION="2.40.1" # full list at https://github.com/static-web-server/static-web-server/tags
 export SWS_INSTALL_DIR="~/.local/bin"
 curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh
 

Make sure you set the environment variables for the piped process (sh in our case), not the piping process (curl).

If you don't want to export environment variables then use:

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | SWS_INSTALL_DIR="~/.local/bin" sh
@@ -21,4 +22,4 @@
 brew install static-web-server
 

Windows

Via Scoop

scoop install static-web-server
 

WebAssembly

Via Wasmer

wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787
-

Binaries

Pre-compiled binaries grouped by CPU architectures.

x86_64

ARM64

x86

ARM

PowerPC

S390X

Source files

\ No newline at end of file +

Binaries

Pre-compiled binaries grouped by CPU architectures.

x86_64

ARM64

x86

ARM

PowerPC

S390X

Source files

\ No newline at end of file diff --git a/docs/search/search_index.json b/docs/search/search_index.json index e4dd9d10..dac7eb06 100644 --- a/docs/search/search_index.json +++ b/docs/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Overview","text":"Static Web Server A cross-platform, high-performance & asynchronous web server for static files serving"},{"location":"#overview","title":"Overview","text":"

Static Web Server (or SWS abbreviated) is a tiny and fast production-ready web server suitable to serve static web files or assets.

It is focused on lightness and easy-to-use principles while keeping high performance and safety powered by The Rust Programming Language.

Written on top of Hyper and Tokio runtime, it provides concurrent and asynchronous networking abilities and the latest HTTP/1 - HTTP/2 implementations.

Cross-platform and available for Linux, macOS, Windows, FreeBSD, NetBSD, Android, Docker and Wasm (via Wasmer).

"},{"location":"#features","title":"Features","text":""},{"location":"#benchmarks","title":"Benchmarks","text":"

For more details see the benchmarks repository.

For feedback or questions feel free to reach us on the discussions page.

"},{"location":"#community","title":"Community","text":"

SWS Community on Discord

"},{"location":"building-from-source/","title":"Building from Source","text":"

Follow these instructions to either build SWS project from the source or the HTML documentation.

"},{"location":"building-from-source/#building-project-from-source","title":"Building project from source","text":"

If you want to build SWS from the source, all you need is a Rust 2021 Edition installed.

So make sure to install Rust 1.85.0 or newer (or nightly) along with the toolchain(s) of your preference.

Then clone the repository and use Cargo to build the project from the source.

git clone https://github.com/static-web-server/static-web-server.git\ncd static-web-server\ncargo build --release\n

Finally, the release binary should be available at target/release/static-web-server or under your toolchain directory chosen.

Don't use the project's Makefile

Please don't use the project's Makefile since it's only intended for development and some on-demand tasks.

"},{"location":"building-from-source/#cargo-features","title":"Cargo features","text":"

When building from the source, all features are enabled by default. However, you can disable just the ones you don't need from the lists below.

Feature Description Default default Activates the default features by omission. all Activates all available features including the experimental feature. This is the default feature used when building SWS binaries. experimental Activates all SWS experimental features. Make sure to also provide the required RUSTFLAGS if the feature requires so. HTTP2/TLS http2 Activates the HTTP2 and TLS feature. Compression compression Activates auto-compression and compression static with all supported algorithms. compression-brotli Activates auto-compression/compression static with only the brotli algorithm. compression-deflate Activates auto-compression/compression static with only the deflate algorithm. compression-gzip Activates auto-compression/compression static with only the gzip algorithm. compression-zstd Activates auto-compression/compression static with only the zstd algorithm. Directory Listing directory-listing Activates the directory listing feature. Basic Authorization basic-auth Activates the Basic HTTP Authorization Schema feature. Fallback Page fallback-page Activates the Fallback Page feature."},{"location":"building-from-source/#disable-all-default-features","title":"Disable all default features","text":"

For example, if you want to run or build SWS without the default features like compression, http2, etc then just try:

# run\ncargo run --no-default-features -- -h\n\n# build\ncargo build --release --no-default-features\n\n# or build including all features (example)\nRUSTFLAGS=\"--cfg tokio_unstable\" \\\n    cargo build -vv --release --features all \n
"},{"location":"building-from-source/#cross-compiling","title":"Cross-compiling","text":"

If you want to cross-compile SWS then consider using Zig as linker for easier cross compiling.

Let's say, you want to cross-compile SWS from macOS to Linux. Then follow these steps.

  1. Add the necessary toolchain, for example just type: rustup target add x86_64-unknown-linux-gnu or rustup target add x86_64-unknown-linux-musl if a statically-linked binary is wanted.
  2. Install the latest Zig version via brew install zig
  3. Install cargo-zigbuild via cargo install cargo-zigbuild
  4. Finally, build SWS as follows:
    # dynamically-linked binary\ncargo zigbuild --verbose --release --target=x86_64-unknown-linux-gnu\n# or statically-linked binary\ncargo zigbuild --verbose --release --target=x86_64-unknown-linux-musl\n

Built binaries can be found under the corresponding toolchain directory inside target/.

"},{"location":"building-from-source/#testing","title":"Testing","text":"
# run tests for default features\ncargo test\n\n# run all tests without default features\ncargo test --tests --no-default-features\n\n# or run tests for all features including experimental ones\nRUSTFLAGS=\"--cfg tokio_unstable\" cargo test --features all\n\n# or run specific tests\ncargo test --test rewrites\n
"},{"location":"building-from-source/#building-documentation-from-source","title":"Building documentation from source","text":"

All HTML documentation is located in the docs/ project's directory and is built using Material for MkDocs.

It's only necessary to have Docker installed.

"},{"location":"building-from-source/#building-documentation","title":"Building documentation","text":"

By default the docs will be built in the /tmp/docs directory, to do so follow these steps.

git clone https://github.com/static-web-server/static-web-server.git\ncd static-web-server\nmkdir /tmp/docs\ndocker run -it --rm \\\n    -v $PWD/docs:/docs \\\n    -v /tmp/docs:/tmp/docs squidfunk/mkdocs-material build\n

Output the docs in a different directory

If you want to output the docs in a different directory then append the --site-dir=/new/dir/path/ argument to the \"squidfunk/mkdocs-material\" build command and make sure to provide the new directory path.

"},{"location":"building-from-source/#development-server","title":"Development server","text":"

If you want to improve the documentation then run the built-in development server via docs/docker-compose.yml.

git clone https://github.com/static-web-server/static-web-server.git\ncd static-web-server\ndocker-compose -f docs/docker-compose.yml up\n

Now the server will be available at localhost:8000

"},{"location":"building-from-source/#formatting-markdown-files","title":"Formatting Markdown files","text":"

This project makes use of mdformat to format Markdown files. The CI job devel-project-docs checks that all Markdown files are formatted correctly.

To format documentation changes, you can run mdformat manually using uv:

uvx --python \">=3.13\" --with mdformat-mkdocs mdformat ./*.md docs/\n
"},{"location":"contributions/","title":"Contributions","text":"

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in current work by you, as defined in the Apache License (Version 2.0) license, shall be dual licensed as described below, without any additional terms or conditions.

Feel free to send some Pull request or file an issue.

For feedback or questions feel free to reach us on the discussions page or join us on Discord Server.

"},{"location":"download-and-install/","title":"Download and Install","text":""},{"location":"download-and-install/#download-and-install","title":"Download and Install","text":"

Latest v2.40.1 release 2025-12-08 (changelog, sha256sum)

Linux 64-bit macOS 64-bit Windows 64-bit FreeBSD 64-bit

See also the release history on GitHub.

Docker

If you are working with Docker containers then check out the Docker feature page.

"},{"location":"download-and-install/#installation-methods","title":"Installation methods","text":""},{"location":"download-and-install/#binary-installer-linuxbsds","title":"Binary installer (Linux/BSDs)","text":"

Use the binary installer if your package manager is not supported.

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh\n

static-web-server will be installed by default under the /usr/local/bin directory.

Alternatively, you can install a specific version of SWS to a custom location by setting environment variables.

export SWS_INSTALL_VERSION=\"2.40.1\" # full list at https://github.com/static-web-server/static-web-server/tags\nexport SWS_INSTALL_DIR=\"~/.local/bin\"\ncurl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh\n

Make sure you set the environment variables for the piped process (sh in our case), not the piping process (curl).

If you don't want to export environment variables then use:

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | SWS_INSTALL_DIR=\"~/.local/bin\" sh\n
"},{"location":"download-and-install/#arch-linux","title":"Arch Linux","text":"

Via Yay or your favorite AUR Helper.

yay -S static-web-server-bin\n
"},{"location":"download-and-install/#exherbo-linux","title":"Exherbo Linux","text":"

Add the rust repository and install the package through cave:

cave sync\ncave resolve -x repository/rust\ncave resolve -x static-web-server\n
"},{"location":"download-and-install/#nixos","title":"NixOS","text":"

Via Nix (Linux/MacOS)

nix-shell -p static-web-server\n# or\nnix-env -iA nixpkgs.static-web-server\n
"},{"location":"download-and-install/#macos","title":"MacOS","text":"

Using Homebrew Formulae (also Linux)

# Build from source\nbrew install static-web-server\n

Or using the SWS Homebrew Tap (also Linux)

brew tap static-web-server/static-web-server\n\n# Just the binary\nbrew install static-web-server-bin\n\n# Or build from source\nbrew install static-web-server\n
"},{"location":"download-and-install/#windows","title":"Windows","text":"

Via Scoop

scoop install static-web-server\n
"},{"location":"download-and-install/#webassembly","title":"WebAssembly","text":"

Via Wasmer

wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787\n
"},{"location":"download-and-install/#binaries","title":"Binaries","text":"

Pre-compiled binaries grouped by CPU architectures.

"},{"location":"download-and-install/#x86_64","title":"x86_64","text":""},{"location":"download-and-install/#arm64","title":"ARM64","text":""},{"location":"download-and-install/#x86","title":"x86","text":""},{"location":"download-and-install/#arm","title":"ARM","text":""},{"location":"download-and-install/#powerpc","title":"PowerPC","text":""},{"location":"download-and-install/#s390x","title":"S390X","text":""},{"location":"download-and-install/#source-files","title":"Source files","text":""},{"location":"download-and-install.template/","title":"Download and Install","text":"

Latest {{RELEASE_VERSION}} release {{RELEASE_DATE}} (changelog, sha256sum)

Linux 64-bit macOS 64-bit Windows 64-bit FreeBSD 64-bit

See also the release history on GitHub.

Docker

If you are working with Docker containers then check out the Docker feature page.

"},{"location":"download-and-install.template/#installation-methods","title":"Installation methods","text":""},{"location":"download-and-install.template/#binary-installer-linuxbsds","title":"Binary installer (Linux/BSDs)","text":"

Use the binary installer if your package manager is not supported.

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh\n

static-web-server will be installed by default under the /usr/local/bin directory.

Alternatively, you can install a specific version of SWS to a custom location by setting environment variables.

export SWS_INSTALL_VERSION=\"{{RELEASE_VERSION_NUM}}\" # full list at https://github.com/static-web-server/static-web-server/tags\nexport SWS_INSTALL_DIR=\"~/.local/bin\"\ncurl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh\n

Make sure you set the environment variables for the piped process (sh in our case), not the piping process (curl).

If you don't want to export environment variables then use:

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | SWS_INSTALL_DIR=\"~/.local/bin\" sh\n
"},{"location":"download-and-install.template/#arch-linux","title":"Arch Linux","text":"

Via Yay or your favorite AUR Helper.

yay -S static-web-server-bin\n
"},{"location":"download-and-install.template/#exherbo-linux","title":"Exherbo Linux","text":"

Add the rust repository and install the package through cave:

cave sync\ncave resolve -x repository/rust\ncave resolve -x static-web-server\n
"},{"location":"download-and-install.template/#nixos","title":"NixOS","text":"

Via Nix (Linux/MacOS)

nix-shell -p static-web-server\n# or\nnix-env -iA nixpkgs.static-web-server\n
"},{"location":"download-and-install.template/#macos","title":"MacOS","text":"

Using Homebrew Formulae (also Linux)

# Build from source\nbrew install static-web-server\n

Or using the SWS Homebrew Tap (also Linux)

brew tap static-web-server/static-web-server\n\n# Just the binary\nbrew install static-web-server-bin\n\n# Or build from source\nbrew install static-web-server\n
"},{"location":"download-and-install.template/#windows","title":"Windows","text":"

Via Scoop

scoop install static-web-server\n
"},{"location":"download-and-install.template/#webassembly","title":"WebAssembly","text":"

Via Wasmer

wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787\n
"},{"location":"download-and-install.template/#binaries","title":"Binaries","text":"

Pre-compiled binaries grouped by CPU architectures.

"},{"location":"download-and-install.template/#x86_64","title":"x86_64","text":""},{"location":"download-and-install.template/#arm64","title":"ARM64","text":""},{"location":"download-and-install.template/#x86","title":"x86","text":""},{"location":"download-and-install.template/#arm","title":"ARM","text":""},{"location":"download-and-install.template/#powerpc","title":"PowerPC","text":""},{"location":"download-and-install.template/#s390x","title":"S390X","text":""},{"location":"download-and-install.template/#source-files","title":"Source files","text":""},{"location":"getting-started/","title":"Getting Started","text":"

Download and install the binary for your specific platform and then type

static-web-server --port 8787 --root ./my-public-dir\n

Or if you use Docker just try

docker run --rm -it -p 8787:80 joseluisq/static-web-server:2\n

Docker Tip

You can specify a Docker volume like -v $HOME/my-public-dir:/var/public to overwrite the default root directory. See Docker examples.

"},{"location":"license/","title":"License","text":"

This work is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0).

\u00a9 2019-present Jose Quintana

For feedback or questions feel free to reach us on the discussions page.

"},{"location":"migration/","title":"Migrating from v1 to v2","text":"

The v2 introduces notable changes including new features, performance improvements and new target support like ARM64 and OSes like FreeBSD, NetBSD, Illumos or Android.

This version v2 was re-written almost from scratch on top of Hyper and Tokio runtime which gives us the Rust asynchronous ability by default and the latest HTTP/1 - HTTP/2 implementation improvements. However, it still tries to keep the same principles of its v1: lightness and easy-to-use. Therefore migration should not be a big deal.

"},{"location":"migration/#v2-breaking-changes","title":"v2 breaking changes","text":"

This major v2 has a few breaking changes. However, migration should not represent a problem.

Tip

It is always worth recommending that you test a major server version upgrade like this first with your application(s) in a development environment or similar.

Please keep in mind the following changes in v2:

The rest of the known options are equivalent to v1 (except the new ones of course).

For feedback or questions feel free to reach us on the discussions page.

"},{"location":"platforms-architectures/","title":"Platforms & Architectures","text":"

Currently, only the following platforms/architectures are supported.

Docker tip

For the list of Docker images supported see Docker OS/Arch page.

"},{"location":"platforms-architectures/#linux","title":"Linux","text":""},{"location":"platforms-architectures/#x86","title":"x86","text":""},{"location":"platforms-architectures/#x86_64","title":"x86_64","text":""},{"location":"platforms-architectures/#arm","title":"ARM","text":""},{"location":"platforms-architectures/#arm64","title":"ARM64","text":""},{"location":"platforms-architectures/#macos","title":"macOS","text":""},{"location":"platforms-architectures/#x86_65","title":"x86_64","text":""},{"location":"platforms-architectures/#arm64_1","title":"ARM64","text":""},{"location":"platforms-architectures/#windows","title":"Windows","text":""},{"location":"platforms-architectures/#x86_1","title":"x86","text":""},{"location":"platforms-architectures/#x86_66","title":"x86_64","text":""},{"location":"platforms-architectures/#arm64_2","title":"ARM64","text":""},{"location":"platforms-architectures/#freebsd","title":"FreeBSD","text":""},{"location":"platforms-architectures/#x86_2","title":"x86","text":""},{"location":"platforms-architectures/#x86_67","title":"x86_64","text":""},{"location":"platforms-architectures/#netbsd","title":"NetBSD","text":""},{"location":"platforms-architectures/#x86_68","title":"x86_64","text":""},{"location":"platforms-architectures/#illumos","title":"Illumos","text":""},{"location":"platforms-architectures/#x86_69","title":"x86_64","text":""},{"location":"platforms-architectures/#powerpc","title":"PowerPC","text":""},{"location":"platforms-architectures/#s390x","title":"S390X","text":""},{"location":"report-security-issues/","title":"Report Security Issues","text":"

To report any security issues, please follow our Security Policy.

"},{"location":"semantic-versioning/","title":"Semantic Versioning","text":"

SWS project adheres to Semantic Versioning (SemVer) for every release like the latest v2.

The project privileges stability, security and performance, so you can rely on a major version like v2 and expect to get features (minor) and bug/security fixes (patch) without breaking changes.

"},{"location":"semantic-versioning/#breaking-changes-for-major-versions","title":"Breaking changes for major versions","text":"

Only switches between major versions \"could\" contain breaking changes or not depending on the particular case. However, this project tries to keep away from any kind of breaking change possible between major versions. But it still supports and prioritizes \"no breaking changes\" for minor and patch (bug fixes) versions.

On the other hand, if a breaking change is found to be \"strictly necessary\" to do. Then a new major version must be promoted as well and users informed accordingly so they can do a seamless transition.

"},{"location":"showcases/","title":"Showcases","text":"

The following is a collection of articles, blog posts, and projects that showcase the use of Static Web Server (SWS). These resources provide insights, tutorials and real-world applications using SWS in various contexts.

"},{"location":"showcases/#projects","title":"Projects","text":"

Projects that utilize Static Web Server as part of their infrastructure or development workflow.

"},{"location":"showcases/#articles","title":"Articles","text":"

Posts and articles discussing the implementation and benefits of using Static Web Server.

"},{"location":"configuration/command-line-arguments/","title":"Command-Line Arguments","text":"

The server can be configured via the following command-line arguments.

Remember

$ static-web-server -h\nA cross-platform, high-performance and asynchronous web server for static files-serving.\n\nUsage: static-web-server [OPTIONS] [COMMAND]\n\nCommands:\n  generate  Generate man pages and shell completions\n  help      Print this message or the help of the given subcommand(s)\n\nOptions:\n  -a, --host <HOST>\n          Host address (E.g 127.0.0.1 or ::1) [env: SERVER_HOST=] [default: ::]\n  -p, --port <PORT>\n          Host port [env: SERVER_PORT=] [default: 80]\n  -f, --fd <FD>\n          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=]\n  -n, --threads-multiplier <THREADS_MULTIPLIER>\n          Number of worker threads multiplier that'll be multiplied by the number of system CPUs using the formula: `worker threads = number of CPUs * n` where `n` is the value that changes here. When multiplier value is 0 or 1 then one thread per core is used. Number of worker threads result should be a number between 1 and 32,768 though it is advised to keep this value on the smaller side [env: SERVER_THREADS_MULTIPLIER=] [default: 1]\n  -b, --max-blocking-threads <MAX_BLOCKING_THREADS>\n          Maximum number of blocking threads [env: SERVER_MAX_BLOCKING_THREADS=] [default: 512]\n  -d, --root <ROOT>\n          Root directory path of static files [env: SERVER_ROOT=] [default: ./public]\n      --page50x <PAGE50X>\n          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]\n      --page404 <PAGE404>\n          HTML file path for 404 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_404=] [default: ./404.html]\n      --page-fallback <PAGE_FALLBACK>\n          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 [env: SERVER_FALLBACK_PAGE=] [default: ]\n  -g, --log-level <LOG_LEVEL>\n          Specify a logging level in lower case. Values: error, warn, info, debug or trace [env: SERVER_LOG_LEVEL=] [default: error]\n      --log-with-ansi [<LOG_WITH_ANSI>]\n          Enable or disable ANSI escape codes for colors and other text formatting of the log output [env: SERVER_LOG_WITH_ANSI=] [default: false] [possible values: true, false]\n  -c, --cors-allow-origins <CORS_ALLOW_ORIGINS>\n          Specify an optional CORS list of allowed origin hosts separated by commas. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host [env: SERVER_CORS_ALLOW_ORIGINS=] [default: ]\n  -j, --cors-allow-headers <CORS_ALLOW_HEADERS>\n          Specify an optional CORS list of allowed headers separated by commas. Default \"origin, content-type\". It requires `--cors-allow-origins` to be used along with [env: SERVER_CORS_ALLOW_HEADERS=] [default: \"origin, content-type, authorization\"]\n      --cors-expose-headers <CORS_EXPOSE_HEADERS>\n          Specify an optional CORS list of exposed headers separated by commas. Default \"origin, content-type\". It requires `--cors-expose-origins` to be used along with [env: SERVER_CORS_EXPOSE_HEADERS=] [default: \"origin, content-type\"]\n  -t, --http2 [<HTTP2>]\n          Enable HTTP/2 with TLS support [env: SERVER_HTTP2_TLS=] [default: false] [possible values: true, false]\n      --http2-tls-cert <HTTP2_TLS_CERT>\n          Specify the file path to read the certificate [env: SERVER_HTTP2_TLS_CERT=]\n      --http2-tls-key <HTTP2_TLS_KEY>\n          Specify the file path to read the private key [env: SERVER_HTTP2_TLS_KEY=]\n      --https-redirect [<HTTPS_REDIRECT>]\n          Redirect all requests with scheme \"http\" to \"https\" for the current server instance. It depends on \"http2\" to be enabled [env: SERVER_HTTPS_REDIRECT=] [default: false] [possible values: true, false]\n      --https-redirect-host <HTTPS_REDIRECT_HOST>\n          Canonical host name or IP of the HTTPS (HTTPS/2) server. It depends on \"https_redirect\" to be enabled [env: SERVER_HTTPS_REDIRECT_HOST=] [default: localhost]\n      --https-redirect-from-port <HTTPS_REDIRECT_FROM_PORT>\n          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]\n      --https-redirect-from-hosts <HTTPS_REDIRECT_FROM_HOSTS>\n          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]\n      --index-files <INDEX_FILES>\n          List of files that will be used as an index for requests ending with the slash character (\u2018/\u2019). Files are checked in the specified order [env: SERVER_INDEX_FILES=] [default: index.html]\n  -x, --compression [<COMPRESSION>]\n          Gzip, Deflate, Brotli or Zstd compression on demand determined by the Accept-Encoding header and applied to text-based web file types only [env: SERVER_COMPRESSION=] [default: true] [possible values: true, false]\n      --compression-level <COMPRESSION_LEVEL>\n          Compression level to apply for Gzip, Deflate, Brotli or Zstd compression [env: SERVER_COMPRESSION_LEVEL=] [default: default] [possible values: fastest, best, default]\n      --compression-static [<COMPRESSION_STATIC>]\n          Look up the pre-compressed file variant (`.gz`, `.br` or `.zst`) on disk of a requested file and serves it directly if available. The compression type is determined by the `Accept-Encoding` header [env: SERVER_COMPRESSION_STATIC=] [default: false] [possible values: true, false]\n  -z, --directory-listing [<DIRECTORY_LISTING>]\n          Enable directory listing for all requests ending with the slash character (\u2018/\u2019) [env: SERVER_DIRECTORY_LISTING=] [default: false] [possible values: true, false]\n      --directory-listing-order <DIRECTORY_LISTING_ORDER>\n          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]\n      --directory-listing-format <DIRECTORY_LISTING_FORMAT>\n          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]\n      --directory-listing-download=<DIRECTORY_LISTING_DOWNLOAD>\n          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]\n      --security-headers [<SECURITY_HEADERS>]\n          Enable security headers by default when HTTP/2 feature is activated. Headers included: \"Strict-Transport-Security: max-age=63072000; includeSubDomains; preload\" (2 years max-age), \"X-Frame-Options: DENY\" and \"Content-Security-Policy: frame-ancestors 'self'\" [env: SERVER_SECURITY_HEADERS=] [default: false] [possible values: true, false]\n  -e, --cache-control-headers [<CACHE_CONTROL_HEADERS>]\n          Enable cache control headers for incoming requests based on a set of file types. The file type list can be found on `src/control_headers.rs` file [env: SERVER_CACHE_CONTROL_HEADERS=] [default: true] [possible values: true, false]\n      --basic-auth <BASIC_AUTH>\n          It provides The \"Basic\" HTTP Authentication scheme using credentials as \"user-id:password\" pairs. Password must be encoded using the \"BCrypt\" password-hashing function [env: SERVER_BASIC_AUTH=] [default: ]\n  -q, --grace-period <GRACE_PERIOD>\n          Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds [env: SERVER_GRACE_PERIOD=] [default: 0]\n  -w, --config-file <CONFIG_FILE>\n          Server TOML configuration file path [env: SERVER_CONFIG_FILE=] [default: ./sws.toml]\n      --log-remote-address [<LOG_REMOTE_ADDRESS>]\n          Log incoming requests information along with its remote address if available using the `info` log level [env: SERVER_LOG_REMOTE_ADDRESS=] [default: false] [possible values: true, false]\n      --log-x-real-ip [<LOG_X_REAL_IP>]\n          Log the X-Real-IP header for remote IP information [env: SERVER_LOG_X_REAL_IP=] [default: false] [possible values: true, false]\n      --log-forwarded-for [<LOG_FORWARDED_FOR>]\n          Log the X-Forwarded-For header for remote IP information [env: SERVER_LOG_FORWARDED_FOR=] [default: false] [possible values: true, false]\n      --trusted-proxies <TRUSTED_PROXIES>\n          List of IPs to use X-Forwarded-For from. The default is to trust all [env: SERVER_TRUSTED_PROXIES=]\n      --redirect-trailing-slash [<REDIRECT_TRAILING_SLASH>]\n          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]\n      --ignore-hidden-files [<IGNORE_HIDDEN_FILES>]\n          Ignore hidden files/directories (dotfiles), preventing them to be served and being included in auto HTML index pages (directory listing) [env: SERVER_IGNORE_HIDDEN_FILES=] [default: false] [possible values: true, false]\n      --disable-symlinks [<DISABLE_SYMLINKS>]\n          Prevent following files or directories if any path name component is a symbolic link [env: SERVER_DISABLE_SYMLINKS=] [default: false] [possible values: true, false]\n      --health [<HEALTH>]\n          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]\n      --accept-markdown [<ACCEPT_MARKDOWN>]\n          Enable markdown content negotiation. When a client sends Accept: text/markdown header, the server will serve markdown files (.md or .html.md) if available [env: SERVER_ACCEPT_MARKDOWN=] [default: false] [possible values: true, false]\n      --maintenance-mode [<MAINTENANCE_MODE>]\n          Enable the server's maintenance mode functionality [env: SERVER_MAINTENANCE_MODE=] [default: false] [possible values: true, false]\n      --maintenance-mode-status <MAINTENANCE_MODE_STATUS>\n          Provide a custom HTTP status code when entering into maintenance mode. Default 503 [env: SERVER_MAINTENANCE_MODE_STATUS=] [default: 503]\n      --maintenance-mode-file <MAINTENANCE_MODE_FILE>\n          Provide a custom maintenance mode HTML file. If not provided then a generic message will be displayed [env: SERVER_MAINTENANCE_MODE_FILE=] [default: ]\n  -V, --version\n          Print version info and exit\n  -h, --help\n          Print help (see more with '--help')\n
"},{"location":"configuration/command-line-arguments/#windows","title":"Windows","text":"

The following options and commands are Windows platform-specific.

 -s, --windows-service <windows-service>\n            Run the web server as a Windows Service [env: SERVER_WINDOWS_SERVICE=]  [default: false]\n\nSUBCOMMANDS:\n    help         Prints this message or the help of the given subcommand(s)\n    install      Install a Windows Service for the web server\n    uninstall    Uninstall the current Windows Service\n
"},{"location":"configuration/config-file/","title":"TOML Configuration File","text":"

SWS can be configured using a TOML 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 env.

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. If it is not found or can not be loaded then SWS will continue using the server defaults.

"},{"location":"configuration/config-file/#toml-file-manifest","title":"TOML File (Manifest)","text":"

Below is just an example showing all features with their default values.

[general]\n\n#### Address & Root dir\nhost = \"::\"\nport = 80\nroot = \"./public\"\n\n#### Logging\nlog-level = \"error\"\n\n#### Cache Control headers\ncache-control-headers = true\n\n#### Auto Compression\ncompression = true\ncompression-level = \"default\"\n\n#### Error pages\n# Note: If a relative path is used then it will be resolved under the root directory.\npage404 = \"./404.html\"\npage50x = \"./50x.html\"\n\n#### HTTP/2 + TLS\nhttp2 = false\nhttp2-tls-cert = \"\"\nhttp2-tls-key = \"\"\nhttps-redirect = false\nhttps-redirect-host = \"localhost\"\nhttps-redirect-from-port = 80\nhttps-redirect-from-hosts = \"localhost\"\n\n#### CORS & Security headers\n# security-headers = true\n# cors-allow-origins = \"\"\n\n#### Directory listing\ndirectory-listing = false\n\n#### Directory listing sorting code\ndirectory-listing-order = 1\n\n#### Directory listing content format\ndirectory-listing-format = \"html\"\n\n#### Directory listing download format\ndirectory-listing-download = []\n\n#### Basic Authentication\n# basic-auth = \"\"\n\n#### File descriptor binding\n# fd = \"\"\n\n#### Worker threads\nthreads-multiplier = 1\n\n#### Grace period after a graceful shutdown\ngrace-period = 0\n\n#### Page fallback for 404s\n# page-fallback = \"\"\n\n#### Log request Remote Address if available\nlog-remote-address = false\n\n#### Log real IP from X-Forwarded-For header if available\nlog-forwarded-for = false\n\n#### IPs to accept the X-Forwarded-For header from. Empty means all\ntrusted-proxies = []\n\n#### Redirect to trailing slash in the requested directory uri\nredirect-trailing-slash = true\n\n#### Check for existing pre-compressed files\ncompression-static = true\n\n#### Health-check endpoint (GET or HEAD `/health`)\nhealth = false\n\n#### Markdown content negotiation\naccept-markdown = false\n\n#### List of index files\n# index-files = \"index.html, index.htm\"\n#### Maintenance Mode\n\nmaintenance-mode = false\n# maintenance-mode-status = 503\n# maintenance-mode-file = \"./maintenance.html\"\n\n### Windows Only\n\n#### Run the web server as a Windows Service\n# windows-service = false\n\n\n[advanced]\n\n#### HTTP Headers customization (examples only)\n\n#### a. Oneline version\n# [[advanced.headers]]\n# source = \"**/*.{js,css}\"\n# headers = { Access-Control-Allow-Origin = \"*\" }\n\n#### b. Multiline version\n# [[advanced.headers]]\n# source = \"/index.html\"\n# [advanced.headers.headers]\n# Cache-Control = \"public, max-age=36000\"\n# Content-Security-Policy = \"frame-ancestors 'self'\"\n# Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n\n#### c. Multiline version with explicit key (dotted)\n# [[advanced.headers]]\n# source = \"**/*.{jpg,jpeg,png,ico,gif}\"\n# headers.Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n\n\n### URL Redirects (examples only)\n\n# [[advanced.redirects]]\n# source = \"**/*.{jpg,jpeg}\"\n# destination = \"/images/generic1.png\"\n# kind = 301\n\n# [[advanced.redirects]]\n# source = \"/index.html\"\n# destination = \"https://static-web-server.net\"\n# kind = 302\n\n### URL Rewrites (examples only)\n\n# [[advanced.rewrites]]\n# source = \"**/*.{png,ico,gif}\"\n# destination = \"/assets/favicon.ico\"\n## Optional redirection\n# redirect = 301\n\n# [[advanced.rewrites]]\n# source = \"**/*.{jpg,jpeg}\"\n# destination = \"/images/sws.png\"\n\n### Virtual Hosting\n\n# [[advanced.virtual-hosts]]\n## But if the \"Host\" header matches this...\n# host = \"sales.example.com\"\n## ...then files will be served from here instead\n# root = \"/var/sales/html\"\n\n# [[advanced.virtual-hosts]]\n# host = \"blog.example.com\"\n# root = \"/var/blog/html\"\n
"},{"location":"configuration/config-file/#general-options","title":"General options","text":"

The TOML [general] section allows adjusting the current options available via the CLI/ENV ones.

So they are equivalent to each other except for the -w, --config-file option which is omitted and can not be used for obvious reasons.

Config file-based features are optional

All server feature options via the configuration file are optional and can be omitted as needed.

"},{"location":"configuration/config-file/#advanced-options","title":"Advanced options","text":"

The TOML [advanced] section is intended for more complex features.

For example Custom HTTP Headers, Custom URL Redirects, URL Rewrites, or Virtual Hosting

"},{"location":"configuration/config-file/#precedence","title":"Precedence","text":"

Whatever config file-based feature option will take precedence over its CLI or ENV equivalent.

"},{"location":"configuration/config-file/#usage","title":"Usage","text":"

The following command runs the server using a specific sws.toml file.

static-web-server -w sws.toml\n
"},{"location":"configuration/environment-variables/","title":"Environment Variables","text":"

The server can be configured via the following environment variables.

Remember

"},{"location":"configuration/environment-variables/#server_host","title":"SERVER_HOST","text":"

The address of the host (e.g. 127.0.0.1). Default [::].

"},{"location":"configuration/environment-variables/#server_port","title":"SERVER_PORT","text":"

The port of the host. Default 80.

"},{"location":"configuration/environment-variables/#server_listen_fd","title":"SERVER_LISTEN_FD","text":"

Optional file descriptor number (e.g. 0) to inherit an already-opened TCP listener (instead of using SERVER_HOST and/or SERVER_PORT). Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_root","title":"SERVER_ROOT","text":"

Relative or absolute root directory path of static files. Default ./public.

"},{"location":"configuration/environment-variables/#server_config_file","title":"SERVER_CONFIG_FILE","text":"

The Server configuration file path is in TOML format. See The TOML Configuration File.

"},{"location":"configuration/environment-variables/#server_grace_period","title":"SERVER_GRACE_PERIOD","text":"

Defines a grace period in seconds after a SIGTERM signal is caught which will delay the server before shutting it down gracefully. The maximum value is 255 seconds. The default value is 0 (no delay).

"},{"location":"configuration/environment-variables/#server_log_level","title":"SERVER_LOG_LEVEL","text":"

Specify a logging level in lowercase. Possible values are error, warn, info, debug or trace. Default error.

"},{"location":"configuration/environment-variables/#server_log_with_ansi","title":"SERVER_LOG_WITH_ANSI","text":"

Enable or disable ANSI escape codes for colors and other text formatting of the log output.

"},{"location":"configuration/environment-variables/#server_log_remote_address","title":"SERVER_LOG_REMOTE_ADDRESS","text":"

Log incoming request information along with its Remote Address (IP) if available using the info log level. Default false.

"},{"location":"configuration/environment-variables/#server_log_x_real_ip","title":"SERVER_LOG_X_REAL_IP","text":"

Log the X-Real-IP header if available using the info log level. Default false.

"},{"location":"configuration/environment-variables/#server_log_forwarded_for","title":"SERVER_LOG_FORWARDED_FOR","text":"

Log the X-Forwarded-For header if available using the info log level. Default false.

"},{"location":"configuration/environment-variables/#server_trusted_proxies","title":"SERVER_TRUSTED_PROXIES","text":"

A comma separated list of IP addresses to accept the X-Forwarded-For header from. An empty string means trust all IPs. Default \"\"

"},{"location":"configuration/environment-variables/#server_error_page_404","title":"SERVER_ERROR_PAGE_404","text":"

HTML file path for 404 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. Default ./404.html.

"},{"location":"configuration/environment-variables/#server_error_page_50x","title":"SERVER_ERROR_PAGE_50X","text":"

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. Default ./50x.html

"},{"location":"configuration/environment-variables/#server_fallback_page","title":"SERVER_FALLBACK_PAGE","text":"

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.

"},{"location":"configuration/environment-variables/#server_threads_multiplier","title":"SERVER_THREADS_MULTIPLIER","text":"

The number of worker threads multiplier will be multiplied by the number of system CPUs using the formula: worker threads = number of CPUs * n where n is the value that changes here. When the multiplier value is 0 or 1 then the number of CPUs is used. The number of worker threads result should be a number between 1 and 32,768 though it is advised to keep this value on the smaller side. Default one thread per core.

"},{"location":"configuration/environment-variables/#server_max_blocking_threads","title":"SERVER_MAX_BLOCKING_THREADS","text":"

Maximum number of blocking threads.

"},{"location":"configuration/environment-variables/#server_http2_tls","title":"SERVER_HTTP2_TLS","text":"

Enable HTTP/2 with TLS support. Make sure also to adjust the current server port. Default false (disabled).

"},{"location":"configuration/environment-variables/#server_http2_tls_cert","title":"SERVER_HTTP2_TLS_CERT","text":"

Specify the file path to read the certificate. Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_http2_tls_key","title":"SERVER_HTTP2_TLS_KEY","text":"

Specify the file path to read the private key. Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_https_redirect","title":"SERVER_HTTPS_REDIRECT","text":"

Redirect all requests with scheme \"http\" to \"https\" for the current server instance. It depends on \"http2\" to be enabled.

"},{"location":"configuration/environment-variables/#server_https_redirect_host","title":"SERVER_HTTPS_REDIRECT_HOST","text":"

Canonical hostname or IP of the HTTPS (HTTPS/2) server. It depends on \"https-redirect\" to be enabled. Default localhost.

"},{"location":"configuration/environment-variables/#server_https_redirect_from_port","title":"SERVER_HTTPS_REDIRECT_FROM_PORT","text":"

HTTP host port where the redirect server will listen for requests to redirect them to HTTPS. It depends on \"https-redirect\" to be enabled. Default 80.

"},{"location":"configuration/environment-variables/#server_https_redirect_from_hosts","title":"SERVER_HTTPS_REDIRECT_FROM_HOSTS","text":"

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. Default localhost.

"},{"location":"configuration/environment-variables/#server_cors_allow_origins","title":"SERVER_CORS_ALLOW_ORIGINS","text":"

Specify an optional CORS list of allowed origin hosts separated by commas. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host. Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_cors_allow_headers","title":"SERVER_CORS_ALLOW_HEADERS","text":"

Specify an optional CORS list of allowed HTTP headers separated by commas. It requires SERVER_CORS_ALLOW_ORIGINS to be used along with. Default origin, content-type.

"},{"location":"configuration/environment-variables/#server_cors_expose_headers","title":"SERVER_CORS_EXPOSE_HEADERS","text":"

Specify an optional CORS list of exposed HTTP headers separated by commas. It requires SERVER_CORS_ALLOW_ORIGINS to be used along with. Default origin, content-type.

"},{"location":"configuration/environment-variables/#server_compression","title":"SERVER_COMPRESSION","text":"

Gzip, Deflate, Brotli or zlib compression on demand determined by the Accept-Encoding header and applied to text-based web file types only. See ad-hoc mime-type list. Default true (enabled).

"},{"location":"configuration/environment-variables/#server_compression_level","title":"SERVER_COMPRESSION_LEVEL","text":"

Supported values are fastest (fast compression but larger resulting files), best (smallest file size but potentially slow) and default (algorithm-specific balanced compression level). Default is default.

"},{"location":"configuration/environment-variables/#server_compression_static","title":"SERVER_COMPRESSION_STATIC","text":"

Look up the pre-compressed file variant (.gz, .br or .zst) on the disk of a requested file and serve it directly if available. Default false (disabled). The compression type is determined by the Accept-Encoding header.

"},{"location":"configuration/environment-variables/#server_directory_listing","title":"SERVER_DIRECTORY_LISTING","text":"

Enable directory listing for all requests ending with the slash character (\u2018/\u2019). Default false (disabled).

"},{"location":"configuration/environment-variables/#server_directory_listing_order","title":"SERVER_DIRECTORY_LISTING_ORDER","text":"

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).

"},{"location":"configuration/environment-variables/#server_directory_listing_format","title":"SERVER_DIRECTORY_LISTING_FORMAT","text":"

Specify a content format for the directory listing entries. Formats supported: html or json. Default html.

"},{"location":"configuration/environment-variables/#server_directory_listing_download","title":"SERVER_DIRECTORY_LISTING_DOWNLOAD","text":"

Specify list of enabled format(s) for directory download. Format supported: targz. Default to empty list (disabled).

"},{"location":"configuration/environment-variables/#server_security_headers","title":"SERVER_SECURITY_HEADERS","text":"

Enable security headers by default when the HTTP/2 feature is activated. Headers included: Strict-Transport-Security: max-age=63072000; includeSubDomains; preload (2 years max-age), X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'self'. Default false (disabled).

"},{"location":"configuration/environment-variables/#server_cache_control_headers","title":"SERVER_CACHE_CONTROL_HEADERS","text":"

Enable cache control headers for incoming requests based on a set of file types. The file type list can be found in src/control_headers.rs file. Default true (enabled).

"},{"location":"configuration/environment-variables/#server_basic_auth","title":"SERVER_BASIC_AUTH","text":"

It provides The \"Basic\" HTTP Authentication Scheme using credentials as user-id:password pairs, encoded using Base64. Password must be encoded using the BCrypt password-hashing function. Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_redirect_trailing_slash","title":"SERVER_REDIRECT_TRAILING_SLASH","text":"

Check for a trailing slash in the requested directory URI and redirect permanent (308) to the same path with a trailing slash suffix if it is missing. Default true (enabled).

"},{"location":"configuration/environment-variables/#server_ignore_hidden_files","title":"SERVER_IGNORE_HIDDEN_FILES","text":"

Ignore hidden files/directories (dotfiles), preventing them from being served and being included in auto HTML index pages (directory listing).

"},{"location":"configuration/environment-variables/#server_disable_symlinks","title":"SERVER_DISABLE_SYMLINKS","text":"

Prevent following files or directories if any path name component is a symbolic link.

"},{"location":"configuration/environment-variables/#server_health","title":"SERVER_HEALTH","text":"

Activate the health endpoint.

"},{"location":"configuration/environment-variables/#server_accept_markdown","title":"SERVER_ACCEPT_MARKDOWN","text":"

Enable markdown content negotiation. When a client sends Accept: text/markdown header, the server will serve markdown files (.md or .html.md) if available. See Markdown Content Negotiation for details. Default false.

"},{"location":"configuration/environment-variables/#server_index_files","title":"SERVER_INDEX_FILES","text":"

List of files that will be used as an index for requests ending with the slash character (\u2018/\u2019). Files are checked in the specified order. Default index.html.

"},{"location":"configuration/environment-variables/#server_maintenance_mode","title":"SERVER_MAINTENANCE_MODE","text":"

Enable the server's maintenance mode functionality.

"},{"location":"configuration/environment-variables/#server_maintenance_mode_status","title":"SERVER_MAINTENANCE_MODE_STATUS","text":"

Provide a custom HTTP status code when entering into maintenance mode. Default 503.

"},{"location":"configuration/environment-variables/#server_maintenance_mode_file","title":"SERVER_MAINTENANCE_MODE_FILE","text":"

Provide a custom maintenance mode HTML file. If not provided then a generic message will be displayed.

"},{"location":"configuration/environment-variables/#windows","title":"Windows","text":"

The following options and commands are Windows platform-specific.

"},{"location":"configuration/environment-variables/#server_windows_service","title":"SERVER_WINDOWS_SERVICE","text":"

Run the web server in a Windows Service context. See more details.

"},{"location":"features/basic-authentication/","title":"Basic HTTP Authentication","text":"

SWS provides 'Basic' HTTP Authentication Scheme 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 env.

The format to use is the following:

username:encrypted_password

Both are separated by a : (punctuation mark) character.

Password Encryption

Only the password must be encoded using the BCrypt password-hashing function.

As an example, we will use the Apache htpasswd tool to generate the username:encrypted_password pair.

htpasswd -nBC10 \"username\"\n# New password: \n# Re-type new password: \n# username:$2y$10$8phm28BB4YpKPDjOpdTT8eUcfVDw0xc85VZPxg2zae1GR8EQqus3i\n

Password Security Advice

The password verification happens at runtime but its verification speed depends on the computing time cost of bcrypt algorithm used.

For example, the htpasswd tool supports a -C argument to adjust the bcrypt's computing time.

Using a higher value is more secure but slower. The default value is 5 and the possible values are ranging from 4 to 17.

Docker Compose Advice

If you are using SERVER_BASIC_AUTH env via a docker-compose.yml file don't forget to replace the single $ (dollar sign) with a $$ (double-dollar sign) if you want those individual $ dollar signs in your configuration to be treated by Docker as literals. More details in the Docker Compose file: variable substitution page.

Finally, assign the credentials and run the server.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --basic-auth 'username:$2y$10$8phm28BB4YpKPDjOpdTT8eUcfVDw0xc85VZPxg2zae1GR8EQqus3i'\n
"},{"location":"features/blocking-threads/","title":"Blocking Threads Customization","text":"

SWS allows limiting the number of blocking threads powered by the Tokio runtime.

This feature can be controlled by the numeric -b, --max-blocking-threads option or the equivalent SERVER_MAX_BLOCKING_THREADS env.

WebAssembly

We use 20 in Wasm by default and 512 in native environments (Tokio's default). See Tokio max_blocking_threads API for more details.

Below is an example.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --max-blocking-threads 20\n
"},{"location":"features/cache-control-headers/","title":"Cache-Control Headers","text":"

SWS provides support for arbitrary 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 env.

Customize HTTP headers

If you want to customize HTTP headers on demand then have a look at the Custom HTTP Headers section.

"},{"location":"features/cache-control-headers/#cache-control-max-age","title":"Cache-Control Max-Age","text":"

Control headers are applied only to the following file types with the corresponding max-age values.

"},{"location":"features/cache-control-headers/#one-day","title":"One day","text":"

A max-age of one day duration is used by default.

Note

One-day max-age for example includes html and other file types.

"},{"location":"features/cache-control-headers/#one-hour","title":"One hour","text":"

A max-age of one hour is applied only to the following file types.

atom, json, rss, xml\n
"},{"location":"features/cache-control-headers/#one-year","title":"One year","text":"

A max-age of one year is applied only to the following file types.

avif, bmp, bz2, css, doc, gif, gz, htc, ico, jpeg, jpg, js, jxl, map, mjs, mp3, mp4, ogg, ogv, pdf, png, rar, rtf, tar, tgz, wav, weba, webm, webp, woff, woff2, zip\n

Below is an example of how to enable the feature.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --cache-control-headers true\n
"},{"location":"features/compression-static/","title":"Pre-compressed files serving","text":"

SWS provides support to serve pre-compressed Gzip, Brotli and Zstandard (zstd) files directly from the disk.

SWS can look up existing pre-compressed file variants (.gz, .br or .zst) on disk and serve them directly.

The feature is disabled by default and can be controlled by the boolean --compression-static option or the equivalent SERVER_COMPRESSION_STATIC env.

When the compression-static option is enabled and the pre-compressed file is found on the file system then it's served directly. Otherwise, if the pre-compressed file is not found then SWS just continues the normal workflow (trying to serve the original file requested instead). Additionally, if for example the compression option was also enabled then the requested file can be compressed on the fly right after.

Compressed file type

The pre-compressed file type is determined by the Accept-Encoding header value.

Here is an example:

static-web-server -p=8787 -d=/var/www --compression-static=true -g=trace\n

Below are some relevant log entries to show how the feature works.

2022-09-22T21:30:12.904102Z  INFO static_web_server::handler: incoming request: method=GET uri=/downloads/Capture5.png\n2022-09-22T21:30:12.904218Z TRACE static_web_server::static_files: dir: base=\"/var/www\", route=\"downloads/Capture5.png\"\n2022-09-22T21:30:12.904295Z TRACE static_web_server::compression_static: preparing pre-compressed file path variant of /var/www/downloads/Capture5.png\n2022-09-22T21:30:12.904509Z TRACE static_web_server::compression_static: getting metadata for pre-compressed file variant /var/www/downloads/Capture5.png.gz\n2022-09-22T21:30:12.904746Z TRACE hyper::proto::h1::conn: flushed({role=server}): State { reading: KeepAlive, writing: Init, keep_alive: Busy }\n2022-09-22T21:30:12.904932Z TRACE static_web_server::static_files: file found: \"/var/www/downloads/Capture5.png.gz\"\n2022-09-22T21:30:12.904983Z TRACE static_web_server::compression_static: pre-compressed file variant found, serving it directly\n2022-09-22T21:30:12.905095Z TRACE hyper::proto::h1::conn: flushed({role=server}): State { reading: KeepAlive, writing: Init, keep_alive: Busy }\n2022-09-22T21:30:12.905836Z TRACE encode_headers: hyper::proto::h1::role: Server::encode status=200, body=Some(Unknown), req_method=Some(GET)\n2022-09-22T21:30:12.905965Z TRACE encode_headers: hyper::proto::h1::role: close time.busy=138\u00b5s time.idle=35.4\u00b5s\n2022-09-22T21:30:12.906236Z DEBUG hyper::proto::h1::io: flushed 242 bytes\n
"},{"location":"features/compression/","title":"Compression","text":"

SWS provides Gzip, Deflate, Brotli and Zstandard (zstd) compression of HTTP responses.

This feature is enabled by default and can be controlled by the boolean -x, --compression option or the equivalent SERVER_COMPRESSION env.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --compression true\n
"},{"location":"features/compression/#choice-of-compression-algorithm","title":"Choice of compression algorithm","text":"

The compression algorithm is determined by the Accept-Encoding header and the compression support built into SWS. By default SWS builds with support for Gzip, Deflate, Brotli and Zstandard algorithms.

"},{"location":"features/compression/#mime-types-compressed","title":"MIME types compressed","text":"

Compression is only applied to files with the MIME types listed below, indicating text and similarly well compressing formats. The asterisk * is a placeholder indicating an arbitrary MIME type part.

text/*\n*+xml\n*+json\napplication/rtf\napplication/javascript\napplication/json\napplication/xml\nfont/ttf\napplication/font-sfnt\napplication/vnd.ms-fontobject\napplication/wasm\n
"},{"location":"features/compression/#compression-level","title":"Compression level","text":"

SWS allows selecting the compression level via --compression-level command line option or the equivalent 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.

"},{"location":"features/cors/","title":"CORS","text":"

SWS provides optional Cross-Origin Resource Sharing (CORS) support.

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 env.

Below is an example of how to enable CORS.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --cors-allow-origins \"https://domain.com\"\n\n    # Or use an asterisk to allow any host\n    # --cors-allow-origins \"*\"\n
"},{"location":"features/cors/#allowed-headers","title":"Allowed headers","text":"

The server also supports a list of CORS allowed 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 env.

Tips

Below is an example of how to CORS.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --cors-allow-origins \"https://domain.com\" \\\n    --cors-allow-headers \"origin, content-type, x-requested-with\"\n
"},{"location":"features/cors/#exposed-headers","title":"Exposed headers","text":"

The server also supports a list of CORS-exposed headers to scripts 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 env.

Tips

Below is an example of how to CORS.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --cors-allow-origins \"https://domain.com\" \\\n    --cors-expose-headers \"origin, content-type, x-requested-with\"\n
"},{"location":"features/custom-http-headers/","title":"Custom HTTP Headers","text":"

SWS allows customizing the server HTTP Response headers on demand.

"},{"location":"features/custom-http-headers/#structure","title":"Structure","text":"

The Server HTTP response headers should be defined mainly as an Array of Tables.

Each table entry should have two key/value pairs:

A particular set of HTTP headers can only be applied when a source matches against the request URI.

Custom HTTP headers take precedence over existing ones

Whatever custom HTTP header could replace an existing one if it was previously defined (e.g. server default headers) and matches its source.

The header's order is important because determines its precedence.

Example: If the feature --cache-control-headers=true is enabled but also a custom cache-control header was defined then the custom header will have priority.

"},{"location":"features/custom-http-headers/#source","title":"Source","text":"

The source is a Glob pattern that should match against the URI that is requesting a resource file.

"},{"location":"features/custom-http-headers/#headers","title":"Headers","text":"

A set of valid plain HTTP headers to be applied.

"},{"location":"features/custom-http-headers/#examples","title":"Examples","text":"

Below are some examples of how to customize server HTTP headers in three variants.

"},{"location":"features/custom-http-headers/#one-line-version","title":"One-line version","text":"
[advanced]\n\n[[advanced.headers]]\nsource = \"**/*.{js,css}\"\nheaders = { Access-Control-Allow-Origin = \"*\" }\n
"},{"location":"features/custom-http-headers/#multiline-version","title":"Multiline version","text":"
[advanced]\n\n[[advanced.headers]]\nsource = \"*.html\"\n[advanced.headers.headers]\nCache-Control = \"public, max-age=36000\"\nContent-Security-Policy = \"frame-ancestors 'self'\"\nStrict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n
"},{"location":"features/custom-http-headers/#multiline-version-with-explicit-header-key-dotted","title":"Multiline version with explicit header key (dotted)","text":"
[advanced]\n\n[[advanced.headers]]\nsource = \"**/*.{jpg,jpeg,png,ico,gif}\"\nheaders.Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n
"},{"location":"features/directory-listing/","title":"Directory Listing","text":"

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 env.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --directory-listing true\n

And here is an example of how the directory listing looks like.

"},{"location":"features/directory-listing/#relative-paths-for-entries","title":"Relative paths for entries","text":"

SWS uses relative paths for the directory listing entries (file or directory) and is used regardless of the redirect trailing slash feature.

However, when the \"redirect trailing slash\" feature is disabled and a directory request URI doesn't contain a trailing slash then the entries will contain the path parent-dir/entry-name as the link value. Otherwise, just an entry-name link value is used (default behavior).

Note also that in both cases, SWS will append a trailing slash to the entry if is a directory.

"},{"location":"features/directory-listing/#sorting","title":"Sorting","text":"

Sorting by Name, Last modified and Size is enabled as clickable columns when the directory listing is activated via the --directory-listing=true option.

You can also use the sort query parameter to sort manually by certain attributes from URI. E.g https://localhost/?sort=5.

"},{"location":"features/directory-listing/#sorting-by-default","title":"Sorting by default","text":"

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 env.

To do so you have to pass a code sorting number. E.g --directory-listing-order=2.

"},{"location":"features/directory-listing/#code-numbers-for-sorting","title":"Code numbers for sorting","text":"

Below are the possible number code values for sorting or ordering which are grouped by attribute.

"},{"location":"features/directory-listing/#name","title":"Name","text":""},{"location":"features/directory-listing/#last-modified","title":"Last modified","text":""},{"location":"features/directory-listing/#size","title":"Size","text":""},{"location":"features/directory-listing/#default","title":"Default","text":"

Tips

Example:

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --directory-listing true \\\n    # E.g Sorting file/dir names in descending order\n    --directory-listing-order 1\n
"},{"location":"features/directory-listing/#output-format","title":"Output format","text":"

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 env.

Tips

"},{"location":"features/directory-listing/#html-format","title":"HTML format","text":"

This is the default format when --directory-listing is enabled.

"},{"location":"features/directory-listing/#json-format","title":"JSON format","text":"

The JSON format used is shown below for directories and files. Note that the size attribute is only available for files and the mtime value is UTC-based.

[\n  {\n    \"name\": \"my-directory\",\n    \"type\": \"directory\",\n    \"mtime\": \"2022-10-07T00:53:50Z\"\n  },\n  {\n    \"name\": \"my_file.tar.gz\",\n    \"type\": \"file\",\n    \"mtime\": \"2022-09-27T22:44:34Z\",\n    \"size\": 332\n  }\n]\n

Here is an example of serving the directory listing in JSON format.

static-web-server \\\n    -p=8787 -d=tests/fixtures/public -g=trace \\\n    --directory-listing=true \\\n    --directory-listing-format=\"json\"\n

And below is a client request example to illustrate how the feature works.

curl -iH \"content-type: application/json\" http://localhost:8787\n# HTTP/1.1 200 OK\n# content-type: application/json\n# content-length: 163\n# cache-control: max-age=86400\n# date: Tue, 11 Oct 2022 23:24:55 GMT\n\n# [{\"name\":\"sp\u00e9cial direct\u00f6ry\",\"type\":\"directory\",\"mtime\":\"2022-10-07T00:53:50Z\"},{\"name\":\"index.html.gz\",\"type\":\"file\",\"mtime\":\"2022-09-27T22:44:34Z\",\"size\":332}]\u23ce\n
"},{"location":"features/directory-listing/#directory-download","title":"Directory Download","text":"

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 env. Currently, targz format is supported.

static-web-server \\\n    --directory-listing=true \\\n    --directory-listing-download=targz\n

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.

"},{"location":"features/disable-symlinks/","title":"Disable Symlinks","text":"

SWS does follow symlinks by default. However, it's possible to disable all symlinks (deny access) by preventing to following files or directories if any path name component is a symbolic link. This applies to direct requests (URL) or those using the directory listing.

As a result, SWS will respond with a 403 Forbidden status if a symlink is requested or it won't be shown in the directory listing if enabled.

This feature is disabled by default and can be controlled by the boolean --disable-symlinks option or the equivalent SERVER_DISABLE_SYMLINKS env.

Here is an example of how to disable symlinks:

static-web-server \\\n    -p=8787 -d=./public -g=trace \\\n    --directory-listing \\\n    --disable-symlinks\n
"},{"location":"features/docker/","title":"Docker","text":"

SWS has first-class Docker support.

It is provided in three Docker image variants such as Scratch, Alpine and Debian images.

All images are available on Docker Hub and GitHub Container Registry.

"},{"location":"features/docker/#osarch","title":"OS/Arch","text":"

All Docker images are Multi-Arch and the following operating systems and architectures are supported.

Scratch and Alpine images use statically-linked binaries

Scratch and Alpine based Docker images use a statically-linked binary that is portable, performant and dependency-free thanks to musl libc, keeping containers as lean as possible.

Debian images use dynamically-linked binaries

Debian based Docker images use SWS dynamically-linked binaries, making containers highly optimized, performant and resource-efficient.

"},{"location":"features/docker/#rootless","title":"Rootless","text":"

The Debian and Alpine Docker images are rootless by default using a dedicated sws user and group. This reduces the attack surface and improves security.

Remember

Users can still run the containers as root if they explicitly set the user to root when running the container, e.g., using the --user root flag with docker run.

The static-web-server binary and all files under /home/sws (home directory) are owned by the non-root sws user and group.

For convenience, those paths are also available:

The current working directory is the home directory by default.

"},{"location":"features/docker/#run-a-container","title":"Run a container","text":"

To give the server a quick try just run the following commands.

Tips

To run SWS, there are several Docker image variants that you can use.

"},{"location":"features/docker/#scratch-just-the-binary","title":"Scratch (just the binary)","text":"
docker run --rm -it -p 8787:80 joseluisq/static-web-server:2 -g info\n# or\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:2 -g info\n
"},{"location":"features/docker/#alpine","title":"Alpine","text":"
docker run --rm -it -p 8787:80 joseluisq/static-web-server:2-alpine -g info\n# or\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:2-alpine -g info\n
"},{"location":"features/docker/#debian","title":"Debian","text":"
docker run --rm -it -p 8787:80 joseluisq/static-web-server:2-debian -g info\n# or\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:2-debian -g info\n
"},{"location":"features/docker/#development","title":"Development","text":"

Additionally, we publish development Docker images based on master branch changes.

# Scratch (just the binary)\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:devel -g info\n# Debian\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:devel-debian -g info\n
"},{"location":"features/docker/#dockerfile","title":"Dockerfile","text":"

SWS Docker images can be extended as needed.

Extending the Scratch Docker image (just the binary)

FROM joseluisq/static-web-server:2\n# or\nFROM ghcr.io/static-web-server/static-web-server:2\n# do stuff...\n

Or the Alpine

FROM joseluisq/static-web-server:2-alpine\n# or\nFROM ghcr.io/static-web-server/static-web-server:2-alpine\n# do stuff...\n

Or the Debian

FROM joseluisq/static-web-server:2-debian\n# or\nFROM ghcr.io/static-web-server/static-web-server:2-debian\n# do stuff...\n
"},{"location":"features/docker/#docker-compose","title":"Docker Compose","text":"

Example using Docker Compose.

version: \"3.3\"\n\nservices:\n  website:\n    image: joseluisq/static-web-server:2-alpine\n    container_name: \"website\"\n    ports:\n      - 80:80\n    restart: unless-stopped\n    environment:\n      # Note: those envs are customizable but also optional\n      - SERVER_ROOT=/var/public\n      - SERVER_CONFIG_FILE=/etc/sws.toml\n    volumes:\n      - ./public:/var/public\n      - ./sws.toml:/etc/sws.toml\n
"},{"location":"features/docker/#traefik-proxy","title":"Traefik Proxy","text":"

Example using Docker Swarm and Traefik Proxy.

  1. Create an external traefik_net Docker attachable network for Traefik:
    • docker network create --driver=overlay --attachable traefik_net
  2. Map a host directory like /var/www/website to the service container or create an external website_data Docker volume if you prefer:
    • docker volume create website_data
version: \"3.3\"\n\nservices:\n  traefik:\n    image: \"traefik:v2.11\"\n    command:\n      #- \"--log.level=DEBUG\"\n      - \"--api.insecure=true\"\n      - \"--providers.docker=true\"\n      - \"--providers.docker.exposedbydefault=false\"\n      - \"--entrypoints.web.address=:80\"\n    ports:\n      - \"80:80\"\n      - \"8080:8080\"\n    volumes:\n      - \"/var/run/docker.sock:/var/run/docker.sock:ro\"\n\n  website:\n    image: joseluisq/static-web-server:2\n    environment:\n      # Note: those envs are customizable but also optional\n      - SERVER_ROOT=/public\n    volumes:\n      - /var/www/website:/public\n      # Or use an existing Docker volume\n      # - website_data:/public\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.docker.network=traefik_net\"\n      - \"traefik.http.routers.website.entrypoints=web\"\n      - \"traefik.http.routers.website.rule=Host(`website.localhost`)\"\n      - \"traefik.http.routers.website.priority=1\"\n      - \"traefik.http.services.website.loadbalancer.server.port=80\"\n    networks:\n      - traefik_net\n\n# volumes:\n#   website_data:\n#     external: true\n\nnetworks:\n  traefik_net:\n    external: true\n
"},{"location":"features/docker/#kubernetes","title":"Kubernetes","text":"

Example using Kubernetes pod with liveness probe.

apiVersion: v1\nkind: Pod\nmetadata:\n  name: website\nspec:\n  containers:\n    - name: sws\n      image: ghcr.io/static-web-server/static-web-server\n      command:\n        - static-web-server\n        - --root=/public\n        - --health\n      ports:\n      - containerPort: 80\n      livenessProbe:\n        httpGet:\n          path: /health\n          port: http\n
"},{"location":"features/docker/#truecharts","title":"TrueCharts","text":"

The TrueCharts Community also provides a ready-to-use Static Web Server Helm-Chart that you can easily deploy in your Kubernetes.

"},{"location":"features/error-pages/","title":"Error Pages","text":"

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) or the --page50x (SERVER_ERROR_PAGE_50X) arguments.

Tip

Either --page404 and --page50x have defaults (optional values) so they can be specified or omitted as required.

Below is an example of how to customize those HTML pages.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --page404 ./my-page-404.html \\\n    --page50x ./my-page-50x.html\n
"},{"location":"features/error-pages/#fallback-page-for-use-with-client-routers","title":"Fallback Page for use with Client Routers","text":"

It is possible to provide a HTML file to be used as fallback page when GET request paths dont exist. The fallback page will be served with a 200 status code, useful when using client routers like React Router or similar. If the path is not specified or simply doesn't exist then this feature will not be activated.

The fallback page path is not relative to the root

The fallback page is an independent path, so provide a valid relative or absolute path.

It can be set with the SERVER_FALLBACK_PAGE environment variable or with the CLI argument --page-fallback.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --page-fallback ./my-public-dir/index.html\n
"},{"location":"features/file-descriptor-socket-passing/","title":"File Descriptor Socket Passing","text":"

SWS provides the ability to accept a socket listener as a file descriptor for use in sandboxing and on-demand applications via systemd (Linux), launchd (Macos) or similar.

Tip

The Socket Activation model is an alternative to TCP port binding.

Socket activation is supported by the -f, --fd option or the equivalent 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.

"},{"location":"features/file-descriptor-socket-passing/#systemd","title":"Systemd","text":"

If you're using systemd on Linux, there is a fully working example in the SWS Git repository under the ./systemd directory.

"},{"location":"features/file-descriptor-socket-passing/#service-example","title":"Service example","text":"

Below is a systemd service example. Follow the steps to create an SWS service using HTTP2 (static-web-server.service). The service will bind SWS to a TCP 443 privileged port without running the server as root.

If you want to change the server port used by the service, edit the value of ListenStream in the static-web-server.socket file.

The template files can be found in ./systemd directory.

# 1. Copy environment file template\n#    Use an environment variable file, add/modify the values if necessary and\n#    assign the proper owner/permissions to the environment variable file.\n#    TIP: you could skip this step and use a config file if you prefer.\ncp systemd/etc_default_static-web-server /etc/default/static-web-server\n\n# TIP: For example, you could create a `nologin` user with specific privileges.\n\n# 2. Copy service file templates\ncp systemd/static-web-server.s* /etc/systemd/system/\n\n# 3. Make sure that the `EnvironmentFile` and `ExecStart` values\n#    of the service match to the corresponding file paths in the `static-web-server.service` file.\n#    TIP: Use absolute paths. \n# EnvironmentFile=/etc/default/static-web-server\n# ExecStart=/usr/local/bin/static-web-server --fd 0\n\n# 4. Make sure to change this value with an existing user editing the `static-web-server.service` file.\n# SupplementaryGroups=www-data\n\n# 5. Create/reuse a directory for placing the certificate and private key.\n#    TIP: this is an example, you can create/reuse your own dirs.\nsudo mkdir /etc/static-web-server\n\n# 6. For example purposes, copy the testing cert/key files.\n#    TIP: Use your own cert/key files instead.\nsudo cp tests/tls/local.dev_cert.ecc.pem /etc/static-web-server/\nsudo cp tests/tls/local.dev_key.ecc.pem /etc/static-web-server/\n\n# 7. Create/reuse a root directory (example only)\nsudo mkdir -p /var/www/html\nsudo sh -c 'echo \"<h1>Static Web Server is running!</h1>\" > /var/www/html/index.html'\n\n# 8. Reload systemd manager configuration\nsudo systemctl daemon-reload\n\n# 9. Start the SWS service\nsudo systemctl start static-web-server.service\n\n# 10. Show the status of the SWS service running\nsudo systemctl status static-web-server.service\n\n# 11. Enable the service to start automatically at boot (optional)\nsudo systemctl enable static-web-server.service\n\n# 12. Analyze and debug the SWS service security\nsudo systemd-analyze security static-web-server.service\n#    If the service was successfully created then you should get something like:\n#    \u2192 Overall exposure level for static-web-server.service: 0.6 SAFE \ud83d\ude00\n
"},{"location":"features/file-descriptor-socket-passing/#testing","title":"Testing","text":"

Alternatively, the lightweight systemfd utility may be useful, especially for testing purposes.

For example, using systemfd utility as follows:

sudo systemfd --no-pid -s http::8091 -- path/to/static-web-server --fd 0\n

Or if you want to test using an environment variables file then you could use Enve.

sudo enve -f /path/to/environment.env systemfd --no-pid -s http::443 -- path/to/static-web-server --fd 0\n
"},{"location":"features/graceful-shutdown/","title":"Graceful Shutdown","text":"

SWS can terminate gracefully in what is known as a graceful shutdown process.

It means that when a SIGTERM termination signal is caught the server will stop receiving more requests immediately but in turn, it will continue processing all existing requests until they are completed (or closed).

Tips

"},{"location":"features/graceful-shutdown/#grace-period","title":"Grace Period","text":"

Sometimes one wants to control the graceful shutdown process for different reasons. For example during Kubernetes rollouts.

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 env.

Tip

The maximum grace period value is 255 seconds (4.25 min). The default value is 0 (no delay).

Here is an example of delaying the graceful shutdown process by 10 seconds after a SIGTERM.

static-web-server -p 8787 -d ./public/ -g trace --grace-period 10\n
"},{"location":"features/health-endpoint/","title":"Health endpoint","text":"

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.

The HTTP methods supported are GET and HEAD.

This feature is disabled by default and can be controlled by the boolean --health option or the equivalent SERVER_HEALTH env.

"},{"location":"features/health-endpoint/#usage-with-kubernetes-liveness-probe","title":"Usage with Kubernetes liveness probe","text":"

The health endpoint is well suited for the Kubernetes liveness probe:

apiVersion: v1\nkind: Pod\nmetadata:\n  name: frontend\nspec:\n  containers:\n    - name: sws\n      image: frontend:1.0.0\n      command:\n        - static-web-server\n        - --root=/public\n        - --log-level=info\n        - --health\n      ports:\n      - containerPort: 80\n        name: http\n      livenessProbe:\n        httpGet:\n          path: /health\n          port: http\n
"},{"location":"features/http-https-redirect/","title":"HTTP to HTTPS redirect","text":"

SWS provides support for redirecting HTTP requests to HTTPS via a 301 Moved Permanently 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 env.

HTTP/2 required

HTTPS redirect requires the HTTP/2 feature to be activated.

"},{"location":"features/http-https-redirect/#https-redirect","title":"HTTPS redirect","text":"

The boolean --https-redirect is the main option and controls the whole HTTPS redirect feature. If true then will tell SWS to redirect all requests with scheme http to https for the current server instance with a 301 Moved Permanently redirect status response code. This option depends on http2 to be enabled.

"},{"location":"features/http-https-redirect/#https-redirect-host","title":"HTTPS redirect host","text":"

The string --https-redirect-host option represents the canonical hostname or IP of the HTTPS (HTTPS/2) server. This is usually associated with the --host option, however here this value will be used as the destination for the redirected requests. It depends on \"https-redirect\" option to be enabled. The default is localhost.

"},{"location":"features/http-https-redirect/#https-redirect-from-port","title":"HTTPS redirect from port","text":"

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.

"},{"location":"features/http-https-redirect/#https-redirect-from-hosts","title":"HTTPS redirect from hosts","text":"

The string --https-redirect-from-hosts option represents a list of hostnames or IPs allowed to redirect from using comma-separated values. Incoming HTTP requests must contain the HTTP Host header and match against this list. It depends on \"https-redirect\" option to be enabled. The default value is localhost.

Tip: define hostnames/IPs to redirect from for increasing security

"},{"location":"features/http-https-redirect/#example","title":"Example","text":"

Below is an example of the feature.

static-web-server -p 4433 -d public/ -g trace \\\n    # HTTP/2 + TLS options\n    --http2=true \\\n    --http2-tls-cert=tests/tls/local.dev_cert.ecc.pem \\\n    --http2-tls-key=tests/tls/local.dev_key.ecc.pem \\\n\\\n    # HTTPS redirect options\n    --https-redirect=true \\\n    --https-redirect-host=\"localhost\" \\\n    --https-redirect-from-port=80 \\\n    --https-redirect-from-hosts=\"localhost\"\n    # or using multiple hostnames/IPs:\n    # --https-redirect-from-hosts = \"localhost,127.0.0.1\"\n

After running the server, the logs should look as follows.

.......\n2023-06-01T22:30:17.555338Z  INFO static_web_server::server: http to https redirect: enabled=true\n2023-06-01T22:30:17.555349Z  INFO static_web_server::server: http to https redirect host: localhost\n2023-06-01T22:30:17.555359Z  INFO static_web_server::server: http to https redirect from port: 80\n2023-06-01T22:30:17.555368Z  INFO static_web_server::server: http to https redirect from hosts: localhost\n2023-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\u00b5s\n2023-06-01T22:30:17.557547Z  INFO static_web_server::server: http2 server is listening on https://[::]:4433\n2023-06-01T22:30:17.557583Z  INFO Server::start_server{addr=[::]:80 threads=8}: static_web_server::server: close time.busy=0.00ns time.idle=1.92\u00b5s\n2023-06-01T22:30:17.557596Z  INFO static_web_server::server: http1 redirect server is listening on http://[::]:80\n2023-06-01T22:30:17.557768Z  INFO static_web_server::server: press ctrl+c to shut down the servers\n
"},{"location":"features/http-methods/","title":"HTTP Methods Supported","text":"

SWS only supports GET, HEAD and OPTIONS HTTP methods.

"},{"location":"features/http-methods/#options-method","title":"OPTIONS Method","text":""},{"location":"features/http-methods/#identifying-allowed-request-methods","title":"Identifying allowed request methods","text":"

The HTTP OPTIONS method can be used to send a request to check for permitted communication options for either a given URL or server.

Example using an HTTP client.

curl -I -X OPTIONS http://localhost:8787/assets/main.js\n# HTTP/1.1 204 No Content\n# allow: OPTIONS, HEAD, GET\n# accept-ranges: bytes\n# cache-control: max-age=31536000\n# date: Thu, 10 Mar 2022 21:26:01 GMT\n
"},{"location":"features/http-methods/#preflight-requests-in-cors","title":"Preflight requests in CORS","text":"

The HTTP OPTIONS method can also be used to send a request asking if it is acceptable to send requests to the server and if it is aware of using specific methods and headers.

Tip

If an Access-Control-Request-Method or Access-Control-Request-Headers value is not allowed then the server replies with a 403 Forbidden HTTP error. See CORS feature for more details.

Example using an HTTP client.

curl http://localhost:8787/assets/main.js \\\n    -I -X OPTIONS \\\n    -H \"Access-Control-Request-Method: HEAD\" \\\n    -H \"Access-Control-Request-Headers: content-type\" \\\n    -H \"Origin: http://localhost:8787\"\n# HTTP/1.1 204 No Content\n# access-control-allow-origin: http://localhost:8787\n# accept-ranges: bytes\n# access-control-allow-headers: content-type, origin\n# access-control-allow-methods: GET, OPTIONS, HEAD\n# cache-control: max-age=31536000\n# date: Thu, 10 Mar 2022 21:45:55 GMT\n
"},{"location":"features/http1/","title":"HTTP/1","text":"

The HTTP/1 is the protocol by default and can be used by specifying a host address via the -a, --host (SERVER_HOST) argument, the port of the host via -p, --port (SERVER_PORT) and the directory of the static files using the -d, --root (SERVER_ROOT) argument.

Tips

Below is an example of how to run the server using HTTP/1.

static-web-server \\\n    --host 127.0.0.1 \\\n    --port 8787 \\\n    --root ./my-public-dir\n
"},{"location":"features/http2-tls/","title":"HTTP/2 and TLS","text":"

SWS provides HTTP/2 protocol and TLS support.

This feature is disabled by default and can be activated via the boolean -t, --http2 option as well as string arguments --http2-tls-cert (TLS certificate file path) and --http2-tls-key (private key file path).

"},{"location":"features/http2-tls/#safe-tls-defaults","title":"Safe TLS defaults","text":"

SWS comes with safe TLS defaults for underlying cryptography.

These defaults are safe and useful for most use cases. See Rustls safe defaults for more details.

"},{"location":"features/http2-tls/#private-key-file-formats","title":"Private key file formats","text":"

Only the following private key file formats are supported:

"},{"location":"features/http2-tls/#example","title":"Example","text":"

Tips

static-web-server \\\n    --host 127.0.0.1 \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --http2 true \\\n    --http2-tls-cert ./my-tls.cert \\\n    --http2-tls-key ./my-tls.key\n
"},{"location":"features/ignore-files/","title":"Ignore files","text":"

SWS provides some options to ignore files or directories from being served and displayed if the directory listing is enabled.

"},{"location":"features/ignore-files/#ignore-hidden-files-dotfiles","title":"Ignore hidden files (dotfiles)","text":"

SWS doesn't ignore dotfiles (hidden files) by default. However, it's possible to ignore those files as shown below. As a result, SWS will respond with a 404 Not Found status.

This feature is disabled by default and can be controlled by the boolean --ignore-hidden-files option or the equivalent SERVER_IGNORE_HIDDEN_FILES env.

Here is an example of how to ignore hidden files:

static-web-server \\\n    -p=8787 -d=tests/fixtures/public -g=trace \\\n    --directory-listing=true \\\n    --ignore-hidden-files true\n
"},{"location":"features/logging/","title":"Logging","text":"

SWS provides logging support by just specifying a log level in lower case. The values allowed are error, warn, info, debug and trace. The default value is error.

This feature is enabled by default and can be controlled by the string -g, --log-level option or the equivalent SERVER_LOG_LEVEL env.

Below is an example of how to adjust the log level.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --log-level \"trace\"\n

Note: The log format is not well defined and is subject to change.

"},{"location":"features/logging/#log-output-with-ansi","title":"Log output with ANSI","text":"

SWS does not output ANSI escape codes by default. However, If you want ANSI escape for colors and other text formatting when logging then use the boolean --log-with-ansi CLI option and its equivalent SERVER_LOG_WITH_ANSI env.

For example, if you want colored log output then use the --log-with-ansi option as follows:

static-web-server -p 8788 -d ./public/ -g trace -z --log-with-ansi\n
"},{"location":"features/logging/#log-remote-addresses","title":"Log Remote Addresses","text":"

SWS provides Remote Address (IP) logging for every request via an INFO log level.

This feature is disabled by default and can be enabled by the boolean --log-remote-address option or the equivalent SERVER_LOG_REMOTE_ADDRESS env.

If the feature is enabled then log entries for requests will contain a remote_addr section with the remote address (IP) value. Otherwise, it will be empty.

Log entry example:

2022-05-23T22:24:50.519540Z  INFO static_web_server::handler: incoming request: method=GET uri=/ remote_addr=192.168.1.126:57625\n

Below is an example of how to enable Remote Address (IP) logging.

static-web-server -a \"0.0.0.0\" -p 8080 -d docker/public/ -g info --log-remote-address=true\n

The relevant log output:

INFO static_web_server::logger: logging level: info\n<...>\nINFO static_web_server::info: log requests with remote IP addresses: enabled=true\n<...>\nINFO static_web_server::handler: incoming request: method=GET uri=/ remote_addr=192.168.1.126:57625\nINFO static_web_server::handler: incoming request: method=GET uri=/favicon.ico remote_addr=192.168.1.126:57625\n
"},{"location":"features/logging/#logging-client-ip-from-x-real-ip-header","title":"Logging Client IP from X-Real-IP header","text":"

Some upstream proxies will report the client's real IP address in the X-Real-IP header.

To enable logging of the X-Real-IP header, enable the --log-x-real-ip option or the equivalent SERVER_LOG_X_REAL_IP environment variable.

When enabled, the log entries will look like:

INFO static_web_server::handler: incoming request: method=GET uri=/ x_real_ip=203.0.113.195\n

If the value of the X-Real-IP header does not parse as an IP address, no value will be logged.

To restrict the logging to only requests that originate from trusted proxy IPs, you can use the --trusted-proxies option, or the equivalent SERVER_TRUSTED_PROXIES env. This should be a list of IPs, separated by commas. An empty list (the default) indicates that all IPs should be trusted.

"},{"location":"features/logging/#logging-client-ip-from-x-forwarded-for-header","title":"Logging Client IP from X-Forwarded-For header","text":"

Note: This header should only be trusted when you know your upstream is handling X-Forwarded-For securely and when using the --trusted-proxies option.

When used behind a reverse proxy the reported remote_addr indicates the proxies IP address and port, not the client's real IP. The Proxy server can be configured to provide the X-Forwarded-For header, containing a comma-separated list of IP addresses, starting with the real remote client IP, and all following intermediate proxies (if any).

To enable logging of the real remote IP, enable the --log-forwarded-for option or the equivalent SERVER_LOG_FORWARDED_FOR env. By default this will log all requests which have a correctly formatted X-Forwarded-For header.

Since the content of the X-Forwarded-For header can be changed by all proxies in the chain, the remote IP address reported may not be trusted.

To restrict the logging to only requests that originate from trusted proxy IPs, you can use the --trusted-proxies option, or the equivalent SERVER_TRUSTED_PROXIES env. This should be a list of IPs, separated by commas. An empty list (the default) indicates that all IPs should be trusted.

Command used for the following examples:

static-web-server -a \"::\" --log-forwarded-for=true --trusted-proxies=\"::1\" -p 8080 -d docker/public/ -g info\n

Look for these lines in the log output:

<...>\nINFO static_web_server::info: log level: info\nINFO static_web_server::info: log requests with remote IP addresses: enabled=false\nINFO static_web_server::info: log X-Forwarded-For real remote IP addresses: enabled=true\nINFO static_web_server::info: trusted IPs for X-Forwarded-For: [::1]\n<...>\n

We can simulate request as from behind reverse proxy with additional intermediate-proxy with following command:

curl \"http://[::1]:8080\" --header \"X-Forwarded-For: 203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348\"\n

Log entry for this request will look like:

INFO static_web_server::handler: incoming request: method=GET uri=/ real_remote_ip=203.0.113.195\n

If we send the request from 127.0.0.1 instead:

curl \"http://127.0.0.1:8080\" --header \"X-Forwarded-For: 203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348\"\n

we get the following log output:

INFO static_web_server::handler: incoming request: method=GET uri=/\n

127.0.0.1 is not in the trusted_proxies, so we dont get a real_remote_address in the log.

Note the absence of the proxies remote address in these examples. If you want to log the remote address and the real remote address, you need to specify both --log-remote-address and --log-forwarded-for.

SWS will parse the X-Forwarded-For header and if the provided client IP is invalid, it will be ignored to prevent log poisoning attacks. In such cases the real_remote_ip section will not be added.

Example from above, but with invalid header:

curl \"http://[::1]:8080\" --header \"X-Forwarded-For: <iframe src=//malware.attack>\"\n
2022-05-23T22:24:50.519540Z  INFO static_web_server::handler: incoming request: method=GET uri=/\n
"},{"location":"features/maintenance-mode/","title":"Maintenance Mode","text":"

SWS provides a way to put a server into a maintenance mode. Allowing the server to respond with a custom HTTP status code and HTML content always by default.

This is useful to allow the server to be taken offline without disrupting the service.

The feature is disabled by default and can be controlled by the boolean --maintenance-mode option or the equivalent SERVER_MAINTENANCE_MODE env.

"},{"location":"features/maintenance-mode/#how-it-works","title":"How it works","text":"

When the feature is enabled, SWS will respond always with the specified (or default) status code and HTML content to every request ignoring all SWS features. Except the Health check, CORS and Basic Authentication features.

"},{"location":"features/maintenance-mode/#http-status-code","title":"HTTP Status Code","text":"

The --maintenance-mode-status or the equivalent SERVER_MAINTENANCE_MODE_STATUS env variable can be used to tell SWS to reply with a specific status code.

When not specified, the server will reply with the 503 Service Unavailable status.

"},{"location":"features/maintenance-mode/#html-page","title":"HTML Page","text":"

The --maintenance-mode-file or the equivalent SERVER_MAINTENANCE_MODE_FILE env variable can be also used to customize the response content.

The value should be an existing local HTML file path. When not provided a generic message will be displayed.

Optional

Remember that either --maintenance-mode-status and --maintenance-mode-file are optional and can be omitted as needed.

Independent path

The --maintenance-mode-file is an independent file path and not relative to the root.

"},{"location":"features/maintenance-mode/#example","title":"Example","text":"

For instance, the server will respond with a 503 Service Unavailable status code and a custom message.

static-web-server -p 8787 -d ./public \\\n    --maintenance-mode \\\n    # optional status code, `503` by default\n    --maintenance-mode-status=503 \\\n    # optional HTML page, generic message by default\n    --maintenance-mode-file=\"./maintenance.html\"\n
"},{"location":"features/man-pages-completions/","title":"Generated CLI documentation","text":"

SWS can generate documentation for its command-line interface through man pages and shell completions.

"},{"location":"features/man-pages-completions/#completions","title":"Completions","text":"

You can generate completions for these shells and completion engines:

By typing the following command, all completions will be exported to a specific directory path:

static-web-server generate --completions /my-completions-dir\n
"},{"location":"features/man-pages-completions/#man-pages","title":"Man Pages","text":"

You can also generate man pages and export them to a specific directory path:

static-web-server generate --man-pages /my-man-pages-dir\n

Additionally, if you want both to be generated then just type:

static-web-server generate ./my-cli-docs-dir\n
"},{"location":"features/markdown-content-negotiation/","title":"Markdown Content Negotiation","text":"

SWS provides an optional content negotiation feature that serves markdown files when clients explicitly request them via the Accept: text/markdown HTTP header.

This feature enables serving the raw markdown source of your documentation or content alongside the rendered HTML versions, allowing clients to choose their preferred format.

The HTTP methods supported are GET and HEAD.

This feature is disabled by default and can be controlled by the boolean --accept-markdown option or the equivalent SERVER_ACCEPT_MARKDOWN env.

"},{"location":"features/markdown-content-negotiation/#how-it-works","title":"How it works","text":"

When a client sends a request with the Accept: text/markdown header, SWS will search for markdown variants in the following order:

  1. path.md - Direct markdown file
  2. path.html.md - Markdown source file
  3. path/index.html.md - Directory index markdown source

If a markdown variant is found, it will be served with the Content-Type: text/markdown; charset=utf-8 header. If no markdown variant exists, the request falls back to normal static file handling.

Important: The feature only activates for the explicit Accept: text/markdown header. Wildcard headers like Accept: text/* or Accept: */* will not trigger markdown content negotiation.

"},{"location":"features/markdown-content-negotiation/#usage-examples","title":"Usage examples","text":""},{"location":"features/markdown-content-negotiation/#basic-usage","title":"Basic usage","text":"

Start the server with markdown content negotiation enabled:

static-web-server --root ./public --accept-markdown\n

Request a markdown file:

curl -H \"Accept: text/markdown\" http://localhost:8080/article\n# Returns: article.md if it exists\n
"},{"location":"features/markdown-content-negotiation/#with-environment-variable","title":"With environment variable","text":"
export SERVER_ACCEPT_MARKDOWN=true\nstatic-web-server --root ./public\n
"},{"location":"features/markdown-content-negotiation/#configuration-file","title":"Configuration file","text":"
[general]\nroot = \"./public\"\naccept-markdown = true\n
"},{"location":"features/markdown-content-negotiation/#use-cases","title":"Use cases","text":""},{"location":"features/markdown-content-negotiation/#llm-friendly-content-with-llmstxt","title":"LLM-friendly content with llms.txt","text":"

This feature is the perfect companion for the llms.txt standard, which recommends that websites provide clean markdown versions of their pages for Large Language Models (LLMs) to consume.

According to the llms.txt standard, pages should provide markdown versions at the same URL with .md appended (or index.html.md for URLs without file names). With SWS's markdown content negotiation, LLMs and AI agents can request these markdown versions using the Accept: text/markdown header:

# LLM requesting markdown version\ncurl -H \"Accept: text/markdown\" https://example.com/docs/api\n# Returns: docs/api.html.md if it exists\n\n# Directory index\ncurl -H \"Accept: text/markdown\" https://example.com/docs/\n# Returns: docs/index.html.md if it exists\n

This approach is superior to traditional .md URL suffixes because:

"},{"location":"features/markdown-content-negotiation/#documentation-sites","title":"Documentation sites","text":"

Serve both HTML and markdown versions of your documentation:

docs/\n\u251c\u2500\u2500 article.html       # Rendered version\n\u2514\u2500\u2500 article.html.md    # Markdown source\n

Browsers get the HTML version, while API clients or tools can request the markdown source:

# Browser request (default)\ncurl http://localhost:8080/article\n# Returns: article.html\n\n# Request markdown source\ncurl -H \"Accept: text/markdown\" http://localhost:8080/article\n# Returns: article.html.md\n
"},{"location":"features/markdown-content-negotiation/#api-responses","title":"API responses","text":"

Allow your API clients to request content in markdown format:

fetch('/documentation', {\n  headers: { 'Accept': 'text/markdown' }\n})\n.then(response => response.text())\n.then(markdown => console.log(markdown));\n
"},{"location":"features/markdown-content-negotiation/#content-management","title":"Content management","text":"

Serve markdown files for editing tools while providing HTML for end users:

# Editor fetching source\ncurl -H \"Accept: text/markdown\" http://localhost:8080/blog/post\n# Returns: blog/post.md\n\n# Regular browser visit\ncurl http://localhost:8080/blog/post\n# Returns: blog/post.html or blog/post/index.html\n
"},{"location":"features/multiple-index-files/","title":"Multiple index files","text":"

SWS allows to provide a list of files that will be used as an index for requests ending with the slash character (\u2018/\u2019).

Notes

This feature is disabled by default and can be controlled by the string list --index-files option or the equivalent SERVER_INDEX_FILES env.

Here is an example:

static-web-server -p 8787 -d ./public \\\n    --index-files=\"index.html, index.htm, default.html\"\n
"},{"location":"features/security-headers/","title":"Security Headers","text":"

SWS provides several security headers support.

When the HTTP/2 feature is activated security headers are enabled automatically.

This feature is disabled by default on HTTP/1 and can be controlled by the boolean --security-headers option or the equivalent SERVER_SECURITY_HEADERS env.

Customize HTTP headers

If you want to customize HTTP headers on demand then have a look at the Custom HTTP Headers section.

"},{"location":"features/security-headers/#headers-included","title":"Headers included","text":"

The following headers are included by default.

"},{"location":"features/trailing-slash-redirect/","title":"Trailing Slash Redirect","text":"

SWS provides automatic trailing slash redirect support for directory requests.

This feature is enabled by default and can be controlled by the boolean --redirect-trailing-slash option or the equivalent SERVER_REDIRECT_TRAILING_SLASH env.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --redirect-trailing-slash true\n
"},{"location":"features/url-redirects/","title":"URL Redirects","text":"

SWS provides the ability to redirect request URLs with Glob pattern-matching support.

URI redirects are particularly useful with pattern matching (globs). Use them for example to prevent broken links if you've moved a page or to shorten URLs.

"},{"location":"features/url-redirects/#structure","title":"Structure","text":"

The URL redirect rules should be defined mainly as an Array of Tables.

Each table entry should have the following key/value pairs:

Note

The incoming request(s) will reach the destination only if the request(s) URI matches the source pattern.

"},{"location":"features/url-redirects/#host","title":"Host","text":"

Optional host redirect entry to be matched against the incoming host URI. If a host redirect setting is specified then SWS will attempt to match the value against the incoming URI host (request), applying the required redirect entry or ignoring it otherwise.

www to non-www redirects

The host entry allows for instance to perform www to non-www redirects or vice versa (see example below).

"},{"location":"features/url-redirects/#source","title":"Source","text":"

The source is a Glob pattern that should match against the URI that is requesting a resource file.

The glob pattern functionality is powered by the globset crate which supports Standard Unix-style glob syntax.

Glob pattern syntax

For more details about the Glob pattern syntax check out https://docs.rs/globset/latest/globset/#syntax

Matching of path separator in *

Up to version 2.33.1 the wildcard * was matching the path separator. For example, /{*}/{*}/ matched /assets/images/logo/.

In later versions, the default has changed such that * does not match the path separator.

"},{"location":"features/url-redirects/#destination","title":"Destination","text":"

The value can be either a local file path that maps to an existing file on the system or an external URL. It could look like /some/directory/file.html. It is worth noting that the / at the beginning indicates the server's root directory.

"},{"location":"features/url-redirects/#replacements","title":"Replacements","text":"

Additionally, a destination supports replacements for every Glob pattern group that matches against the source. The replacement order starts from 0 to n and is defined with a dollar sign followed by an index (Glob pattern group occurrence).

Group your Glob patterns

When using replacements, also group your Glob pattern by surrounding them with curly braces so every group should map to its corresponding replacement. For example: source = \"**/{*}.{jpg,jpeg,svg}\"

"},{"location":"features/url-redirects/#kind","title":"Kind","text":"

It is a number that indicates the HTTP response code (redirect). The values can be:

"},{"location":"features/url-redirects/#examples","title":"Examples","text":"
[advanced]\n\n### URL Redirects\n\n# a. Simple route redirect example (existing file)\n[[advanced.redirects]]\nsource = \"**/*.{jpg,jpeg}\"\ndestination = \"/images/generic1.png\"\nkind = 301\n\n# b. Simple route redirect example (external URL)\n[[advanced.redirects]]\nsource = \"/index.html\"\ndestination = \"https://static-web-server.net\"\nkind = 302\n\n# c. Simple route redirect example with destination replacements\n[[advanced.redirects]]\n## Note that we're using curly braces to group the `*` wildcard.\n## See https://docs.rs/globset/latest/globset/#syntax\nsource = \"**/{*}.{jpg,jpeg,svg}\"\n## For example, the destination will result in `http://localhost/assets/abcdef.jpeg`\ndestination = \"http://localhost/assets/$1.$2\"\nkind = 301\n\n# d. Simple route redirect using the `host` option\n# to perform www to non-www redirection.\n[[advanced.redirects]]\nhost = \"www.domain.com\"\nsource = \"/{*}\"\ndestination = \"https://domain.com/$1\"\nkind = 301\n

If you request something like:

curl -I http://localhost:4433/abcdef.jpeg\n

Then the server logs should look something like this:

2023-07-11T21:11:22.217358Z  INFO static_web_server::handler: incoming request: method=HEAD uri=/abcdef.jpeg\n2023-07-11T21:11:22.217974Z DEBUG static_web_server::handler: url redirects glob pattern: [\"$0\", \"$1\", \"$2\"]\n2023-07-11T21:11:22.217992Z DEBUG static_web_server::handler: url redirects regex equivalent: (?-u)^(?:/?|.*/)(?:[^/]*)\\.(?:svg|jpeg|jpg)$\n2023-07-11T21:11:22.218002Z DEBUG static_web_server::handler: url redirects glob pattern captures: [\"abcdef.jpeg\", \"abcdef\", \"jpeg\"]\n2023-07-11T21:11:22.218076Z DEBUG static_web_server::handler: url redirects glob pattern destination: \"http://localhost/assets/$1.$2\"\n2023-07-11T21:11:22.218712Z DEBUG static_web_server::handler: url redirects glob pattern destination replaced: \"http://localhost/assets/abcdef.jpeg\"\n2023-07-11T21:11:22.218739Z TRACE static_web_server::handler: uri matches redirects glob pattern, redirecting with status '301 Moved Permanently'\n...\n
"},{"location":"features/url-rewrites/","title":"URL Rewrites","text":"

SWS provides the ability to rewrite request URLs (routes) with Glob pattern-matching support.

URI rewrites are particularly useful with pattern matching (globs), as the server can accept any URL that matches the pattern and let the client-side code decide what to display.

"},{"location":"features/url-rewrites/#structure","title":"Structure","text":"

URL rewrite rules should be defined mainly as an Array of Tables.

Each table entry should have two key/value pairs:

Note

The incoming request(s) will reach the destination only if the request(s) URI matches the source pattern.

"},{"location":"features/url-rewrites/#source","title":"Source","text":"

It's a Glob pattern that should match against the URI that is requesting a resource file.

The glob pattern functionality is powered by the globset crate which supports Standard Unix-style glob syntax.

Glob pattern syntax

For more details about the Glob pattern syntax check out https://docs.rs/globset/latest/globset/#syntax

Matching of path separator in *

Up to version 2.33.1 the wildcard * was matching the path separator. For example, /{*}/{*}/ matched /assets/images/logo/.

In later versions, the default has changed such that * does not match the path separator.

"},{"location":"features/url-rewrites/#destination","title":"Destination","text":"

The value should be a relative or absolute URL. A relative URL could look like /some/directory/file.html. An absolute URL can be https://external.example.com/ for example.

"},{"location":"features/url-rewrites/#replacements","title":"Replacements","text":"

Additionally, a destination supports replacements for every Glob pattern group that matches against the source.

Replacements order start from 0 to n and are defined with a dollar sign followed by an index (Glob pattern group occurrence).

Group your Glob patterns

When using replacements, also group your Glob pattern by surrounding them with curly braces so every group should map to its corresponding replacement. For example: source = \"**/{*}.{png,gif}\"

"},{"location":"features/url-rewrites/#destination-processing","title":"Destination processing","text":"

How destination is processed depends on whether the redirect key (see below) is present. If it is present, SWS will perform an external redirect. It will send a redirect response to the client, and the browser will usually proceed to the destination. In case of a relative URL, it will be another page on the same server. An absolute URL can result in navigation to another server.

Without a redirect key, SWS will perform an internal redirect. It will attempt to retrieve the file denoted by the destination and send it to the client. While it is possible to specify an absolute URL here as well, it will always be processed by the same SWS instance. It will result by the request being mapped to a different virtual host however if a matching virtual host is present.

"},{"location":"features/url-rewrites/#different-roots-within-the-same-virtual-host","title":"Different roots within the same virtual host","text":"

Normally, different root directories are only possible with different virtual hosts. Rewrites however allow exposing another root in a subdirectory for example. For that, you add an internal virtual host that isn't normally visible from outside, e.g. internal.local. You then rewrite the requests to the subdirectory to the internal virtual host. For example:

[general]\nroot = \"/usr/srv/www\"\n\n[advanced]\n\n[[advanced.rewrites]]\nsource = \"/test/{**}\"\ndestination = \"http://internal.local/test/$1\"\n\n[[advanced.virtual-hosts]]\nhost = \"internal.local\"\nroot = \"/usr/srv/alternative-root\"\n

A request to /index.html will be mapped to /usr/srv/www/index.html, yet /test/hi.txt will be mapped to the file /usr/srv/alternative-root/test/hi.txt.

This approach has two caveats:

  1. When SWS produces redirects (e.g. redirecting http://internal.local/test/subdir to http://internal.local/test/subdir/), it isn't aware of rewrites. Unless the path part of the URL is identical before and after rewrite (like in the example above), this will result in broken redirects.
  2. While the internal.local virtual host isn't normally accessed directly, this doesn't mean that it isn't possible for someone knowing (or guessing) its name. You should consider all files under the virtual host's root as public. Don't put any secrets in it even if these aren't accessible via rewrites.
"},{"location":"features/url-rewrites/#redirect","title":"Redirect","text":"

An optional number that indicates the HTTP response code (redirect). The values can be:

"},{"location":"features/url-rewrites/#examples","title":"Examples","text":"
[advanced]\n\n### URL Rewrites\n\n# a. Simple route rewrite example\n[[advanced.rewrites]]\nsource = \"**/*.{png,ico,gif}\"\ndestination = \"/assets/generic1.png\"\n\n# b. Route rewrite example with redirection\n[[advanced.rewrites]]\nsource = \"**/*.{jpg,jpeg}\"\ndestination = \"/images/generic2.png\"\n## NOTE: `redirect` can be omitted too\nredirect = 301\n\n# c. Route rewrite example with destination replacements\n[[advanced.rewrites]]\n## Note that we're using curly braces to group the `*` wildcard.\n## See https://docs.rs/globset/latest/globset/#syntax\nsource = \"**/{*}.{png,gif}\"\n## For example, the destination will result in `/assets/abcdef.png`\ndestination = \"/assets/$1.$2\"\n

If you request something like:

curl -I http://localhost/abcdef.png\n

Then the server logs should look something like this:

2023-07-08T20:31:36.606035Z  INFO static_web_server::handler: incoming request: method=HEAD uri=/abcdef.png\n2023-07-08T20:31:36.608439Z DEBUG static_web_server::handler: url rewrites glob patterns: [\"$0\", \"$1\", \"$2\"]\n2023-07-08T20:31:36.608491Z DEBUG static_web_server::handler: url rewrites regex equivalent: (?-u)^(?:/?|.*/)(?:[^/]*)\\.(?:gif|png)$\n2023-07-08T20:31:36.608525Z DEBUG static_web_server::handler: url rewrites glob pattern captures: [\"abcdef.png\", \"abcdef\", \"png\"]\n2023-07-08T20:31:36.608561Z DEBUG static_web_server::handler: url rewrites glob pattern destination: \"/assets/$1.$2\"\n2023-07-08T20:31:36.609655Z DEBUG static_web_server::handler: url rewrites glob patterns destination replaced: \"/assets/abcdef.png\"\n2023-07-08T20:31:36.609735Z TRACE static_web_server::static_files: dir: base=\"public\", route=\"assets/abcdef.png\"\n...\n
"},{"location":"features/virtual-hosting/","title":"Virtual Hosting","text":"

SWS provides rudimentary support for name-based virtual hosting. This allows you to serve files from different root directories depending on the \"Host\" header of the request, with all other settings staying the same.

All other settings are the same!

Each virtual host has to have all the same settings (aside from root). If using TLS, your certificates will have to cover all virtual host names as Subject Alternative Names (SANs). Also, beware of other conflicting settings like redirects and rewrites. If you find yourself needing different settings for different virtual hosts, it is recommended to run multiple instances of SWS.

Virtual hosting can be useful for serving more than one static website from the same SWS instance, if it's not otherwise feasible to run multiple instances of SWS. Browsers will automatically send a Host header which matches the hostname in the URL bar, which is how HTTP servers are able to tell which \"virtual\" host that the client is accessing.

By default, SWS will always serve files from the main root directory. If you configure virtual hosting and the \"Host\" header matches, SWS will instead look for files in an alternate root directory you specify.

"},{"location":"features/virtual-hosting/#examples","title":"Examples","text":"
# By default, all requests are served from here\nroot = \"/var/www/html\"\n\n[advanced]\n\n[[advanced.virtual-hosts]]\n# But if the \"Host\" header matches this...\nhost = \"sales.example.com\"\n# ...then files will be served from here instead\nroot = \"/var/sales/html\"\n\n[[advanced.virtual-hosts]]\nhost = \"blog.example.com\"\nroot = \"/var/blog/html\"\n
"},{"location":"features/webassembly/","title":"WebAssembly","text":"

SWS can run in a WebAssembly context.

Wasm/Wasix targets are not officially supported by SWS yet

We do not officially support Wasm or Wasix targets yet. But SWS project will eventually support Wasix as a target in a not remote future. In the meantime, Wasmer folks made it possible to run SWS via Wasix today via a series of patches.

"},{"location":"features/webassembly/#wasix","title":"Wasix","text":"

You can run SWS using The Wasmer Runtime with Wasix. See the wasmer/static-web-server package.

To run SWS, make sure to install Wasmer first and then enable its net and threads features as well as map your host directory via the mapdir option before starting the server.

Here is an example.

wasmer run wasmer/static-web-server \\\n    --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787\n

See The WASIX with Axum Tutorial for more details.

"},{"location":"features/windows-service/","title":"Windows Service","text":"

SWS can be also executed in a Windows Service context. Therefore it also provides a subcommand to install SWS as a Windows Service.

This feature is disabled by default and can be controlled by the boolean -s, --windows-service option or the equivalent SERVER_WINDOWS_SERVICE env.

Static Web Server running as a Windows Service and displayed by 'services.msc' application.

"},{"location":"features/windows-service/#important-notes","title":"Important Notes","text":""},{"location":"features/windows-service/#service-privileges","title":"Service privileges","text":"

To either install or uninstall the SWS Windows service requires administrator privileges, so make sure to open the terminal application as administrator or give your Powershell session enough privileges otherwise you will get an \"Access is denied\" error.

We recommend a Powershell session with administrator privileges.

"},{"location":"features/windows-service/#windows-firewall","title":"Windows Firewall","text":"

You can serve content with SWS in a Windows network. However, if you face issues running SWS it could be due to missing firewall configuration. So you probably have to define an inbound rule to allow inbound network traffic on a specified TCP port of your choice.

Follow the steps below to adjust your firewall:

  1. Configure an Inbound Port Rule in your Windows firewall so clients can reach the server's port.
  2. In your SWS config file, use the server IP as a host or a non-routable address like 0.0.0.0 if you prefer.
  3. Create a Windows Service following https://static-web-server.net/features/windows-service/ and start it.
  4. Finally, restart the service to apply the changes.

Note that the steps above are general and you have to adjust your firewall rule(s) according to your needs.

"},{"location":"features/windows-service/#install-the-service","title":"Install the service","text":"

To install the SWS service use the install command along with a configuration file for further SWS options customization.

Make sure to provide a configuration file to run SWS service properly. In particular, configure the server address, port and root directory accordingly. If not then the service might not start.

The following command will create the SWS service called static-web-server with a \"Static Web Server\" display name.

static-web-server.exe -w C:\\Users\\MyUser\\sws.toml install\n# Windows Service (static-web-server) is installed successfully!\n# Start the service typing: sc.exe start \"static-web-server\" (it requires administrator privileges) or using the 'services.msc' application.\n
"},{"location":"features/windows-service/#interact-with-the-service","title":"Interact with the service","text":"

SWS doesn't provide a way to interact with Windows services directly. Instead, use the Windows built-in tools to interact with the SWS service once created.

For that purpose, you can use either the Windows sc.exe or the services.msc application.

For example, using sc.exe you can show the SWS service configuration used once installed.

sc.exe qc \"static-web-server\"\n# [SC] QueryServiceConfig SUCCESS\n\n# SERVICE_NAME: static-web-server\n#         TYPE               : 10  WIN32_OWN_PROCESS\n#         START_TYPE         : 3   DEMAND_START\n#         ERROR_CONTROL      : 1   NORMAL\n#         BINARY_PATH_NAME   : C:\\Users\\MyUser\\static-web-server.exe\n#                                   --windows-service=true\n#                                   --config-file=C:\\Users\\MyUser\\sws.toml\n#         LOAD_ORDER_GROUP   :\n#         TAG                : 0\n#         DISPLAY_NAME       : Static Web Server\n#         DEPENDENCIES       :\n#         SERVICE_START_NAME : LocalSystem\n

Remember that alternatively, you can also use the services.msc application if you prefer GUI service control.

"},{"location":"features/windows-service/#start","title":"Start","text":"

To start the service use the following sc.exe command.

sc.exe start \"static-web-server\"\n# SERVICE_NAME: static-web-server\n#     TYPE               : 10  WIN32_OWN_PROCESS\n#     STATE              : 2  START_PENDING\n#                             (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)\n#     WIN32_EXIT_CODE    : 0  (0x0)\n#     SERVICE_EXIT_CODE  : 0  (0x0)\n#     CHECKPOINT         : 0x0\n#     WAIT_HINT          : 0x7d0\n#     PID                : 3068\n#     FLAGS              :\n
"},{"location":"features/windows-service/#status","title":"Status","text":"

To show the service status use the following sc.exe command.

sc.exe query \"static-web-server\"\n# SERVICE_NAME: static-web-server\n#     TYPE               : 10  WIN32_OWN_PROCESS\n#     STATE              : 4  RUNNING\n#                             (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)\n#     WIN32_EXIT_CODE    : 0  (0x0)\n#     SERVICE_EXIT_CODE  : 0  (0x0)\n#     CHECKPOINT         : 0x0\n#     WAIT_HINT          : 0x0\n
"},{"location":"features/windows-service/#stop","title":"Stop","text":"

To stop the service use the following sc.exe command.

sc.exe stop \"static-web-server\"\n# SERVICE_NAME: static-web-server\n#         TYPE               : 10  WIN32_OWN_PROCESS\n#         STATE              : 3  STOP_PENDING\n#                                 (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)\n#         WIN32_EXIT_CODE    : 0  (0x0)\n#         SERVICE_EXIT_CODE  : 0  (0x0)\n#         CHECKPOINT         : 0x2\n#         WAIT_HINT          : 0xbb8\n

After stopping the service you can also show its status.

sc.exe query \"static-web-server\"\n# SERVICE_NAME: static-web-server\n#         TYPE               : 10  WIN32_OWN_PROCESS\n#         STATE              : 1  STOPPED\n#         WIN32_EXIT_CODE    : 0  (0x0)\n#         SERVICE_EXIT_CODE  : 0  (0x0)\n#         CHECKPOINT         : 0x0\n#         WAIT_HINT          : 0x0\n
"},{"location":"features/windows-service/#uninstall-the-service","title":"Uninstall the service","text":"

To uninstall the SWS service just use the uninstall command. Note that the service should be first stopped before uninstalling it.

static-web-server.exe uninstall\n# Windows Service (static-web-server) is uninstalled!\n

After uninstalling the service you can verify if removed.

sc.exe qc \"static-web-server\"\n# [SC] OpenService FAILED 1060:\n#\n# The specified service does not exist as an installed service.\n
"},{"location":"features/worker-threads/","title":"Worker Threads Customization","text":"

SWS allows customizing the number of worker threads powered by the Tokio runtime.

See Tokio worker_threads API.

This feature can be controlled by the numeric -n, --threads-multiplier option or the equivalent SERVER_THREADS_MULTIPLIER env.

"},{"location":"features/worker-threads/#worker-threads-multiplier","title":"Worker threads multiplier","text":"

The value of -n, --threads-multiplier works as multiplier digits to determine the number of worker threads used by the server.

Multiplying this input number by the number of system CPUs.

The formula used is the following:

worker threads = number of CPUs * n

Where n is the input value of -n, --threads-multiplier.

For example: If there are 4 available CPUs and the --threads-multiplier is 8 then the total of worker threads to use will be 32.

Tip

When the --threads-multiplier input value is 0 or 1 then one thread per core is used (default value).

WebAssembly

We use 2 threads per core in Wasm and 1 in native environments by default.

Warn

The number of worker threads resulted should be a number between 1 and 32,768 though it is advised to keep this value on the smaller side. See Tokio worker_threads API for more details.

Below is an example of how to adjust the number of worker threads.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    # NOTE: \"8\" gets multiplied by the number of the available cores.\n    --threads-multiplier 8\n
"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"Overview","text":"Static Web Server A cross-platform, high-performance & asynchronous web server for static files serving"},{"location":"#overview","title":"Overview","text":"

Static Web Server (or SWS abbreviated) is a tiny and fast production-ready web server suitable to serve static web files or assets.

It is focused on lightness and easy-to-use principles while keeping high performance and safety powered by The Rust Programming Language.

Written on top of Hyper and Tokio runtime, it provides concurrent and asynchronous networking abilities and the latest HTTP/1 - HTTP/2 implementations.

Cross-platform and available for Linux, macOS, Windows, FreeBSD, NetBSD, Android, Docker and Wasm (via Wasmer).

"},{"location":"#features","title":"Features","text":""},{"location":"#benchmarks","title":"Benchmarks","text":"

For more details see the benchmarks repository.

For feedback or questions feel free to reach us on the discussions page.

"},{"location":"#community","title":"Community","text":"

SWS Community on Discord

"},{"location":"building-from-source/","title":"Building from Source","text":"

Follow these instructions to either build SWS project from the source or the HTML documentation.

"},{"location":"building-from-source/#building-project-from-source","title":"Building project from source","text":"

If you want to build SWS from the source, all you need is a Rust 2021 Edition installed.

So make sure to install Rust 1.85.0 or newer (or nightly) along with the toolchain(s) of your preference.

Then clone the repository and use Cargo to build the project from the source.

git clone https://github.com/static-web-server/static-web-server.git\ncd static-web-server\ncargo build --release\n

Finally, the release binary should be available at target/release/static-web-server or under your toolchain directory chosen.

Don't use the project's Makefile

Please don't use the project's Makefile since it's only intended for development and some on-demand tasks.

"},{"location":"building-from-source/#cargo-features","title":"Cargo features","text":"

When building from the source, all features are enabled by default. However, you can disable just the ones you don't need from the lists below.

Feature Description Default default Activates the default features by omission. all Activates all available features including the experimental feature. This is the default feature used when building SWS binaries. experimental Activates all SWS experimental features. Make sure to also provide the required RUSTFLAGS if the feature requires so. HTTP2/TLS http2 Activates the HTTP2 and TLS feature. Compression compression Activates auto-compression and compression static with all supported algorithms. compression-brotli Activates auto-compression/compression static with only the brotli algorithm. compression-deflate Activates auto-compression/compression static with only the deflate algorithm. compression-gzip Activates auto-compression/compression static with only the gzip algorithm. compression-zstd Activates auto-compression/compression static with only the zstd algorithm. Directory Listing directory-listing Activates the directory listing feature. Basic Authorization basic-auth Activates the Basic HTTP Authorization Schema feature. Fallback Page fallback-page Activates the Fallback Page feature."},{"location":"building-from-source/#disable-all-default-features","title":"Disable all default features","text":"

For example, if you want to run or build SWS without the default features like compression, http2, etc then just try:

# run\ncargo run --no-default-features -- -h\n\n# build\ncargo build --release --no-default-features\n\n# or build including all features (example)\nRUSTFLAGS=\"--cfg tokio_unstable\" \\\n    cargo build -vv --release --features all \n
"},{"location":"building-from-source/#cross-compiling","title":"Cross-compiling","text":"

If you want to cross-compile SWS then consider using Zig as linker for easier cross compiling.

Let's say, you want to cross-compile SWS from macOS to Linux. Then follow these steps.

  1. Add the necessary toolchain, for example just type: rustup target add x86_64-unknown-linux-gnu or rustup target add x86_64-unknown-linux-musl if a statically-linked binary is wanted.
  2. Install the latest Zig version via brew install zig
  3. Install cargo-zigbuild via cargo install cargo-zigbuild
  4. Finally, build SWS as follows:
    # dynamically-linked binary\ncargo zigbuild --verbose --release --target=x86_64-unknown-linux-gnu\n# or statically-linked binary\ncargo zigbuild --verbose --release --target=x86_64-unknown-linux-musl\n

Built binaries can be found under the corresponding toolchain directory inside target/.

"},{"location":"building-from-source/#testing","title":"Testing","text":"
# run tests for default features\ncargo test\n\n# run all tests without default features\ncargo test --tests --no-default-features\n\n# or run tests for all features including experimental ones\nRUSTFLAGS=\"--cfg tokio_unstable\" cargo test --features all\n\n# or run specific tests\ncargo test --test rewrites\n
"},{"location":"building-from-source/#building-documentation-from-source","title":"Building documentation from source","text":"

All HTML documentation is located in the docs/ project's directory and is built using Material for MkDocs.

It's only necessary to have Docker installed.

"},{"location":"building-from-source/#building-documentation","title":"Building documentation","text":"

By default the docs will be built in the /tmp/docs directory, to do so follow these steps.

git clone https://github.com/static-web-server/static-web-server.git\ncd static-web-server\nmkdir /tmp/docs\ndocker run -it --rm \\\n    -v $PWD/docs:/docs \\\n    -v /tmp/docs:/tmp/docs squidfunk/mkdocs-material build\n

Output the docs in a different directory

If you want to output the docs in a different directory then append the --site-dir=/new/dir/path/ argument to the \"squidfunk/mkdocs-material\" build command and make sure to provide the new directory path.

"},{"location":"building-from-source/#development-server","title":"Development server","text":"

If you want to improve the documentation then run the built-in development server via docs/docker-compose.yml.

git clone https://github.com/static-web-server/static-web-server.git\ncd static-web-server\ndocker-compose -f docs/docker-compose.yml up\n

Now the server will be available at localhost:8000

"},{"location":"building-from-source/#formatting-markdown-files","title":"Formatting Markdown files","text":"

This project makes use of mdformat to format Markdown files. The CI job devel-project-docs checks that all Markdown files are formatted correctly.

To format documentation changes, you can run mdformat manually using uv:

uvx --python \">=3.13\" --with mdformat-mkdocs mdformat ./*.md docs/\n
"},{"location":"contributions/","title":"Contributions","text":"

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in current work by you, as defined in the Apache License (Version 2.0) license, shall be dual licensed as described below, without any additional terms or conditions.

Feel free to send some Pull request or file an issue.

For feedback or questions feel free to reach us on the discussions page or join us on Discord Server.

"},{"location":"download-and-install/","title":"Download and Install","text":""},{"location":"download-and-install/#download-and-install","title":"Download and Install","text":"

Latest v2.40.1 release 2025-12-08 (changelog, sha256sum)

Linux 64-bit macOS 64-bit Windows 64-bit FreeBSD 64-bit

See also the release history on GitHub.

Docker

If you are working with Docker containers then check out the Docker feature page.

"},{"location":"download-and-install/#installation-methods","title":"Installation methods","text":""},{"location":"download-and-install/#binary-installer-linuxbsds","title":"Binary installer (Linux/BSDs)","text":"

Use the binary installer if your package manager is not supported.

With curl.

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh\n

Or with GNU wget (Busybox wget is not supported).

wget --https-only --secure-protocol=TLSv1_2 -qO- https://get.static-web-server.net | sh\n

The latest static-web-server version will be installed by default under the /usr/local/bin directory.

Alternatively, you can install a specific version of SWS to a custom location by setting environment variables.

export SWS_INSTALL_VERSION=\"2.40.1\" # full list at https://github.com/static-web-server/static-web-server/tags\nexport SWS_INSTALL_DIR=\"~/.local/bin\"\ncurl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh\n

Make sure you set the environment variables for the piped process (sh in our case), not the piping process (curl).

If you don't want to export environment variables then use:

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | SWS_INSTALL_DIR=\"~/.local/bin\" sh\n
"},{"location":"download-and-install/#arch-linux","title":"Arch Linux","text":"

Via Yay or your favorite AUR Helper.

yay -S static-web-server-bin\n
"},{"location":"download-and-install/#exherbo-linux","title":"Exherbo Linux","text":"

Add the rust repository and install the package through cave:

cave sync\ncave resolve -x repository/rust\ncave resolve -x static-web-server\n
"},{"location":"download-and-install/#nixos","title":"NixOS","text":"

Via Nix (Linux/MacOS)

nix-shell -p static-web-server\n# or\nnix-env -iA nixpkgs.static-web-server\n
"},{"location":"download-and-install/#macos","title":"MacOS","text":"

Using Homebrew Formulae (also Linux)

# Build from source\nbrew install static-web-server\n

Or using the SWS Homebrew Tap (also Linux)

brew tap static-web-server/static-web-server\n\n# Just the binary\nbrew install static-web-server-bin\n\n# Or build from source\nbrew install static-web-server\n
"},{"location":"download-and-install/#windows","title":"Windows","text":"

Via Scoop

scoop install static-web-server\n
"},{"location":"download-and-install/#webassembly","title":"WebAssembly","text":"

Via Wasmer

wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787\n
"},{"location":"download-and-install/#binaries","title":"Binaries","text":"

Pre-compiled binaries grouped by CPU architectures.

"},{"location":"download-and-install/#x86_64","title":"x86_64","text":""},{"location":"download-and-install/#arm64","title":"ARM64","text":""},{"location":"download-and-install/#x86","title":"x86","text":""},{"location":"download-and-install/#arm","title":"ARM","text":""},{"location":"download-and-install/#powerpc","title":"PowerPC","text":""},{"location":"download-and-install/#s390x","title":"S390X","text":""},{"location":"download-and-install/#source-files","title":"Source files","text":""},{"location":"download-and-install.template/","title":"Download and Install","text":"

Latest {{RELEASE_VERSION}} release {{RELEASE_DATE}} (changelog, sha256sum)

Linux 64-bit macOS 64-bit Windows 64-bit FreeBSD 64-bit

See also the release history on GitHub.

Docker

If you are working with Docker containers then check out the Docker feature page.

"},{"location":"download-and-install.template/#installation-methods","title":"Installation methods","text":""},{"location":"download-and-install.template/#binary-installer-linuxbsds","title":"Binary installer (Linux/BSDs)","text":"

Use the binary installer if your package manager is not supported.

With curl.

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh\n

Or with GNU wget (Busybox wget is not supported).

wget --https-only --secure-protocol=TLSv1_2 -qO- https://get.static-web-server.net | sh\n

static-web-server will be installed by default under the /usr/local/bin directory.

Alternatively, you can install a specific version of SWS to a custom location by setting environment variables.

export SWS_INSTALL_VERSION=\"{{RELEASE_VERSION_NUM}}\" # full list at https://github.com/static-web-server/static-web-server/tags\nexport SWS_INSTALL_DIR=\"~/.local/bin\"\ncurl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | sh\n

Make sure you set the environment variables for the piped process (sh in our case), not the piping process (curl).

If you don't want to export environment variables then use:

curl --proto '=https' --tlsv1.2 -sSfL https://get.static-web-server.net | SWS_INSTALL_DIR=\"~/.local/bin\" sh\n
"},{"location":"download-and-install.template/#arch-linux","title":"Arch Linux","text":"

Via Yay or your favorite AUR Helper.

yay -S static-web-server-bin\n
"},{"location":"download-and-install.template/#exherbo-linux","title":"Exherbo Linux","text":"

Add the rust repository and install the package through cave:

cave sync\ncave resolve -x repository/rust\ncave resolve -x static-web-server\n
"},{"location":"download-and-install.template/#nixos","title":"NixOS","text":"

Via Nix (Linux/MacOS)

nix-shell -p static-web-server\n# or\nnix-env -iA nixpkgs.static-web-server\n
"},{"location":"download-and-install.template/#macos","title":"MacOS","text":"

Using Homebrew Formulae (also Linux)

# Build from source\nbrew install static-web-server\n

Or using the SWS Homebrew Tap (also Linux)

brew tap static-web-server/static-web-server\n\n# Just the binary\nbrew install static-web-server-bin\n\n# Or build from source\nbrew install static-web-server\n
"},{"location":"download-and-install.template/#windows","title":"Windows","text":"

Via Scoop

scoop install static-web-server\n
"},{"location":"download-and-install.template/#webassembly","title":"WebAssembly","text":"

Via Wasmer

wasmer run wasmer/static-web-server --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787\n
"},{"location":"download-and-install.template/#binaries","title":"Binaries","text":"

Pre-compiled binaries grouped by CPU architectures.

"},{"location":"download-and-install.template/#x86_64","title":"x86_64","text":""},{"location":"download-and-install.template/#arm64","title":"ARM64","text":""},{"location":"download-and-install.template/#x86","title":"x86","text":""},{"location":"download-and-install.template/#arm","title":"ARM","text":""},{"location":"download-and-install.template/#powerpc","title":"PowerPC","text":""},{"location":"download-and-install.template/#s390x","title":"S390X","text":""},{"location":"download-and-install.template/#source-files","title":"Source files","text":""},{"location":"getting-started/","title":"Getting Started","text":"

Download and install the binary for your specific platform and then type

static-web-server --port 8787 --root ./my-public-dir\n

Or if you use Docker just try

docker run --rm -it -p 8787:80 joseluisq/static-web-server:2\n

Docker Tip

You can specify a Docker volume like -v $HOME/my-public-dir:/var/public to overwrite the default root directory. See Docker examples.

"},{"location":"license/","title":"License","text":"

This work is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0).

\u00a9 2019-present Jose Quintana

For feedback or questions feel free to reach us on the discussions page.

"},{"location":"migration/","title":"Migrating from v1 to v2","text":"

The v2 introduces notable changes including new features, performance improvements and new target support like ARM64 and OSes like FreeBSD, NetBSD, Illumos or Android.

This version v2 was re-written almost from scratch on top of Hyper and Tokio runtime which gives us the Rust asynchronous ability by default and the latest HTTP/1 - HTTP/2 implementation improvements. However, it still tries to keep the same principles of its v1: lightness and easy-to-use. Therefore migration should not be a big deal.

"},{"location":"migration/#v2-breaking-changes","title":"v2 breaking changes","text":"

This major v2 has a few breaking changes. However, migration should not represent a problem.

Tip

It is always worth recommending that you test a major server version upgrade like this first with your application(s) in a development environment or similar.

Please keep in mind the following changes in v2:

The rest of the known options are equivalent to v1 (except the new ones of course).

For feedback or questions feel free to reach us on the discussions page.

"},{"location":"platforms-architectures/","title":"Platforms & Architectures","text":"

Currently, only the following platforms/architectures are supported.

Docker tip

For the list of Docker images supported see Docker OS/Arch page.

"},{"location":"platforms-architectures/#linux","title":"Linux","text":""},{"location":"platforms-architectures/#x86","title":"x86","text":""},{"location":"platforms-architectures/#x86_64","title":"x86_64","text":""},{"location":"platforms-architectures/#arm","title":"ARM","text":""},{"location":"platforms-architectures/#arm64","title":"ARM64","text":""},{"location":"platforms-architectures/#macos","title":"macOS","text":""},{"location":"platforms-architectures/#x86_65","title":"x86_64","text":""},{"location":"platforms-architectures/#arm64_1","title":"ARM64","text":""},{"location":"platforms-architectures/#windows","title":"Windows","text":""},{"location":"platforms-architectures/#x86_1","title":"x86","text":""},{"location":"platforms-architectures/#x86_66","title":"x86_64","text":""},{"location":"platforms-architectures/#arm64_2","title":"ARM64","text":""},{"location":"platforms-architectures/#freebsd","title":"FreeBSD","text":""},{"location":"platforms-architectures/#x86_2","title":"x86","text":""},{"location":"platforms-architectures/#x86_67","title":"x86_64","text":""},{"location":"platforms-architectures/#netbsd","title":"NetBSD","text":""},{"location":"platforms-architectures/#x86_68","title":"x86_64","text":""},{"location":"platforms-architectures/#illumos","title":"Illumos","text":""},{"location":"platforms-architectures/#x86_69","title":"x86_64","text":""},{"location":"platforms-architectures/#powerpc","title":"PowerPC","text":""},{"location":"platforms-architectures/#s390x","title":"S390X","text":""},{"location":"report-security-issues/","title":"Report Security Issues","text":"

To report any security issues, please follow our Security Policy.

"},{"location":"semantic-versioning/","title":"Semantic Versioning","text":"

SWS project adheres to Semantic Versioning (SemVer) for every release like the latest v2.

The project privileges stability, security and performance, so you can rely on a major version like v2 and expect to get features (minor) and bug/security fixes (patch) without breaking changes.

"},{"location":"semantic-versioning/#breaking-changes-for-major-versions","title":"Breaking changes for major versions","text":"

Only switches between major versions \"could\" contain breaking changes or not depending on the particular case. However, this project tries to keep away from any kind of breaking change possible between major versions. But it still supports and prioritizes \"no breaking changes\" for minor and patch (bug fixes) versions.

On the other hand, if a breaking change is found to be \"strictly necessary\" to do. Then a new major version must be promoted as well and users informed accordingly so they can do a seamless transition.

"},{"location":"showcases/","title":"Showcases","text":"

The following is a collection of articles, blog posts, and projects that showcase the use of Static Web Server (SWS). These resources provide insights, tutorials and real-world applications using SWS in various contexts.

"},{"location":"showcases/#projects","title":"Projects","text":"

Projects that utilize Static Web Server as part of their infrastructure or development workflow.

"},{"location":"showcases/#articles","title":"Articles","text":"

Posts and articles discussing the implementation and benefits of using Static Web Server.

"},{"location":"configuration/command-line-arguments/","title":"Command-Line Arguments","text":"

The server can be configured via the following command-line arguments.

Remember

$ static-web-server -h\nA cross-platform, high-performance and asynchronous web server for static files-serving.\n\nUsage: static-web-server [OPTIONS] [COMMAND]\n\nCommands:\n  generate  Generate man pages and shell completions\n  help      Print this message or the help of the given subcommand(s)\n\nOptions:\n  -a, --host <HOST>\n          Host address (E.g 127.0.0.1 or ::1) [env: SERVER_HOST=] [default: ::]\n  -p, --port <PORT>\n          Host port [env: SERVER_PORT=] [default: 80]\n  -f, --fd <FD>\n          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=]\n  -n, --threads-multiplier <THREADS_MULTIPLIER>\n          Number of worker threads multiplier that'll be multiplied by the number of system CPUs using the formula: `worker threads = number of CPUs * n` where `n` is the value that changes here. When multiplier value is 0 or 1 then one thread per core is used. Number of worker threads result should be a number between 1 and 32,768 though it is advised to keep this value on the smaller side [env: SERVER_THREADS_MULTIPLIER=] [default: 1]\n  -b, --max-blocking-threads <MAX_BLOCKING_THREADS>\n          Maximum number of blocking threads [env: SERVER_MAX_BLOCKING_THREADS=] [default: 512]\n  -d, --root <ROOT>\n          Root directory path of static files [env: SERVER_ROOT=] [default: ./public]\n      --page50x <PAGE50X>\n          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]\n      --page404 <PAGE404>\n          HTML file path for 404 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_404=] [default: ./404.html]\n      --page-fallback <PAGE_FALLBACK>\n          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 [env: SERVER_FALLBACK_PAGE=] [default: ]\n  -g, --log-level <LOG_LEVEL>\n          Specify a logging level in lower case. Values: error, warn, info, debug or trace [env: SERVER_LOG_LEVEL=] [default: error]\n      --log-with-ansi [<LOG_WITH_ANSI>]\n          Enable or disable ANSI escape codes for colors and other text formatting of the log output [env: SERVER_LOG_WITH_ANSI=] [default: false] [possible values: true, false]\n  -c, --cors-allow-origins <CORS_ALLOW_ORIGINS>\n          Specify an optional CORS list of allowed origin hosts separated by commas. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host [env: SERVER_CORS_ALLOW_ORIGINS=] [default: ]\n  -j, --cors-allow-headers <CORS_ALLOW_HEADERS>\n          Specify an optional CORS list of allowed headers separated by commas. Default \"origin, content-type\". It requires `--cors-allow-origins` to be used along with [env: SERVER_CORS_ALLOW_HEADERS=] [default: \"origin, content-type, authorization\"]\n      --cors-expose-headers <CORS_EXPOSE_HEADERS>\n          Specify an optional CORS list of exposed headers separated by commas. Default \"origin, content-type\". It requires `--cors-expose-origins` to be used along with [env: SERVER_CORS_EXPOSE_HEADERS=] [default: \"origin, content-type\"]\n  -t, --http2 [<HTTP2>]\n          Enable HTTP/2 with TLS support [env: SERVER_HTTP2_TLS=] [default: false] [possible values: true, false]\n      --http2-tls-cert <HTTP2_TLS_CERT>\n          Specify the file path to read the certificate [env: SERVER_HTTP2_TLS_CERT=]\n      --http2-tls-key <HTTP2_TLS_KEY>\n          Specify the file path to read the private key [env: SERVER_HTTP2_TLS_KEY=]\n      --https-redirect [<HTTPS_REDIRECT>]\n          Redirect all requests with scheme \"http\" to \"https\" for the current server instance. It depends on \"http2\" to be enabled [env: SERVER_HTTPS_REDIRECT=] [default: false] [possible values: true, false]\n      --https-redirect-host <HTTPS_REDIRECT_HOST>\n          Canonical host name or IP of the HTTPS (HTTPS/2) server. It depends on \"https_redirect\" to be enabled [env: SERVER_HTTPS_REDIRECT_HOST=] [default: localhost]\n      --https-redirect-from-port <HTTPS_REDIRECT_FROM_PORT>\n          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]\n      --https-redirect-from-hosts <HTTPS_REDIRECT_FROM_HOSTS>\n          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]\n      --index-files <INDEX_FILES>\n          List of files that will be used as an index for requests ending with the slash character (\u2018/\u2019). Files are checked in the specified order [env: SERVER_INDEX_FILES=] [default: index.html]\n  -x, --compression [<COMPRESSION>]\n          Gzip, Deflate, Brotli or Zstd compression on demand determined by the Accept-Encoding header and applied to text-based web file types only [env: SERVER_COMPRESSION=] [default: true] [possible values: true, false]\n      --compression-level <COMPRESSION_LEVEL>\n          Compression level to apply for Gzip, Deflate, Brotli or Zstd compression [env: SERVER_COMPRESSION_LEVEL=] [default: default] [possible values: fastest, best, default]\n      --compression-static [<COMPRESSION_STATIC>]\n          Look up the pre-compressed file variant (`.gz`, `.br` or `.zst`) on disk of a requested file and serves it directly if available. The compression type is determined by the `Accept-Encoding` header [env: SERVER_COMPRESSION_STATIC=] [default: false] [possible values: true, false]\n  -z, --directory-listing [<DIRECTORY_LISTING>]\n          Enable directory listing for all requests ending with the slash character (\u2018/\u2019) [env: SERVER_DIRECTORY_LISTING=] [default: false] [possible values: true, false]\n      --directory-listing-order <DIRECTORY_LISTING_ORDER>\n          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]\n      --directory-listing-format <DIRECTORY_LISTING_FORMAT>\n          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]\n      --directory-listing-download=<DIRECTORY_LISTING_DOWNLOAD>\n          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]\n      --security-headers [<SECURITY_HEADERS>]\n          Enable security headers by default when HTTP/2 feature is activated. Headers included: \"Strict-Transport-Security: max-age=63072000; includeSubDomains; preload\" (2 years max-age), \"X-Frame-Options: DENY\" and \"Content-Security-Policy: frame-ancestors 'self'\" [env: SERVER_SECURITY_HEADERS=] [default: false] [possible values: true, false]\n  -e, --cache-control-headers [<CACHE_CONTROL_HEADERS>]\n          Enable cache control headers for incoming requests based on a set of file types. The file type list can be found on `src/control_headers.rs` file [env: SERVER_CACHE_CONTROL_HEADERS=] [default: true] [possible values: true, false]\n      --basic-auth <BASIC_AUTH>\n          It provides The \"Basic\" HTTP Authentication scheme using credentials as \"user-id:password\" pairs. Password must be encoded using the \"BCrypt\" password-hashing function [env: SERVER_BASIC_AUTH=] [default: ]\n  -q, --grace-period <GRACE_PERIOD>\n          Defines a grace period in seconds after a `SIGTERM` signal is caught which will delay the server before to shut it down gracefully. The maximum value is 255 seconds [env: SERVER_GRACE_PERIOD=] [default: 0]\n  -w, --config-file <CONFIG_FILE>\n          Server TOML configuration file path [env: SERVER_CONFIG_FILE=] [default: ./sws.toml]\n      --log-remote-address [<LOG_REMOTE_ADDRESS>]\n          Log incoming requests information along with its remote address if available using the `info` log level [env: SERVER_LOG_REMOTE_ADDRESS=] [default: false] [possible values: true, false]\n      --log-x-real-ip [<LOG_X_REAL_IP>]\n          Log the X-Real-IP header for remote IP information [env: SERVER_LOG_X_REAL_IP=] [default: false] [possible values: true, false]\n      --log-forwarded-for [<LOG_FORWARDED_FOR>]\n          Log the X-Forwarded-For header for remote IP information [env: SERVER_LOG_FORWARDED_FOR=] [default: false] [possible values: true, false]\n      --trusted-proxies <TRUSTED_PROXIES>\n          List of IPs to use X-Forwarded-For from. The default is to trust all [env: SERVER_TRUSTED_PROXIES=]\n      --redirect-trailing-slash [<REDIRECT_TRAILING_SLASH>]\n          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]\n      --ignore-hidden-files [<IGNORE_HIDDEN_FILES>]\n          Ignore hidden files/directories (dotfiles), preventing them to be served and being included in auto HTML index pages (directory listing) [env: SERVER_IGNORE_HIDDEN_FILES=] [default: false] [possible values: true, false]\n      --disable-symlinks [<DISABLE_SYMLINKS>]\n          Prevent following files or directories if any path name component is a symbolic link [env: SERVER_DISABLE_SYMLINKS=] [default: false] [possible values: true, false]\n      --health [<HEALTH>]\n          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]\n      --accept-markdown [<ACCEPT_MARKDOWN>]\n          Enable markdown content negotiation. When a client sends Accept: text/markdown header, the server will serve markdown files (.md or .html.md) if available [env: SERVER_ACCEPT_MARKDOWN=] [default: false] [possible values: true, false]\n      --maintenance-mode [<MAINTENANCE_MODE>]\n          Enable the server's maintenance mode functionality [env: SERVER_MAINTENANCE_MODE=] [default: false] [possible values: true, false]\n      --maintenance-mode-status <MAINTENANCE_MODE_STATUS>\n          Provide a custom HTTP status code when entering into maintenance mode. Default 503 [env: SERVER_MAINTENANCE_MODE_STATUS=] [default: 503]\n      --maintenance-mode-file <MAINTENANCE_MODE_FILE>\n          Provide a custom maintenance mode HTML file. If not provided then a generic message will be displayed [env: SERVER_MAINTENANCE_MODE_FILE=] [default: ]\n  -V, --version\n          Print version info and exit\n  -h, --help\n          Print help (see more with '--help')\n
"},{"location":"configuration/command-line-arguments/#windows","title":"Windows","text":"

The following options and commands are Windows platform-specific.

 -s, --windows-service <windows-service>\n            Run the web server as a Windows Service [env: SERVER_WINDOWS_SERVICE=]  [default: false]\n\nSUBCOMMANDS:\n    help         Prints this message or the help of the given subcommand(s)\n    install      Install a Windows Service for the web server\n    uninstall    Uninstall the current Windows Service\n
"},{"location":"configuration/config-file/","title":"TOML Configuration File","text":"

SWS can be configured using a TOML 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 env.

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. If it is not found or can not be loaded then SWS will continue using the server defaults.

"},{"location":"configuration/config-file/#toml-file-manifest","title":"TOML File (Manifest)","text":"

Below is just an example showing all features with their default values.

[general]\n\n#### Address & Root dir\nhost = \"::\"\nport = 80\nroot = \"./public\"\n\n#### Logging\nlog-level = \"error\"\n\n#### Cache Control headers\ncache-control-headers = true\n\n#### Auto Compression\ncompression = true\ncompression-level = \"default\"\n\n#### Error pages\n# Note: If a relative path is used then it will be resolved under the root directory.\npage404 = \"./404.html\"\npage50x = \"./50x.html\"\n\n#### HTTP/2 + TLS\nhttp2 = false\nhttp2-tls-cert = \"\"\nhttp2-tls-key = \"\"\nhttps-redirect = false\nhttps-redirect-host = \"localhost\"\nhttps-redirect-from-port = 80\nhttps-redirect-from-hosts = \"localhost\"\n\n#### CORS & Security headers\n# security-headers = true\n# cors-allow-origins = \"\"\n\n#### Directory listing\ndirectory-listing = false\n\n#### Directory listing sorting code\ndirectory-listing-order = 1\n\n#### Directory listing content format\ndirectory-listing-format = \"html\"\n\n#### Directory listing download format\ndirectory-listing-download = []\n\n#### Basic Authentication\n# basic-auth = \"\"\n\n#### File descriptor binding\n# fd = \"\"\n\n#### Worker threads\nthreads-multiplier = 1\n\n#### Grace period after a graceful shutdown\ngrace-period = 0\n\n#### Page fallback for 404s\n# page-fallback = \"\"\n\n#### Log request Remote Address if available\nlog-remote-address = false\n\n#### Log real IP from X-Forwarded-For header if available\nlog-forwarded-for = false\n\n#### IPs to accept the X-Forwarded-For header from. Empty means all\ntrusted-proxies = []\n\n#### Redirect to trailing slash in the requested directory uri\nredirect-trailing-slash = true\n\n#### Check for existing pre-compressed files\ncompression-static = true\n\n#### Health-check endpoint (GET or HEAD `/health`)\nhealth = false\n\n#### Markdown content negotiation\naccept-markdown = false\n\n#### List of index files\n# index-files = \"index.html, index.htm\"\n#### Maintenance Mode\n\nmaintenance-mode = false\n# maintenance-mode-status = 503\n# maintenance-mode-file = \"./maintenance.html\"\n\n### Windows Only\n\n#### Run the web server as a Windows Service\n# windows-service = false\n\n\n[advanced]\n\n#### HTTP Headers customization (examples only)\n\n#### a. Oneline version\n# [[advanced.headers]]\n# source = \"**/*.{js,css}\"\n# headers = { Access-Control-Allow-Origin = \"*\" }\n\n#### b. Multiline version\n# [[advanced.headers]]\n# source = \"/index.html\"\n# [advanced.headers.headers]\n# Cache-Control = \"public, max-age=36000\"\n# Content-Security-Policy = \"frame-ancestors 'self'\"\n# Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n\n#### c. Multiline version with explicit key (dotted)\n# [[advanced.headers]]\n# source = \"**/*.{jpg,jpeg,png,ico,gif}\"\n# headers.Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n\n\n### URL Redirects (examples only)\n\n# [[advanced.redirects]]\n# source = \"**/*.{jpg,jpeg}\"\n# destination = \"/images/generic1.png\"\n# kind = 301\n\n# [[advanced.redirects]]\n# source = \"/index.html\"\n# destination = \"https://static-web-server.net\"\n# kind = 302\n\n### URL Rewrites (examples only)\n\n# [[advanced.rewrites]]\n# source = \"**/*.{png,ico,gif}\"\n# destination = \"/assets/favicon.ico\"\n## Optional redirection\n# redirect = 301\n\n# [[advanced.rewrites]]\n# source = \"**/*.{jpg,jpeg}\"\n# destination = \"/images/sws.png\"\n\n### Virtual Hosting\n\n# [[advanced.virtual-hosts]]\n## But if the \"Host\" header matches this...\n# host = \"sales.example.com\"\n## ...then files will be served from here instead\n# root = \"/var/sales/html\"\n\n# [[advanced.virtual-hosts]]\n# host = \"blog.example.com\"\n# root = \"/var/blog/html\"\n
"},{"location":"configuration/config-file/#general-options","title":"General options","text":"

The TOML [general] section allows adjusting the current options available via the CLI/ENV ones.

So they are equivalent to each other except for the -w, --config-file option which is omitted and can not be used for obvious reasons.

Config file-based features are optional

All server feature options via the configuration file are optional and can be omitted as needed.

"},{"location":"configuration/config-file/#advanced-options","title":"Advanced options","text":"

The TOML [advanced] section is intended for more complex features.

For example Custom HTTP Headers, Custom URL Redirects, URL Rewrites, or Virtual Hosting

"},{"location":"configuration/config-file/#precedence","title":"Precedence","text":"

Whatever config file-based feature option will take precedence over its CLI or ENV equivalent.

"},{"location":"configuration/config-file/#usage","title":"Usage","text":"

The following command runs the server using a specific sws.toml file.

static-web-server -w sws.toml\n
"},{"location":"configuration/environment-variables/","title":"Environment Variables","text":"

The server can be configured via the following environment variables.

Remember

"},{"location":"configuration/environment-variables/#server_host","title":"SERVER_HOST","text":"

The address of the host (e.g. 127.0.0.1). Default [::].

"},{"location":"configuration/environment-variables/#server_port","title":"SERVER_PORT","text":"

The port of the host. Default 80.

"},{"location":"configuration/environment-variables/#server_listen_fd","title":"SERVER_LISTEN_FD","text":"

Optional file descriptor number (e.g. 0) to inherit an already-opened TCP listener (instead of using SERVER_HOST and/or SERVER_PORT). Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_root","title":"SERVER_ROOT","text":"

Relative or absolute root directory path of static files. Default ./public.

"},{"location":"configuration/environment-variables/#server_config_file","title":"SERVER_CONFIG_FILE","text":"

The Server configuration file path is in TOML format. See The TOML Configuration File.

"},{"location":"configuration/environment-variables/#server_grace_period","title":"SERVER_GRACE_PERIOD","text":"

Defines a grace period in seconds after a SIGTERM signal is caught which will delay the server before shutting it down gracefully. The maximum value is 255 seconds. The default value is 0 (no delay).

"},{"location":"configuration/environment-variables/#server_log_level","title":"SERVER_LOG_LEVEL","text":"

Specify a logging level in lowercase. Possible values are error, warn, info, debug or trace. Default error.

"},{"location":"configuration/environment-variables/#server_log_with_ansi","title":"SERVER_LOG_WITH_ANSI","text":"

Enable or disable ANSI escape codes for colors and other text formatting of the log output.

"},{"location":"configuration/environment-variables/#server_log_remote_address","title":"SERVER_LOG_REMOTE_ADDRESS","text":"

Log incoming request information along with its Remote Address (IP) if available using the info log level. Default false.

"},{"location":"configuration/environment-variables/#server_log_x_real_ip","title":"SERVER_LOG_X_REAL_IP","text":"

Log the X-Real-IP header if available using the info log level. Default false.

"},{"location":"configuration/environment-variables/#server_log_forwarded_for","title":"SERVER_LOG_FORWARDED_FOR","text":"

Log the X-Forwarded-For header if available using the info log level. Default false.

"},{"location":"configuration/environment-variables/#server_trusted_proxies","title":"SERVER_TRUSTED_PROXIES","text":"

A comma separated list of IP addresses to accept the X-Forwarded-For header from. An empty string means trust all IPs. Default \"\"

"},{"location":"configuration/environment-variables/#server_error_page_404","title":"SERVER_ERROR_PAGE_404","text":"

HTML file path for 404 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. Default ./404.html.

"},{"location":"configuration/environment-variables/#server_error_page_50x","title":"SERVER_ERROR_PAGE_50X","text":"

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. Default ./50x.html

"},{"location":"configuration/environment-variables/#server_fallback_page","title":"SERVER_FALLBACK_PAGE","text":"

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.

"},{"location":"configuration/environment-variables/#server_threads_multiplier","title":"SERVER_THREADS_MULTIPLIER","text":"

The number of worker threads multiplier will be multiplied by the number of system CPUs using the formula: worker threads = number of CPUs * n where n is the value that changes here. When the multiplier value is 0 or 1 then the number of CPUs is used. The number of worker threads result should be a number between 1 and 32,768 though it is advised to keep this value on the smaller side. Default one thread per core.

"},{"location":"configuration/environment-variables/#server_max_blocking_threads","title":"SERVER_MAX_BLOCKING_THREADS","text":"

Maximum number of blocking threads.

"},{"location":"configuration/environment-variables/#server_http2_tls","title":"SERVER_HTTP2_TLS","text":"

Enable HTTP/2 with TLS support. Make sure also to adjust the current server port. Default false (disabled).

"},{"location":"configuration/environment-variables/#server_http2_tls_cert","title":"SERVER_HTTP2_TLS_CERT","text":"

Specify the file path to read the certificate. Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_http2_tls_key","title":"SERVER_HTTP2_TLS_KEY","text":"

Specify the file path to read the private key. Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_https_redirect","title":"SERVER_HTTPS_REDIRECT","text":"

Redirect all requests with scheme \"http\" to \"https\" for the current server instance. It depends on \"http2\" to be enabled.

"},{"location":"configuration/environment-variables/#server_https_redirect_host","title":"SERVER_HTTPS_REDIRECT_HOST","text":"

Canonical hostname or IP of the HTTPS (HTTPS/2) server. It depends on \"https-redirect\" to be enabled. Default localhost.

"},{"location":"configuration/environment-variables/#server_https_redirect_from_port","title":"SERVER_HTTPS_REDIRECT_FROM_PORT","text":"

HTTP host port where the redirect server will listen for requests to redirect them to HTTPS. It depends on \"https-redirect\" to be enabled. Default 80.

"},{"location":"configuration/environment-variables/#server_https_redirect_from_hosts","title":"SERVER_HTTPS_REDIRECT_FROM_HOSTS","text":"

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. Default localhost.

"},{"location":"configuration/environment-variables/#server_cors_allow_origins","title":"SERVER_CORS_ALLOW_ORIGINS","text":"

Specify an optional CORS list of allowed origin hosts separated by commas. Host ports or protocols aren't being checked. Use an asterisk (*) to allow any host. Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_cors_allow_headers","title":"SERVER_CORS_ALLOW_HEADERS","text":"

Specify an optional CORS list of allowed HTTP headers separated by commas. It requires SERVER_CORS_ALLOW_ORIGINS to be used along with. Default origin, content-type.

"},{"location":"configuration/environment-variables/#server_cors_expose_headers","title":"SERVER_CORS_EXPOSE_HEADERS","text":"

Specify an optional CORS list of exposed HTTP headers separated by commas. It requires SERVER_CORS_ALLOW_ORIGINS to be used along with. Default origin, content-type.

"},{"location":"configuration/environment-variables/#server_compression","title":"SERVER_COMPRESSION","text":"

Gzip, Deflate, Brotli or zlib compression on demand determined by the Accept-Encoding header and applied to text-based web file types only. See ad-hoc mime-type list. Default true (enabled).

"},{"location":"configuration/environment-variables/#server_compression_level","title":"SERVER_COMPRESSION_LEVEL","text":"

Supported values are fastest (fast compression but larger resulting files), best (smallest file size but potentially slow) and default (algorithm-specific balanced compression level). Default is default.

"},{"location":"configuration/environment-variables/#server_compression_static","title":"SERVER_COMPRESSION_STATIC","text":"

Look up the pre-compressed file variant (.gz, .br or .zst) on the disk of a requested file and serve it directly if available. Default false (disabled). The compression type is determined by the Accept-Encoding header.

"},{"location":"configuration/environment-variables/#server_directory_listing","title":"SERVER_DIRECTORY_LISTING","text":"

Enable directory listing for all requests ending with the slash character (\u2018/\u2019). Default false (disabled).

"},{"location":"configuration/environment-variables/#server_directory_listing_order","title":"SERVER_DIRECTORY_LISTING_ORDER","text":"

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).

"},{"location":"configuration/environment-variables/#server_directory_listing_format","title":"SERVER_DIRECTORY_LISTING_FORMAT","text":"

Specify a content format for the directory listing entries. Formats supported: html or json. Default html.

"},{"location":"configuration/environment-variables/#server_directory_listing_download","title":"SERVER_DIRECTORY_LISTING_DOWNLOAD","text":"

Specify list of enabled format(s) for directory download. Format supported: targz. Default to empty list (disabled).

"},{"location":"configuration/environment-variables/#server_security_headers","title":"SERVER_SECURITY_HEADERS","text":"

Enable security headers by default when the HTTP/2 feature is activated. Headers included: Strict-Transport-Security: max-age=63072000; includeSubDomains; preload (2 years max-age), X-Frame-Options: DENY and Content-Security-Policy: frame-ancestors 'self'. Default false (disabled).

"},{"location":"configuration/environment-variables/#server_cache_control_headers","title":"SERVER_CACHE_CONTROL_HEADERS","text":"

Enable cache control headers for incoming requests based on a set of file types. The file type list can be found in src/control_headers.rs file. Default true (enabled).

"},{"location":"configuration/environment-variables/#server_basic_auth","title":"SERVER_BASIC_AUTH","text":"

It provides The \"Basic\" HTTP Authentication Scheme using credentials as user-id:password pairs, encoded using Base64. Password must be encoded using the BCrypt password-hashing function. Default empty (disabled).

"},{"location":"configuration/environment-variables/#server_redirect_trailing_slash","title":"SERVER_REDIRECT_TRAILING_SLASH","text":"

Check for a trailing slash in the requested directory URI and redirect permanent (308) to the same path with a trailing slash suffix if it is missing. Default true (enabled).

"},{"location":"configuration/environment-variables/#server_ignore_hidden_files","title":"SERVER_IGNORE_HIDDEN_FILES","text":"

Ignore hidden files/directories (dotfiles), preventing them from being served and being included in auto HTML index pages (directory listing).

"},{"location":"configuration/environment-variables/#server_disable_symlinks","title":"SERVER_DISABLE_SYMLINKS","text":"

Prevent following files or directories if any path name component is a symbolic link.

"},{"location":"configuration/environment-variables/#server_health","title":"SERVER_HEALTH","text":"

Activate the health endpoint.

"},{"location":"configuration/environment-variables/#server_accept_markdown","title":"SERVER_ACCEPT_MARKDOWN","text":"

Enable markdown content negotiation. When a client sends Accept: text/markdown header, the server will serve markdown files (.md or .html.md) if available. See Markdown Content Negotiation for details. Default false.

"},{"location":"configuration/environment-variables/#server_index_files","title":"SERVER_INDEX_FILES","text":"

List of files that will be used as an index for requests ending with the slash character (\u2018/\u2019). Files are checked in the specified order. Default index.html.

"},{"location":"configuration/environment-variables/#server_maintenance_mode","title":"SERVER_MAINTENANCE_MODE","text":"

Enable the server's maintenance mode functionality.

"},{"location":"configuration/environment-variables/#server_maintenance_mode_status","title":"SERVER_MAINTENANCE_MODE_STATUS","text":"

Provide a custom HTTP status code when entering into maintenance mode. Default 503.

"},{"location":"configuration/environment-variables/#server_maintenance_mode_file","title":"SERVER_MAINTENANCE_MODE_FILE","text":"

Provide a custom maintenance mode HTML file. If not provided then a generic message will be displayed.

"},{"location":"configuration/environment-variables/#windows","title":"Windows","text":"

The following options and commands are Windows platform-specific.

"},{"location":"configuration/environment-variables/#server_windows_service","title":"SERVER_WINDOWS_SERVICE","text":"

Run the web server in a Windows Service context. See more details.

"},{"location":"features/basic-authentication/","title":"Basic HTTP Authentication","text":"

SWS provides 'Basic' HTTP Authentication Scheme 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 env.

The format to use is the following:

username:encrypted_password

Both are separated by a : (punctuation mark) character.

Password Encryption

Only the password must be encoded using the BCrypt password-hashing function.

As an example, we will use the Apache htpasswd tool to generate the username:encrypted_password pair.

htpasswd -nBC10 \"username\"\n# New password: \n# Re-type new password: \n# username:$2y$10$8phm28BB4YpKPDjOpdTT8eUcfVDw0xc85VZPxg2zae1GR8EQqus3i\n

Password Security Advice

The password verification happens at runtime but its verification speed depends on the computing time cost of bcrypt algorithm used.

For example, the htpasswd tool supports a -C argument to adjust the bcrypt's computing time.

Using a higher value is more secure but slower. The default value is 5 and the possible values are ranging from 4 to 17.

Docker Compose Advice

If you are using SERVER_BASIC_AUTH env via a docker-compose.yml file don't forget to replace the single $ (dollar sign) with a $$ (double-dollar sign) if you want those individual $ dollar signs in your configuration to be treated by Docker as literals. More details in the Docker Compose file: variable substitution page.

Finally, assign the credentials and run the server.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --basic-auth 'username:$2y$10$8phm28BB4YpKPDjOpdTT8eUcfVDw0xc85VZPxg2zae1GR8EQqus3i'\n
"},{"location":"features/blocking-threads/","title":"Blocking Threads Customization","text":"

SWS allows limiting the number of blocking threads powered by the Tokio runtime.

This feature can be controlled by the numeric -b, --max-blocking-threads option or the equivalent SERVER_MAX_BLOCKING_THREADS env.

WebAssembly

We use 20 in Wasm by default and 512 in native environments (Tokio's default). See Tokio max_blocking_threads API for more details.

Below is an example.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --max-blocking-threads 20\n
"},{"location":"features/cache-control-headers/","title":"Cache-Control Headers","text":"

SWS provides support for arbitrary 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 env.

Customize HTTP headers

If you want to customize HTTP headers on demand then have a look at the Custom HTTP Headers section.

"},{"location":"features/cache-control-headers/#cache-control-max-age","title":"Cache-Control Max-Age","text":"

Control headers are applied only to the following file types with the corresponding max-age values.

"},{"location":"features/cache-control-headers/#one-day","title":"One day","text":"

A max-age of one day duration is used by default.

Note

One-day max-age for example includes html and other file types.

"},{"location":"features/cache-control-headers/#one-hour","title":"One hour","text":"

A max-age of one hour is applied only to the following file types.

atom, json, rss, xml\n
"},{"location":"features/cache-control-headers/#one-year","title":"One year","text":"

A max-age of one year is applied only to the following file types.

avif, bmp, bz2, css, doc, gif, gz, htc, ico, jpeg, jpg, js, jxl, map, mjs, mp3, mp4, ogg, ogv, pdf, png, rar, rtf, tar, tgz, wav, weba, webm, webp, woff, woff2, zip\n

Below is an example of how to enable the feature.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --cache-control-headers true\n
"},{"location":"features/compression-static/","title":"Pre-compressed files serving","text":"

SWS provides support to serve pre-compressed Gzip, Brotli and Zstandard (zstd) files directly from the disk.

SWS can look up existing pre-compressed file variants (.gz, .br or .zst) on disk and serve them directly.

The feature is disabled by default and can be controlled by the boolean --compression-static option or the equivalent SERVER_COMPRESSION_STATIC env.

When the compression-static option is enabled and the pre-compressed file is found on the file system then it's served directly. Otherwise, if the pre-compressed file is not found then SWS just continues the normal workflow (trying to serve the original file requested instead). Additionally, if for example the compression option was also enabled then the requested file can be compressed on the fly right after.

Compressed file type

The pre-compressed file type is determined by the Accept-Encoding header value.

Here is an example:

static-web-server -p=8787 -d=/var/www --compression-static=true -g=trace\n

Below are some relevant log entries to show how the feature works.

2022-09-22T21:30:12.904102Z  INFO static_web_server::handler: incoming request: method=GET uri=/downloads/Capture5.png\n2022-09-22T21:30:12.904218Z TRACE static_web_server::static_files: dir: base=\"/var/www\", route=\"downloads/Capture5.png\"\n2022-09-22T21:30:12.904295Z TRACE static_web_server::compression_static: preparing pre-compressed file path variant of /var/www/downloads/Capture5.png\n2022-09-22T21:30:12.904509Z TRACE static_web_server::compression_static: getting metadata for pre-compressed file variant /var/www/downloads/Capture5.png.gz\n2022-09-22T21:30:12.904746Z TRACE hyper::proto::h1::conn: flushed({role=server}): State { reading: KeepAlive, writing: Init, keep_alive: Busy }\n2022-09-22T21:30:12.904932Z TRACE static_web_server::static_files: file found: \"/var/www/downloads/Capture5.png.gz\"\n2022-09-22T21:30:12.904983Z TRACE static_web_server::compression_static: pre-compressed file variant found, serving it directly\n2022-09-22T21:30:12.905095Z TRACE hyper::proto::h1::conn: flushed({role=server}): State { reading: KeepAlive, writing: Init, keep_alive: Busy }\n2022-09-22T21:30:12.905836Z TRACE encode_headers: hyper::proto::h1::role: Server::encode status=200, body=Some(Unknown), req_method=Some(GET)\n2022-09-22T21:30:12.905965Z TRACE encode_headers: hyper::proto::h1::role: close time.busy=138\u00b5s time.idle=35.4\u00b5s\n2022-09-22T21:30:12.906236Z DEBUG hyper::proto::h1::io: flushed 242 bytes\n
"},{"location":"features/compression/","title":"Compression","text":"

SWS provides Gzip, Deflate, Brotli and Zstandard (zstd) compression of HTTP responses.

This feature is enabled by default and can be controlled by the boolean -x, --compression option or the equivalent SERVER_COMPRESSION env.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --compression true\n
"},{"location":"features/compression/#choice-of-compression-algorithm","title":"Choice of compression algorithm","text":"

The compression algorithm is determined by the Accept-Encoding header and the compression support built into SWS. By default SWS builds with support for Gzip, Deflate, Brotli and Zstandard algorithms.

"},{"location":"features/compression/#mime-types-compressed","title":"MIME types compressed","text":"

Compression is only applied to files with the MIME types listed below, indicating text and similarly well compressing formats. The asterisk * is a placeholder indicating an arbitrary MIME type part.

text/*\n*+xml\n*+json\napplication/rtf\napplication/javascript\napplication/json\napplication/xml\nfont/ttf\napplication/font-sfnt\napplication/vnd.ms-fontobject\napplication/wasm\n
"},{"location":"features/compression/#compression-level","title":"Compression level","text":"

SWS allows selecting the compression level via --compression-level command line option or the equivalent 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.

"},{"location":"features/cors/","title":"CORS","text":"

SWS provides optional Cross-Origin Resource Sharing (CORS) support.

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 env.

Below is an example of how to enable CORS.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --cors-allow-origins \"https://domain.com\"\n\n    # Or use an asterisk to allow any host\n    # --cors-allow-origins \"*\"\n
"},{"location":"features/cors/#allowed-headers","title":"Allowed headers","text":"

The server also supports a list of CORS allowed 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 env.

Tips

Below is an example of how to CORS.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --cors-allow-origins \"https://domain.com\" \\\n    --cors-allow-headers \"origin, content-type, x-requested-with\"\n
"},{"location":"features/cors/#exposed-headers","title":"Exposed headers","text":"

The server also supports a list of CORS-exposed headers to scripts 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 env.

Tips

Below is an example of how to CORS.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --cors-allow-origins \"https://domain.com\" \\\n    --cors-expose-headers \"origin, content-type, x-requested-with\"\n
"},{"location":"features/custom-http-headers/","title":"Custom HTTP Headers","text":"

SWS allows customizing the server HTTP Response headers on demand.

"},{"location":"features/custom-http-headers/#structure","title":"Structure","text":"

The Server HTTP response headers should be defined mainly as an Array of Tables.

Each table entry should have two key/value pairs:

A particular set of HTTP headers can only be applied when a source matches against the request URI.

Custom HTTP headers take precedence over existing ones

Whatever custom HTTP header could replace an existing one if it was previously defined (e.g. server default headers) and matches its source.

The header's order is important because determines its precedence.

Example: If the feature --cache-control-headers=true is enabled but also a custom cache-control header was defined then the custom header will have priority.

"},{"location":"features/custom-http-headers/#source","title":"Source","text":"

The source is a Glob pattern that should match against the URI that is requesting a resource file.

"},{"location":"features/custom-http-headers/#headers","title":"Headers","text":"

A set of valid plain HTTP headers to be applied.

"},{"location":"features/custom-http-headers/#examples","title":"Examples","text":"

Below are some examples of how to customize server HTTP headers in three variants.

"},{"location":"features/custom-http-headers/#one-line-version","title":"One-line version","text":"
[advanced]\n\n[[advanced.headers]]\nsource = \"**/*.{js,css}\"\nheaders = { Access-Control-Allow-Origin = \"*\" }\n
"},{"location":"features/custom-http-headers/#multiline-version","title":"Multiline version","text":"
[advanced]\n\n[[advanced.headers]]\nsource = \"*.html\"\n[advanced.headers.headers]\nCache-Control = \"public, max-age=36000\"\nContent-Security-Policy = \"frame-ancestors 'self'\"\nStrict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n
"},{"location":"features/custom-http-headers/#multiline-version-with-explicit-header-key-dotted","title":"Multiline version with explicit header key (dotted)","text":"
[advanced]\n\n[[advanced.headers]]\nsource = \"**/*.{jpg,jpeg,png,ico,gif}\"\nheaders.Strict-Transport-Security = \"max-age=63072000; includeSubDomains; preload\"\n
"},{"location":"features/directory-listing/","title":"Directory Listing","text":"

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 env.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --directory-listing true\n

And here is an example of how the directory listing looks like.

"},{"location":"features/directory-listing/#relative-paths-for-entries","title":"Relative paths for entries","text":"

SWS uses relative paths for the directory listing entries (file or directory) and is used regardless of the redirect trailing slash feature.

However, when the \"redirect trailing slash\" feature is disabled and a directory request URI doesn't contain a trailing slash then the entries will contain the path parent-dir/entry-name as the link value. Otherwise, just an entry-name link value is used (default behavior).

Note also that in both cases, SWS will append a trailing slash to the entry if is a directory.

"},{"location":"features/directory-listing/#sorting","title":"Sorting","text":"

Sorting by Name, Last modified and Size is enabled as clickable columns when the directory listing is activated via the --directory-listing=true option.

You can also use the sort query parameter to sort manually by certain attributes from URI. E.g https://localhost/?sort=5.

"},{"location":"features/directory-listing/#sorting-by-default","title":"Sorting by default","text":"

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 env.

To do so you have to pass a code sorting number. E.g --directory-listing-order=2.

"},{"location":"features/directory-listing/#code-numbers-for-sorting","title":"Code numbers for sorting","text":"

Below are the possible number code values for sorting or ordering which are grouped by attribute.

"},{"location":"features/directory-listing/#name","title":"Name","text":""},{"location":"features/directory-listing/#last-modified","title":"Last modified","text":""},{"location":"features/directory-listing/#size","title":"Size","text":""},{"location":"features/directory-listing/#default","title":"Default","text":"

Tips

Example:

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --directory-listing true \\\n    # E.g Sorting file/dir names in descending order\n    --directory-listing-order 1\n
"},{"location":"features/directory-listing/#output-format","title":"Output format","text":"

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 env.

Tips

"},{"location":"features/directory-listing/#html-format","title":"HTML format","text":"

This is the default format when --directory-listing is enabled.

"},{"location":"features/directory-listing/#json-format","title":"JSON format","text":"

The JSON format used is shown below for directories and files. Note that the size attribute is only available for files and the mtime value is UTC-based.

[\n  {\n    \"name\": \"my-directory\",\n    \"type\": \"directory\",\n    \"mtime\": \"2022-10-07T00:53:50Z\"\n  },\n  {\n    \"name\": \"my_file.tar.gz\",\n    \"type\": \"file\",\n    \"mtime\": \"2022-09-27T22:44:34Z\",\n    \"size\": 332\n  }\n]\n

Here is an example of serving the directory listing in JSON format.

static-web-server \\\n    -p=8787 -d=tests/fixtures/public -g=trace \\\n    --directory-listing=true \\\n    --directory-listing-format=\"json\"\n

And below is a client request example to illustrate how the feature works.

curl -iH \"content-type: application/json\" http://localhost:8787\n# HTTP/1.1 200 OK\n# content-type: application/json\n# content-length: 163\n# cache-control: max-age=86400\n# date: Tue, 11 Oct 2022 23:24:55 GMT\n\n# [{\"name\":\"sp\u00e9cial direct\u00f6ry\",\"type\":\"directory\",\"mtime\":\"2022-10-07T00:53:50Z\"},{\"name\":\"index.html.gz\",\"type\":\"file\",\"mtime\":\"2022-09-27T22:44:34Z\",\"size\":332}]\u23ce\n
"},{"location":"features/directory-listing/#directory-download","title":"Directory Download","text":"

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 env. Currently, targz format is supported.

static-web-server \\\n    --directory-listing=true \\\n    --directory-listing-download=targz\n

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.

"},{"location":"features/disable-symlinks/","title":"Disable Symlinks","text":"

SWS does follow symlinks by default. However, it's possible to disable all symlinks (deny access) by preventing to following files or directories if any path name component is a symbolic link. This applies to direct requests (URL) or those using the directory listing.

As a result, SWS will respond with a 403 Forbidden status if a symlink is requested or it won't be shown in the directory listing if enabled.

This feature is disabled by default and can be controlled by the boolean --disable-symlinks option or the equivalent SERVER_DISABLE_SYMLINKS env.

Here is an example of how to disable symlinks:

static-web-server \\\n    -p=8787 -d=./public -g=trace \\\n    --directory-listing \\\n    --disable-symlinks\n
"},{"location":"features/docker/","title":"Docker","text":"

SWS has first-class Docker support.

It is provided in three Docker image variants such as Scratch, Alpine and Debian images.

All images are available on Docker Hub and GitHub Container Registry.

"},{"location":"features/docker/#osarch","title":"OS/Arch","text":"

All Docker images are Multi-Arch and the following operating systems and architectures are supported.

Scratch and Alpine images use statically-linked binaries

Scratch and Alpine based Docker images use a statically-linked binary that is portable, performant and dependency-free thanks to musl libc, keeping containers as lean as possible.

Debian images use dynamically-linked binaries

Debian based Docker images use SWS dynamically-linked binaries, making containers highly optimized, performant and resource-efficient.

"},{"location":"features/docker/#rootless","title":"Rootless","text":"

The Debian and Alpine Docker images are rootless by default using a dedicated sws user and group. This reduces the attack surface and improves security.

Remember

Users can still run the containers as root if they explicitly set the user to root when running the container, e.g., using the --user root flag with docker run.

The static-web-server binary and all files under /home/sws (home directory) are owned by the non-root sws user and group.

For convenience, those paths are also available:

The current working directory is the home directory by default.

"},{"location":"features/docker/#run-a-container","title":"Run a container","text":"

To give the server a quick try just run the following commands.

Tips

To run SWS, there are several Docker image variants that you can use.

"},{"location":"features/docker/#scratch-just-the-binary","title":"Scratch (just the binary)","text":"
docker run --rm -it -p 8787:80 joseluisq/static-web-server:2 -g info\n# or\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:2 -g info\n
"},{"location":"features/docker/#alpine","title":"Alpine","text":"
docker run --rm -it -p 8787:80 joseluisq/static-web-server:2-alpine -g info\n# or\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:2-alpine -g info\n
"},{"location":"features/docker/#debian","title":"Debian","text":"
docker run --rm -it -p 8787:80 joseluisq/static-web-server:2-debian -g info\n# or\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:2-debian -g info\n
"},{"location":"features/docker/#development","title":"Development","text":"

Additionally, we publish development Docker images based on master branch changes.

# Scratch (just the binary)\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:devel -g info\n# Debian\ndocker run --rm -it -p 8787:80 ghcr.io/static-web-server/static-web-server:devel-debian -g info\n
"},{"location":"features/docker/#dockerfile","title":"Dockerfile","text":"

SWS Docker images can be extended as needed.

Extending the Scratch Docker image (just the binary)

FROM joseluisq/static-web-server:2\n# or\nFROM ghcr.io/static-web-server/static-web-server:2\n# do stuff...\n

Or the Alpine

FROM joseluisq/static-web-server:2-alpine\n# or\nFROM ghcr.io/static-web-server/static-web-server:2-alpine\n# do stuff...\n

Or the Debian

FROM joseluisq/static-web-server:2-debian\n# or\nFROM ghcr.io/static-web-server/static-web-server:2-debian\n# do stuff...\n
"},{"location":"features/docker/#docker-compose","title":"Docker Compose","text":"

Example using Docker Compose.

version: \"3.3\"\n\nservices:\n  website:\n    image: joseluisq/static-web-server:2-alpine\n    container_name: \"website\"\n    ports:\n      - 80:80\n    restart: unless-stopped\n    environment:\n      # Note: those envs are customizable but also optional\n      - SERVER_ROOT=/var/public\n      - SERVER_CONFIG_FILE=/etc/sws.toml\n    volumes:\n      - ./public:/var/public\n      - ./sws.toml:/etc/sws.toml\n
"},{"location":"features/docker/#traefik-proxy","title":"Traefik Proxy","text":"

Example using Docker Swarm and Traefik Proxy.

  1. Create an external traefik_net Docker attachable network for Traefik:
    • docker network create --driver=overlay --attachable traefik_net
  2. Map a host directory like /var/www/website to the service container or create an external website_data Docker volume if you prefer:
    • docker volume create website_data
version: \"3.3\"\n\nservices:\n  traefik:\n    image: \"traefik:v2.11\"\n    command:\n      #- \"--log.level=DEBUG\"\n      - \"--api.insecure=true\"\n      - \"--providers.docker=true\"\n      - \"--providers.docker.exposedbydefault=false\"\n      - \"--entrypoints.web.address=:80\"\n    ports:\n      - \"80:80\"\n      - \"8080:8080\"\n    volumes:\n      - \"/var/run/docker.sock:/var/run/docker.sock:ro\"\n\n  website:\n    image: joseluisq/static-web-server:2\n    environment:\n      # Note: those envs are customizable but also optional\n      - SERVER_ROOT=/public\n    volumes:\n      - /var/www/website:/public\n      # Or use an existing Docker volume\n      # - website_data:/public\n    labels:\n      - \"traefik.enable=true\"\n      - \"traefik.docker.network=traefik_net\"\n      - \"traefik.http.routers.website.entrypoints=web\"\n      - \"traefik.http.routers.website.rule=Host(`website.localhost`)\"\n      - \"traefik.http.routers.website.priority=1\"\n      - \"traefik.http.services.website.loadbalancer.server.port=80\"\n    networks:\n      - traefik_net\n\n# volumes:\n#   website_data:\n#     external: true\n\nnetworks:\n  traefik_net:\n    external: true\n
"},{"location":"features/docker/#kubernetes","title":"Kubernetes","text":"

Example using Kubernetes pod with liveness probe.

apiVersion: v1\nkind: Pod\nmetadata:\n  name: website\nspec:\n  containers:\n    - name: sws\n      image: ghcr.io/static-web-server/static-web-server\n      command:\n        - static-web-server\n        - --root=/public\n        - --health\n      ports:\n      - containerPort: 80\n      livenessProbe:\n        httpGet:\n          path: /health\n          port: http\n
"},{"location":"features/docker/#truecharts","title":"TrueCharts","text":"

The TrueCharts Community also provides a ready-to-use Static Web Server Helm-Chart that you can easily deploy in your Kubernetes.

"},{"location":"features/error-pages/","title":"Error Pages","text":"

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) or the --page50x (SERVER_ERROR_PAGE_50X) arguments.

Tip

Either --page404 and --page50x have defaults (optional values) so they can be specified or omitted as required.

Below is an example of how to customize those HTML pages.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --page404 ./my-page-404.html \\\n    --page50x ./my-page-50x.html\n
"},{"location":"features/error-pages/#fallback-page-for-use-with-client-routers","title":"Fallback Page for use with Client Routers","text":"

It is possible to provide a HTML file to be used as fallback page when GET request paths dont exist. The fallback page will be served with a 200 status code, useful when using client routers like React Router or similar. If the path is not specified or simply doesn't exist then this feature will not be activated.

The fallback page path is not relative to the root

The fallback page is an independent path, so provide a valid relative or absolute path.

It can be set with the SERVER_FALLBACK_PAGE environment variable or with the CLI argument --page-fallback.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --page-fallback ./my-public-dir/index.html\n
"},{"location":"features/file-descriptor-socket-passing/","title":"File Descriptor Socket Passing","text":"

SWS provides the ability to accept a socket listener as a file descriptor for use in sandboxing and on-demand applications via systemd (Linux), launchd (Macos) or similar.

Tip

The Socket Activation model is an alternative to TCP port binding.

Socket activation is supported by the -f, --fd option or the equivalent 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.

"},{"location":"features/file-descriptor-socket-passing/#systemd","title":"Systemd","text":"

If you're using systemd on Linux, there is a fully working example in the SWS Git repository under the ./systemd directory.

"},{"location":"features/file-descriptor-socket-passing/#service-example","title":"Service example","text":"

Below is a systemd service example. Follow the steps to create an SWS service using HTTP2 (static-web-server.service). The service will bind SWS to a TCP 443 privileged port without running the server as root.

If you want to change the server port used by the service, edit the value of ListenStream in the static-web-server.socket file.

The template files can be found in ./systemd directory.

# 1. Copy environment file template\n#    Use an environment variable file, add/modify the values if necessary and\n#    assign the proper owner/permissions to the environment variable file.\n#    TIP: you could skip this step and use a config file if you prefer.\ncp systemd/etc_default_static-web-server /etc/default/static-web-server\n\n# TIP: For example, you could create a `nologin` user with specific privileges.\n\n# 2. Copy service file templates\ncp systemd/static-web-server.s* /etc/systemd/system/\n\n# 3. Make sure that the `EnvironmentFile` and `ExecStart` values\n#    of the service match to the corresponding file paths in the `static-web-server.service` file.\n#    TIP: Use absolute paths. \n# EnvironmentFile=/etc/default/static-web-server\n# ExecStart=/usr/local/bin/static-web-server --fd 0\n\n# 4. Make sure to change this value with an existing user editing the `static-web-server.service` file.\n# SupplementaryGroups=www-data\n\n# 5. Create/reuse a directory for placing the certificate and private key.\n#    TIP: this is an example, you can create/reuse your own dirs.\nsudo mkdir /etc/static-web-server\n\n# 6. For example purposes, copy the testing cert/key files.\n#    TIP: Use your own cert/key files instead.\nsudo cp tests/tls/local.dev_cert.ecc.pem /etc/static-web-server/\nsudo cp tests/tls/local.dev_key.ecc.pem /etc/static-web-server/\n\n# 7. Create/reuse a root directory (example only)\nsudo mkdir -p /var/www/html\nsudo sh -c 'echo \"<h1>Static Web Server is running!</h1>\" > /var/www/html/index.html'\n\n# 8. Reload systemd manager configuration\nsudo systemctl daemon-reload\n\n# 9. Start the SWS service\nsudo systemctl start static-web-server.service\n\n# 10. Show the status of the SWS service running\nsudo systemctl status static-web-server.service\n\n# 11. Enable the service to start automatically at boot (optional)\nsudo systemctl enable static-web-server.service\n\n# 12. Analyze and debug the SWS service security\nsudo systemd-analyze security static-web-server.service\n#    If the service was successfully created then you should get something like:\n#    \u2192 Overall exposure level for static-web-server.service: 0.6 SAFE \ud83d\ude00\n
"},{"location":"features/file-descriptor-socket-passing/#testing","title":"Testing","text":"

Alternatively, the lightweight systemfd utility may be useful, especially for testing purposes.

For example, using systemfd utility as follows:

sudo systemfd --no-pid -s http::8091 -- path/to/static-web-server --fd 0\n

Or if you want to test using an environment variables file then you could use Enve.

sudo enve -f /path/to/environment.env systemfd --no-pid -s http::443 -- path/to/static-web-server --fd 0\n
"},{"location":"features/graceful-shutdown/","title":"Graceful Shutdown","text":"

SWS can terminate gracefully in what is known as a graceful shutdown process.

It means that when a SIGTERM termination signal is caught the server will stop receiving more requests immediately but in turn, it will continue processing all existing requests until they are completed (or closed).

Tips

"},{"location":"features/graceful-shutdown/#grace-period","title":"Grace Period","text":"

Sometimes one wants to control the graceful shutdown process for different reasons. For example during Kubernetes rollouts.

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 env.

Tip

The maximum grace period value is 255 seconds (4.25 min). The default value is 0 (no delay).

Here is an example of delaying the graceful shutdown process by 10 seconds after a SIGTERM.

static-web-server -p 8787 -d ./public/ -g trace --grace-period 10\n
"},{"location":"features/health-endpoint/","title":"Health endpoint","text":"

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.

The HTTP methods supported are GET and HEAD.

This feature is disabled by default and can be controlled by the boolean --health option or the equivalent SERVER_HEALTH env.

"},{"location":"features/health-endpoint/#usage-with-kubernetes-liveness-probe","title":"Usage with Kubernetes liveness probe","text":"

The health endpoint is well suited for the Kubernetes liveness probe:

apiVersion: v1\nkind: Pod\nmetadata:\n  name: frontend\nspec:\n  containers:\n    - name: sws\n      image: frontend:1.0.0\n      command:\n        - static-web-server\n        - --root=/public\n        - --log-level=info\n        - --health\n      ports:\n      - containerPort: 80\n        name: http\n      livenessProbe:\n        httpGet:\n          path: /health\n          port: http\n
"},{"location":"features/http-https-redirect/","title":"HTTP to HTTPS redirect","text":"

SWS provides support for redirecting HTTP requests to HTTPS via a 301 Moved Permanently 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 env.

HTTP/2 required

HTTPS redirect requires the HTTP/2 feature to be activated.

"},{"location":"features/http-https-redirect/#https-redirect","title":"HTTPS redirect","text":"

The boolean --https-redirect is the main option and controls the whole HTTPS redirect feature. If true then will tell SWS to redirect all requests with scheme http to https for the current server instance with a 301 Moved Permanently redirect status response code. This option depends on http2 to be enabled.

"},{"location":"features/http-https-redirect/#https-redirect-host","title":"HTTPS redirect host","text":"

The string --https-redirect-host option represents the canonical hostname or IP of the HTTPS (HTTPS/2) server. This is usually associated with the --host option, however here this value will be used as the destination for the redirected requests. It depends on \"https-redirect\" option to be enabled. The default is localhost.

"},{"location":"features/http-https-redirect/#https-redirect-from-port","title":"HTTPS redirect from port","text":"

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.

"},{"location":"features/http-https-redirect/#https-redirect-from-hosts","title":"HTTPS redirect from hosts","text":"

The string --https-redirect-from-hosts option represents a list of hostnames or IPs allowed to redirect from using comma-separated values. Incoming HTTP requests must contain the HTTP Host header and match against this list. It depends on \"https-redirect\" option to be enabled. The default value is localhost.

Tip: define hostnames/IPs to redirect from for increasing security

"},{"location":"features/http-https-redirect/#example","title":"Example","text":"

Below is an example of the feature.

static-web-server -p 4433 -d public/ -g trace \\\n    # HTTP/2 + TLS options\n    --http2=true \\\n    --http2-tls-cert=tests/tls/local.dev_cert.ecc.pem \\\n    --http2-tls-key=tests/tls/local.dev_key.ecc.pem \\\n\\\n    # HTTPS redirect options\n    --https-redirect=true \\\n    --https-redirect-host=\"localhost\" \\\n    --https-redirect-from-port=80 \\\n    --https-redirect-from-hosts=\"localhost\"\n    # or using multiple hostnames/IPs:\n    # --https-redirect-from-hosts = \"localhost,127.0.0.1\"\n

After running the server, the logs should look as follows.

.......\n2023-06-01T22:30:17.555338Z  INFO static_web_server::server: http to https redirect: enabled=true\n2023-06-01T22:30:17.555349Z  INFO static_web_server::server: http to https redirect host: localhost\n2023-06-01T22:30:17.555359Z  INFO static_web_server::server: http to https redirect from port: 80\n2023-06-01T22:30:17.555368Z  INFO static_web_server::server: http to https redirect from hosts: localhost\n2023-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\u00b5s\n2023-06-01T22:30:17.557547Z  INFO static_web_server::server: http2 server is listening on https://[::]:4433\n2023-06-01T22:30:17.557583Z  INFO Server::start_server{addr=[::]:80 threads=8}: static_web_server::server: close time.busy=0.00ns time.idle=1.92\u00b5s\n2023-06-01T22:30:17.557596Z  INFO static_web_server::server: http1 redirect server is listening on http://[::]:80\n2023-06-01T22:30:17.557768Z  INFO static_web_server::server: press ctrl+c to shut down the servers\n
"},{"location":"features/http-methods/","title":"HTTP Methods Supported","text":"

SWS only supports GET, HEAD and OPTIONS HTTP methods.

"},{"location":"features/http-methods/#options-method","title":"OPTIONS Method","text":""},{"location":"features/http-methods/#identifying-allowed-request-methods","title":"Identifying allowed request methods","text":"

The HTTP OPTIONS method can be used to send a request to check for permitted communication options for either a given URL or server.

Example using an HTTP client.

curl -I -X OPTIONS http://localhost:8787/assets/main.js\n# HTTP/1.1 204 No Content\n# allow: OPTIONS, HEAD, GET\n# accept-ranges: bytes\n# cache-control: max-age=31536000\n# date: Thu, 10 Mar 2022 21:26:01 GMT\n
"},{"location":"features/http-methods/#preflight-requests-in-cors","title":"Preflight requests in CORS","text":"

The HTTP OPTIONS method can also be used to send a request asking if it is acceptable to send requests to the server and if it is aware of using specific methods and headers.

Tip

If an Access-Control-Request-Method or Access-Control-Request-Headers value is not allowed then the server replies with a 403 Forbidden HTTP error. See CORS feature for more details.

Example using an HTTP client.

curl http://localhost:8787/assets/main.js \\\n    -I -X OPTIONS \\\n    -H \"Access-Control-Request-Method: HEAD\" \\\n    -H \"Access-Control-Request-Headers: content-type\" \\\n    -H \"Origin: http://localhost:8787\"\n# HTTP/1.1 204 No Content\n# access-control-allow-origin: http://localhost:8787\n# accept-ranges: bytes\n# access-control-allow-headers: content-type, origin\n# access-control-allow-methods: GET, OPTIONS, HEAD\n# cache-control: max-age=31536000\n# date: Thu, 10 Mar 2022 21:45:55 GMT\n
"},{"location":"features/http1/","title":"HTTP/1","text":"

The HTTP/1 is the protocol by default and can be used by specifying a host address via the -a, --host (SERVER_HOST) argument, the port of the host via -p, --port (SERVER_PORT) and the directory of the static files using the -d, --root (SERVER_ROOT) argument.

Tips

Below is an example of how to run the server using HTTP/1.

static-web-server \\\n    --host 127.0.0.1 \\\n    --port 8787 \\\n    --root ./my-public-dir\n
"},{"location":"features/http2-tls/","title":"HTTP/2 and TLS","text":"

SWS provides HTTP/2 protocol and TLS support.

This feature is disabled by default and can be activated via the boolean -t, --http2 option as well as string arguments --http2-tls-cert (TLS certificate file path) and --http2-tls-key (private key file path).

"},{"location":"features/http2-tls/#safe-tls-defaults","title":"Safe TLS defaults","text":"

SWS comes with safe TLS defaults for underlying cryptography.

These defaults are safe and useful for most use cases. See Rustls safe defaults for more details.

"},{"location":"features/http2-tls/#private-key-file-formats","title":"Private key file formats","text":"

Only the following private key file formats are supported:

"},{"location":"features/http2-tls/#example","title":"Example","text":"

Tips

static-web-server \\\n    --host 127.0.0.1 \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --http2 true \\\n    --http2-tls-cert ./my-tls.cert \\\n    --http2-tls-key ./my-tls.key\n
"},{"location":"features/ignore-files/","title":"Ignore files","text":"

SWS provides some options to ignore files or directories from being served and displayed if the directory listing is enabled.

"},{"location":"features/ignore-files/#ignore-hidden-files-dotfiles","title":"Ignore hidden files (dotfiles)","text":"

SWS doesn't ignore dotfiles (hidden files) by default. However, it's possible to ignore those files as shown below. As a result, SWS will respond with a 404 Not Found status.

This feature is disabled by default and can be controlled by the boolean --ignore-hidden-files option or the equivalent SERVER_IGNORE_HIDDEN_FILES env.

Here is an example of how to ignore hidden files:

static-web-server \\\n    -p=8787 -d=tests/fixtures/public -g=trace \\\n    --directory-listing=true \\\n    --ignore-hidden-files true\n
"},{"location":"features/logging/","title":"Logging","text":"

SWS provides logging support by just specifying a log level in lower case. The values allowed are error, warn, info, debug and trace. The default value is error.

This feature is enabled by default and can be controlled by the string -g, --log-level option or the equivalent SERVER_LOG_LEVEL env.

Below is an example of how to adjust the log level.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --log-level \"trace\"\n

Note: The log format is not well defined and is subject to change.

"},{"location":"features/logging/#log-output-with-ansi","title":"Log output with ANSI","text":"

SWS does not output ANSI escape codes by default. However, If you want ANSI escape for colors and other text formatting when logging then use the boolean --log-with-ansi CLI option and its equivalent SERVER_LOG_WITH_ANSI env.

For example, if you want colored log output then use the --log-with-ansi option as follows:

static-web-server -p 8788 -d ./public/ -g trace -z --log-with-ansi\n
"},{"location":"features/logging/#log-remote-addresses","title":"Log Remote Addresses","text":"

SWS provides Remote Address (IP) logging for every request via an INFO log level.

This feature is disabled by default and can be enabled by the boolean --log-remote-address option or the equivalent SERVER_LOG_REMOTE_ADDRESS env.

If the feature is enabled then log entries for requests will contain a remote_addr section with the remote address (IP) value. Otherwise, it will be empty.

Log entry example:

2022-05-23T22:24:50.519540Z  INFO static_web_server::handler: incoming request: method=GET uri=/ remote_addr=192.168.1.126:57625\n

Below is an example of how to enable Remote Address (IP) logging.

static-web-server -a \"0.0.0.0\" -p 8080 -d docker/public/ -g info --log-remote-address=true\n

The relevant log output:

INFO static_web_server::logger: logging level: info\n<...>\nINFO static_web_server::info: log requests with remote IP addresses: enabled=true\n<...>\nINFO static_web_server::handler: incoming request: method=GET uri=/ remote_addr=192.168.1.126:57625\nINFO static_web_server::handler: incoming request: method=GET uri=/favicon.ico remote_addr=192.168.1.126:57625\n
"},{"location":"features/logging/#logging-client-ip-from-x-real-ip-header","title":"Logging Client IP from X-Real-IP header","text":"

Some upstream proxies will report the client's real IP address in the X-Real-IP header.

To enable logging of the X-Real-IP header, enable the --log-x-real-ip option or the equivalent SERVER_LOG_X_REAL_IP environment variable.

When enabled, the log entries will look like:

INFO static_web_server::handler: incoming request: method=GET uri=/ x_real_ip=203.0.113.195\n

If the value of the X-Real-IP header does not parse as an IP address, no value will be logged.

To restrict the logging to only requests that originate from trusted proxy IPs, you can use the --trusted-proxies option, or the equivalent SERVER_TRUSTED_PROXIES env. This should be a list of IPs, separated by commas. An empty list (the default) indicates that all IPs should be trusted.

"},{"location":"features/logging/#logging-client-ip-from-x-forwarded-for-header","title":"Logging Client IP from X-Forwarded-For header","text":"

Note: This header should only be trusted when you know your upstream is handling X-Forwarded-For securely and when using the --trusted-proxies option.

When used behind a reverse proxy the reported remote_addr indicates the proxies IP address and port, not the client's real IP. The Proxy server can be configured to provide the X-Forwarded-For header, containing a comma-separated list of IP addresses, starting with the real remote client IP, and all following intermediate proxies (if any).

To enable logging of the real remote IP, enable the --log-forwarded-for option or the equivalent SERVER_LOG_FORWARDED_FOR env. By default this will log all requests which have a correctly formatted X-Forwarded-For header.

Since the content of the X-Forwarded-For header can be changed by all proxies in the chain, the remote IP address reported may not be trusted.

To restrict the logging to only requests that originate from trusted proxy IPs, you can use the --trusted-proxies option, or the equivalent SERVER_TRUSTED_PROXIES env. This should be a list of IPs, separated by commas. An empty list (the default) indicates that all IPs should be trusted.

Command used for the following examples:

static-web-server -a \"::\" --log-forwarded-for=true --trusted-proxies=\"::1\" -p 8080 -d docker/public/ -g info\n

Look for these lines in the log output:

<...>\nINFO static_web_server::info: log level: info\nINFO static_web_server::info: log requests with remote IP addresses: enabled=false\nINFO static_web_server::info: log X-Forwarded-For real remote IP addresses: enabled=true\nINFO static_web_server::info: trusted IPs for X-Forwarded-For: [::1]\n<...>\n

We can simulate request as from behind reverse proxy with additional intermediate-proxy with following command:

curl \"http://[::1]:8080\" --header \"X-Forwarded-For: 203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348\"\n

Log entry for this request will look like:

INFO static_web_server::handler: incoming request: method=GET uri=/ real_remote_ip=203.0.113.195\n

If we send the request from 127.0.0.1 instead:

curl \"http://127.0.0.1:8080\" --header \"X-Forwarded-For: 203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348\"\n

we get the following log output:

INFO static_web_server::handler: incoming request: method=GET uri=/\n

127.0.0.1 is not in the trusted_proxies, so we dont get a real_remote_address in the log.

Note the absence of the proxies remote address in these examples. If you want to log the remote address and the real remote address, you need to specify both --log-remote-address and --log-forwarded-for.

SWS will parse the X-Forwarded-For header and if the provided client IP is invalid, it will be ignored to prevent log poisoning attacks. In such cases the real_remote_ip section will not be added.

Example from above, but with invalid header:

curl \"http://[::1]:8080\" --header \"X-Forwarded-For: <iframe src=//malware.attack>\"\n
2022-05-23T22:24:50.519540Z  INFO static_web_server::handler: incoming request: method=GET uri=/\n
"},{"location":"features/maintenance-mode/","title":"Maintenance Mode","text":"

SWS provides a way to put a server into a maintenance mode. Allowing the server to respond with a custom HTTP status code and HTML content always by default.

This is useful to allow the server to be taken offline without disrupting the service.

The feature is disabled by default and can be controlled by the boolean --maintenance-mode option or the equivalent SERVER_MAINTENANCE_MODE env.

"},{"location":"features/maintenance-mode/#how-it-works","title":"How it works","text":"

When the feature is enabled, SWS will respond always with the specified (or default) status code and HTML content to every request ignoring all SWS features. Except the Health check, CORS and Basic Authentication features.

"},{"location":"features/maintenance-mode/#http-status-code","title":"HTTP Status Code","text":"

The --maintenance-mode-status or the equivalent SERVER_MAINTENANCE_MODE_STATUS env variable can be used to tell SWS to reply with a specific status code.

When not specified, the server will reply with the 503 Service Unavailable status.

"},{"location":"features/maintenance-mode/#html-page","title":"HTML Page","text":"

The --maintenance-mode-file or the equivalent SERVER_MAINTENANCE_MODE_FILE env variable can be also used to customize the response content.

The value should be an existing local HTML file path. When not provided a generic message will be displayed.

Optional

Remember that either --maintenance-mode-status and --maintenance-mode-file are optional and can be omitted as needed.

Independent path

The --maintenance-mode-file is an independent file path and not relative to the root.

"},{"location":"features/maintenance-mode/#example","title":"Example","text":"

For instance, the server will respond with a 503 Service Unavailable status code and a custom message.

static-web-server -p 8787 -d ./public \\\n    --maintenance-mode \\\n    # optional status code, `503` by default\n    --maintenance-mode-status=503 \\\n    # optional HTML page, generic message by default\n    --maintenance-mode-file=\"./maintenance.html\"\n
"},{"location":"features/man-pages-completions/","title":"Generated CLI documentation","text":"

SWS can generate documentation for its command-line interface through man pages and shell completions.

"},{"location":"features/man-pages-completions/#completions","title":"Completions","text":"

You can generate completions for these shells and completion engines:

By typing the following command, all completions will be exported to a specific directory path:

static-web-server generate --completions /my-completions-dir\n
"},{"location":"features/man-pages-completions/#man-pages","title":"Man Pages","text":"

You can also generate man pages and export them to a specific directory path:

static-web-server generate --man-pages /my-man-pages-dir\n

Additionally, if you want both to be generated then just type:

static-web-server generate ./my-cli-docs-dir\n
"},{"location":"features/markdown-content-negotiation/","title":"Markdown Content Negotiation","text":"

SWS provides an optional content negotiation feature that serves markdown files when clients explicitly request them via the Accept: text/markdown HTTP header.

This feature enables serving the raw markdown source of your documentation or content alongside the rendered HTML versions, allowing clients to choose their preferred format.

The HTTP methods supported are GET and HEAD.

This feature is disabled by default and can be controlled by the boolean --accept-markdown option or the equivalent SERVER_ACCEPT_MARKDOWN env.

"},{"location":"features/markdown-content-negotiation/#how-it-works","title":"How it works","text":"

When a client sends a request with the Accept: text/markdown header, SWS will search for markdown variants in the following order:

  1. path.md - Direct markdown file
  2. path.html.md - Markdown source file
  3. path/index.html.md - Directory index markdown source

If a markdown variant is found, it will be served with the Content-Type: text/markdown; charset=utf-8 header. If no markdown variant exists, the request falls back to normal static file handling.

Important: The feature only activates for the explicit Accept: text/markdown header. Wildcard headers like Accept: text/* or Accept: */* will not trigger markdown content negotiation.

"},{"location":"features/markdown-content-negotiation/#usage-examples","title":"Usage examples","text":""},{"location":"features/markdown-content-negotiation/#basic-usage","title":"Basic usage","text":"

Start the server with markdown content negotiation enabled:

static-web-server --root ./public --accept-markdown\n

Request a markdown file:

curl -H \"Accept: text/markdown\" http://localhost:8080/article\n# Returns: article.md if it exists\n
"},{"location":"features/markdown-content-negotiation/#with-environment-variable","title":"With environment variable","text":"
export SERVER_ACCEPT_MARKDOWN=true\nstatic-web-server --root ./public\n
"},{"location":"features/markdown-content-negotiation/#configuration-file","title":"Configuration file","text":"
[general]\nroot = \"./public\"\naccept-markdown = true\n
"},{"location":"features/markdown-content-negotiation/#use-cases","title":"Use cases","text":""},{"location":"features/markdown-content-negotiation/#llm-friendly-content-with-llmstxt","title":"LLM-friendly content with llms.txt","text":"

This feature is the perfect companion for the llms.txt standard, which recommends that websites provide clean markdown versions of their pages for Large Language Models (LLMs) to consume.

According to the llms.txt standard, pages should provide markdown versions at the same URL with .md appended (or index.html.md for URLs without file names). With SWS's markdown content negotiation, LLMs and AI agents can request these markdown versions using the Accept: text/markdown header:

# LLM requesting markdown version\ncurl -H \"Accept: text/markdown\" https://example.com/docs/api\n# Returns: docs/api.html.md if it exists\n\n# Directory index\ncurl -H \"Accept: text/markdown\" https://example.com/docs/\n# Returns: docs/index.html.md if it exists\n

This approach is superior to traditional .md URL suffixes because:

"},{"location":"features/markdown-content-negotiation/#documentation-sites","title":"Documentation sites","text":"

Serve both HTML and markdown versions of your documentation:

docs/\n\u251c\u2500\u2500 article.html       # Rendered version\n\u2514\u2500\u2500 article.html.md    # Markdown source\n

Browsers get the HTML version, while API clients or tools can request the markdown source:

# Browser request (default)\ncurl http://localhost:8080/article\n# Returns: article.html\n\n# Request markdown source\ncurl -H \"Accept: text/markdown\" http://localhost:8080/article\n# Returns: article.html.md\n
"},{"location":"features/markdown-content-negotiation/#api-responses","title":"API responses","text":"

Allow your API clients to request content in markdown format:

fetch('/documentation', {\n  headers: { 'Accept': 'text/markdown' }\n})\n.then(response => response.text())\n.then(markdown => console.log(markdown));\n
"},{"location":"features/markdown-content-negotiation/#content-management","title":"Content management","text":"

Serve markdown files for editing tools while providing HTML for end users:

# Editor fetching source\ncurl -H \"Accept: text/markdown\" http://localhost:8080/blog/post\n# Returns: blog/post.md\n\n# Regular browser visit\ncurl http://localhost:8080/blog/post\n# Returns: blog/post.html or blog/post/index.html\n
"},{"location":"features/multiple-index-files/","title":"Multiple index files","text":"

SWS allows to provide a list of files that will be used as an index for requests ending with the slash character (\u2018/\u2019).

Notes

This feature is disabled by default and can be controlled by the string list --index-files option or the equivalent SERVER_INDEX_FILES env.

Here is an example:

static-web-server -p 8787 -d ./public \\\n    --index-files=\"index.html, index.htm, default.html\"\n
"},{"location":"features/security-headers/","title":"Security Headers","text":"

SWS provides several security headers support.

When the HTTP/2 feature is activated security headers are enabled automatically.

This feature is disabled by default on HTTP/1 and can be controlled by the boolean --security-headers option or the equivalent SERVER_SECURITY_HEADERS env.

Customize HTTP headers

If you want to customize HTTP headers on demand then have a look at the Custom HTTP Headers section.

"},{"location":"features/security-headers/#headers-included","title":"Headers included","text":"

The following headers are included by default.

"},{"location":"features/trailing-slash-redirect/","title":"Trailing Slash Redirect","text":"

SWS provides automatic trailing slash redirect support for directory requests.

This feature is enabled by default and can be controlled by the boolean --redirect-trailing-slash option or the equivalent SERVER_REDIRECT_TRAILING_SLASH env.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    --redirect-trailing-slash true\n
"},{"location":"features/url-redirects/","title":"URL Redirects","text":"

SWS provides the ability to redirect request URLs with Glob pattern-matching support.

URI redirects are particularly useful with pattern matching (globs). Use them for example to prevent broken links if you've moved a page or to shorten URLs.

"},{"location":"features/url-redirects/#structure","title":"Structure","text":"

The URL redirect rules should be defined mainly as an Array of Tables.

Each table entry should have the following key/value pairs:

Note

The incoming request(s) will reach the destination only if the request(s) URI matches the source pattern.

"},{"location":"features/url-redirects/#host","title":"Host","text":"

Optional host redirect entry to be matched against the incoming host URI. If a host redirect setting is specified then SWS will attempt to match the value against the incoming URI host (request), applying the required redirect entry or ignoring it otherwise.

www to non-www redirects

The host entry allows for instance to perform www to non-www redirects or vice versa (see example below).

"},{"location":"features/url-redirects/#source","title":"Source","text":"

The source is a Glob pattern that should match against the URI that is requesting a resource file.

The glob pattern functionality is powered by the globset crate which supports Standard Unix-style glob syntax.

Glob pattern syntax

For more details about the Glob pattern syntax check out https://docs.rs/globset/latest/globset/#syntax

Matching of path separator in *

Up to version 2.33.1 the wildcard * was matching the path separator. For example, /{*}/{*}/ matched /assets/images/logo/.

In later versions, the default has changed such that * does not match the path separator.

"},{"location":"features/url-redirects/#destination","title":"Destination","text":"

The value can be either a local file path that maps to an existing file on the system or an external URL. It could look like /some/directory/file.html. It is worth noting that the / at the beginning indicates the server's root directory.

"},{"location":"features/url-redirects/#replacements","title":"Replacements","text":"

Additionally, a destination supports replacements for every Glob pattern group that matches against the source. The replacement order starts from 0 to n and is defined with a dollar sign followed by an index (Glob pattern group occurrence).

Group your Glob patterns

When using replacements, also group your Glob pattern by surrounding them with curly braces so every group should map to its corresponding replacement. For example: source = \"**/{*}.{jpg,jpeg,svg}\"

"},{"location":"features/url-redirects/#kind","title":"Kind","text":"

It is a number that indicates the HTTP response code (redirect). The values can be:

"},{"location":"features/url-redirects/#examples","title":"Examples","text":"
[advanced]\n\n### URL Redirects\n\n# a. Simple route redirect example (existing file)\n[[advanced.redirects]]\nsource = \"**/*.{jpg,jpeg}\"\ndestination = \"/images/generic1.png\"\nkind = 301\n\n# b. Simple route redirect example (external URL)\n[[advanced.redirects]]\nsource = \"/index.html\"\ndestination = \"https://static-web-server.net\"\nkind = 302\n\n# c. Simple route redirect example with destination replacements\n[[advanced.redirects]]\n## Note that we're using curly braces to group the `*` wildcard.\n## See https://docs.rs/globset/latest/globset/#syntax\nsource = \"**/{*}.{jpg,jpeg,svg}\"\n## For example, the destination will result in `http://localhost/assets/abcdef.jpeg`\ndestination = \"http://localhost/assets/$1.$2\"\nkind = 301\n\n# d. Simple route redirect using the `host` option\n# to perform www to non-www redirection.\n[[advanced.redirects]]\nhost = \"www.domain.com\"\nsource = \"/{*}\"\ndestination = \"https://domain.com/$1\"\nkind = 301\n

If you request something like:

curl -I http://localhost:4433/abcdef.jpeg\n

Then the server logs should look something like this:

2023-07-11T21:11:22.217358Z  INFO static_web_server::handler: incoming request: method=HEAD uri=/abcdef.jpeg\n2023-07-11T21:11:22.217974Z DEBUG static_web_server::handler: url redirects glob pattern: [\"$0\", \"$1\", \"$2\"]\n2023-07-11T21:11:22.217992Z DEBUG static_web_server::handler: url redirects regex equivalent: (?-u)^(?:/?|.*/)(?:[^/]*)\\.(?:svg|jpeg|jpg)$\n2023-07-11T21:11:22.218002Z DEBUG static_web_server::handler: url redirects glob pattern captures: [\"abcdef.jpeg\", \"abcdef\", \"jpeg\"]\n2023-07-11T21:11:22.218076Z DEBUG static_web_server::handler: url redirects glob pattern destination: \"http://localhost/assets/$1.$2\"\n2023-07-11T21:11:22.218712Z DEBUG static_web_server::handler: url redirects glob pattern destination replaced: \"http://localhost/assets/abcdef.jpeg\"\n2023-07-11T21:11:22.218739Z TRACE static_web_server::handler: uri matches redirects glob pattern, redirecting with status '301 Moved Permanently'\n...\n
"},{"location":"features/url-rewrites/","title":"URL Rewrites","text":"

SWS provides the ability to rewrite request URLs (routes) with Glob pattern-matching support.

URI rewrites are particularly useful with pattern matching (globs), as the server can accept any URL that matches the pattern and let the client-side code decide what to display.

"},{"location":"features/url-rewrites/#structure","title":"Structure","text":"

URL rewrite rules should be defined mainly as an Array of Tables.

Each table entry should have two key/value pairs:

Note

The incoming request(s) will reach the destination only if the request(s) URI matches the source pattern.

"},{"location":"features/url-rewrites/#source","title":"Source","text":"

It's a Glob pattern that should match against the URI that is requesting a resource file.

The glob pattern functionality is powered by the globset crate which supports Standard Unix-style glob syntax.

Glob pattern syntax

For more details about the Glob pattern syntax check out https://docs.rs/globset/latest/globset/#syntax

Matching of path separator in *

Up to version 2.33.1 the wildcard * was matching the path separator. For example, /{*}/{*}/ matched /assets/images/logo/.

In later versions, the default has changed such that * does not match the path separator.

"},{"location":"features/url-rewrites/#destination","title":"Destination","text":"

The value should be a relative or absolute URL. A relative URL could look like /some/directory/file.html. An absolute URL can be https://external.example.com/ for example.

"},{"location":"features/url-rewrites/#replacements","title":"Replacements","text":"

Additionally, a destination supports replacements for every Glob pattern group that matches against the source.

Replacements order start from 0 to n and are defined with a dollar sign followed by an index (Glob pattern group occurrence).

Group your Glob patterns

When using replacements, also group your Glob pattern by surrounding them with curly braces so every group should map to its corresponding replacement. For example: source = \"**/{*}.{png,gif}\"

"},{"location":"features/url-rewrites/#destination-processing","title":"Destination processing","text":"

How destination is processed depends on whether the redirect key (see below) is present. If it is present, SWS will perform an external redirect. It will send a redirect response to the client, and the browser will usually proceed to the destination. In case of a relative URL, it will be another page on the same server. An absolute URL can result in navigation to another server.

Without a redirect key, SWS will perform an internal redirect. It will attempt to retrieve the file denoted by the destination and send it to the client. While it is possible to specify an absolute URL here as well, it will always be processed by the same SWS instance. It will result by the request being mapped to a different virtual host however if a matching virtual host is present.

"},{"location":"features/url-rewrites/#different-roots-within-the-same-virtual-host","title":"Different roots within the same virtual host","text":"

Normally, different root directories are only possible with different virtual hosts. Rewrites however allow exposing another root in a subdirectory for example. For that, you add an internal virtual host that isn't normally visible from outside, e.g. internal.local. You then rewrite the requests to the subdirectory to the internal virtual host. For example:

[general]\nroot = \"/usr/srv/www\"\n\n[advanced]\n\n[[advanced.rewrites]]\nsource = \"/test/{**}\"\ndestination = \"http://internal.local/test/$1\"\n\n[[advanced.virtual-hosts]]\nhost = \"internal.local\"\nroot = \"/usr/srv/alternative-root\"\n

A request to /index.html will be mapped to /usr/srv/www/index.html, yet /test/hi.txt will be mapped to the file /usr/srv/alternative-root/test/hi.txt.

This approach has two caveats:

  1. When SWS produces redirects (e.g. redirecting http://internal.local/test/subdir to http://internal.local/test/subdir/), it isn't aware of rewrites. Unless the path part of the URL is identical before and after rewrite (like in the example above), this will result in broken redirects.
  2. While the internal.local virtual host isn't normally accessed directly, this doesn't mean that it isn't possible for someone knowing (or guessing) its name. You should consider all files under the virtual host's root as public. Don't put any secrets in it even if these aren't accessible via rewrites.
"},{"location":"features/url-rewrites/#redirect","title":"Redirect","text":"

An optional number that indicates the HTTP response code (redirect). The values can be:

"},{"location":"features/url-rewrites/#examples","title":"Examples","text":"
[advanced]\n\n### URL Rewrites\n\n# a. Simple route rewrite example\n[[advanced.rewrites]]\nsource = \"**/*.{png,ico,gif}\"\ndestination = \"/assets/generic1.png\"\n\n# b. Route rewrite example with redirection\n[[advanced.rewrites]]\nsource = \"**/*.{jpg,jpeg}\"\ndestination = \"/images/generic2.png\"\n## NOTE: `redirect` can be omitted too\nredirect = 301\n\n# c. Route rewrite example with destination replacements\n[[advanced.rewrites]]\n## Note that we're using curly braces to group the `*` wildcard.\n## See https://docs.rs/globset/latest/globset/#syntax\nsource = \"**/{*}.{png,gif}\"\n## For example, the destination will result in `/assets/abcdef.png`\ndestination = \"/assets/$1.$2\"\n

If you request something like:

curl -I http://localhost/abcdef.png\n

Then the server logs should look something like this:

2023-07-08T20:31:36.606035Z  INFO static_web_server::handler: incoming request: method=HEAD uri=/abcdef.png\n2023-07-08T20:31:36.608439Z DEBUG static_web_server::handler: url rewrites glob patterns: [\"$0\", \"$1\", \"$2\"]\n2023-07-08T20:31:36.608491Z DEBUG static_web_server::handler: url rewrites regex equivalent: (?-u)^(?:/?|.*/)(?:[^/]*)\\.(?:gif|png)$\n2023-07-08T20:31:36.608525Z DEBUG static_web_server::handler: url rewrites glob pattern captures: [\"abcdef.png\", \"abcdef\", \"png\"]\n2023-07-08T20:31:36.608561Z DEBUG static_web_server::handler: url rewrites glob pattern destination: \"/assets/$1.$2\"\n2023-07-08T20:31:36.609655Z DEBUG static_web_server::handler: url rewrites glob patterns destination replaced: \"/assets/abcdef.png\"\n2023-07-08T20:31:36.609735Z TRACE static_web_server::static_files: dir: base=\"public\", route=\"assets/abcdef.png\"\n...\n
"},{"location":"features/virtual-hosting/","title":"Virtual Hosting","text":"

SWS provides rudimentary support for name-based virtual hosting. This allows you to serve files from different root directories depending on the \"Host\" header of the request, with all other settings staying the same.

All other settings are the same!

Each virtual host has to have all the same settings (aside from root). If using TLS, your certificates will have to cover all virtual host names as Subject Alternative Names (SANs). Also, beware of other conflicting settings like redirects and rewrites. If you find yourself needing different settings for different virtual hosts, it is recommended to run multiple instances of SWS.

Virtual hosting can be useful for serving more than one static website from the same SWS instance, if it's not otherwise feasible to run multiple instances of SWS. Browsers will automatically send a Host header which matches the hostname in the URL bar, which is how HTTP servers are able to tell which \"virtual\" host that the client is accessing.

By default, SWS will always serve files from the main root directory. If you configure virtual hosting and the \"Host\" header matches, SWS will instead look for files in an alternate root directory you specify.

"},{"location":"features/virtual-hosting/#examples","title":"Examples","text":"
# By default, all requests are served from here\nroot = \"/var/www/html\"\n\n[advanced]\n\n[[advanced.virtual-hosts]]\n# But if the \"Host\" header matches this...\nhost = \"sales.example.com\"\n# ...then files will be served from here instead\nroot = \"/var/sales/html\"\n\n[[advanced.virtual-hosts]]\nhost = \"blog.example.com\"\nroot = \"/var/blog/html\"\n
"},{"location":"features/webassembly/","title":"WebAssembly","text":"

SWS can run in a WebAssembly context.

Wasm/Wasix targets are not officially supported by SWS yet

We do not officially support Wasm or Wasix targets yet. But SWS project will eventually support Wasix as a target in a not remote future. In the meantime, Wasmer folks made it possible to run SWS via Wasix today via a series of patches.

"},{"location":"features/webassembly/#wasix","title":"Wasix","text":"

You can run SWS using The Wasmer Runtime with Wasix. See the wasmer/static-web-server package.

To run SWS, make sure to install Wasmer first and then enable its net and threads features as well as map your host directory via the mapdir option before starting the server.

Here is an example.

wasmer run wasmer/static-web-server \\\n    --net --enable-threads --mapdir /public:/my/host/dir -- --port 8787\n

See The WASIX with Axum Tutorial for more details.

"},{"location":"features/windows-service/","title":"Windows Service","text":"

SWS can be also executed in a Windows Service context. Therefore it also provides a subcommand to install SWS as a Windows Service.

This feature is disabled by default and can be controlled by the boolean -s, --windows-service option or the equivalent SERVER_WINDOWS_SERVICE env.

Static Web Server running as a Windows Service and displayed by 'services.msc' application.

"},{"location":"features/windows-service/#important-notes","title":"Important Notes","text":""},{"location":"features/windows-service/#service-privileges","title":"Service privileges","text":"

To either install or uninstall the SWS Windows service requires administrator privileges, so make sure to open the terminal application as administrator or give your Powershell session enough privileges otherwise you will get an \"Access is denied\" error.

We recommend a Powershell session with administrator privileges.

"},{"location":"features/windows-service/#windows-firewall","title":"Windows Firewall","text":"

You can serve content with SWS in a Windows network. However, if you face issues running SWS it could be due to missing firewall configuration. So you probably have to define an inbound rule to allow inbound network traffic on a specified TCP port of your choice.

Follow the steps below to adjust your firewall:

  1. Configure an Inbound Port Rule in your Windows firewall so clients can reach the server's port.
  2. In your SWS config file, use the server IP as a host or a non-routable address like 0.0.0.0 if you prefer.
  3. Create a Windows Service following https://static-web-server.net/features/windows-service/ and start it.
  4. Finally, restart the service to apply the changes.

Note that the steps above are general and you have to adjust your firewall rule(s) according to your needs.

"},{"location":"features/windows-service/#install-the-service","title":"Install the service","text":"

To install the SWS service use the install command along with a configuration file for further SWS options customization.

Make sure to provide a configuration file to run SWS service properly. In particular, configure the server address, port and root directory accordingly. If not then the service might not start.

The following command will create the SWS service called static-web-server with a \"Static Web Server\" display name.

static-web-server.exe -w C:\\Users\\MyUser\\sws.toml install\n# Windows Service (static-web-server) is installed successfully!\n# Start the service typing: sc.exe start \"static-web-server\" (it requires administrator privileges) or using the 'services.msc' application.\n
"},{"location":"features/windows-service/#interact-with-the-service","title":"Interact with the service","text":"

SWS doesn't provide a way to interact with Windows services directly. Instead, use the Windows built-in tools to interact with the SWS service once created.

For that purpose, you can use either the Windows sc.exe or the services.msc application.

For example, using sc.exe you can show the SWS service configuration used once installed.

sc.exe qc \"static-web-server\"\n# [SC] QueryServiceConfig SUCCESS\n\n# SERVICE_NAME: static-web-server\n#         TYPE               : 10  WIN32_OWN_PROCESS\n#         START_TYPE         : 3   DEMAND_START\n#         ERROR_CONTROL      : 1   NORMAL\n#         BINARY_PATH_NAME   : C:\\Users\\MyUser\\static-web-server.exe\n#                                   --windows-service=true\n#                                   --config-file=C:\\Users\\MyUser\\sws.toml\n#         LOAD_ORDER_GROUP   :\n#         TAG                : 0\n#         DISPLAY_NAME       : Static Web Server\n#         DEPENDENCIES       :\n#         SERVICE_START_NAME : LocalSystem\n

Remember that alternatively, you can also use the services.msc application if you prefer GUI service control.

"},{"location":"features/windows-service/#start","title":"Start","text":"

To start the service use the following sc.exe command.

sc.exe start \"static-web-server\"\n# SERVICE_NAME: static-web-server\n#     TYPE               : 10  WIN32_OWN_PROCESS\n#     STATE              : 2  START_PENDING\n#                             (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)\n#     WIN32_EXIT_CODE    : 0  (0x0)\n#     SERVICE_EXIT_CODE  : 0  (0x0)\n#     CHECKPOINT         : 0x0\n#     WAIT_HINT          : 0x7d0\n#     PID                : 3068\n#     FLAGS              :\n
"},{"location":"features/windows-service/#status","title":"Status","text":"

To show the service status use the following sc.exe command.

sc.exe query \"static-web-server\"\n# SERVICE_NAME: static-web-server\n#     TYPE               : 10  WIN32_OWN_PROCESS\n#     STATE              : 4  RUNNING\n#                             (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)\n#     WIN32_EXIT_CODE    : 0  (0x0)\n#     SERVICE_EXIT_CODE  : 0  (0x0)\n#     CHECKPOINT         : 0x0\n#     WAIT_HINT          : 0x0\n
"},{"location":"features/windows-service/#stop","title":"Stop","text":"

To stop the service use the following sc.exe command.

sc.exe stop \"static-web-server\"\n# SERVICE_NAME: static-web-server\n#         TYPE               : 10  WIN32_OWN_PROCESS\n#         STATE              : 3  STOP_PENDING\n#                                 (STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)\n#         WIN32_EXIT_CODE    : 0  (0x0)\n#         SERVICE_EXIT_CODE  : 0  (0x0)\n#         CHECKPOINT         : 0x2\n#         WAIT_HINT          : 0xbb8\n

After stopping the service you can also show its status.

sc.exe query \"static-web-server\"\n# SERVICE_NAME: static-web-server\n#         TYPE               : 10  WIN32_OWN_PROCESS\n#         STATE              : 1  STOPPED\n#         WIN32_EXIT_CODE    : 0  (0x0)\n#         SERVICE_EXIT_CODE  : 0  (0x0)\n#         CHECKPOINT         : 0x0\n#         WAIT_HINT          : 0x0\n
"},{"location":"features/windows-service/#uninstall-the-service","title":"Uninstall the service","text":"

To uninstall the SWS service just use the uninstall command. Note that the service should be first stopped before uninstalling it.

static-web-server.exe uninstall\n# Windows Service (static-web-server) is uninstalled!\n

After uninstalling the service you can verify if removed.

sc.exe qc \"static-web-server\"\n# [SC] OpenService FAILED 1060:\n#\n# The specified service does not exist as an installed service.\n
"},{"location":"features/worker-threads/","title":"Worker Threads Customization","text":"

SWS allows customizing the number of worker threads powered by the Tokio runtime.

See Tokio worker_threads API.

This feature can be controlled by the numeric -n, --threads-multiplier option or the equivalent SERVER_THREADS_MULTIPLIER env.

"},{"location":"features/worker-threads/#worker-threads-multiplier","title":"Worker threads multiplier","text":"

The value of -n, --threads-multiplier works as multiplier digits to determine the number of worker threads used by the server.

Multiplying this input number by the number of system CPUs.

The formula used is the following:

worker threads = number of CPUs * n

Where n is the input value of -n, --threads-multiplier.

For example: If there are 4 available CPUs and the --threads-multiplier is 8 then the total of worker threads to use will be 32.

Tip

When the --threads-multiplier input value is 0 or 1 then one thread per core is used (default value).

WebAssembly

We use 2 threads per core in Wasm and 1 in native environments by default.

Warn

The number of worker threads resulted should be a number between 1 and 32,768 though it is advised to keep this value on the smaller side. See Tokio worker_threads API for more details.

Below is an example of how to adjust the number of worker threads.

static-web-server \\\n    --port 8787 \\\n    --root ./my-public-dir \\\n    # NOTE: \"8\" gets multiplied by the number of the available cores.\n    --threads-multiplier 8\n
"}]} \ No newline at end of file diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 4fce4d6d..648bbea7 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -2,190 +2,190 @@ https://static-web-server.net/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/building-from-source/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/contributions/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/download-and-install/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/download-and-install.template/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/getting-started/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/license/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/migration/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/platforms-architectures/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/report-security-issues/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/semantic-versioning/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/showcases/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/configuration/command-line-arguments/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/configuration/config-file/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/configuration/environment-variables/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/basic-authentication/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/blocking-threads/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/cache-control-headers/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/compression-static/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/compression/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/cors/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/custom-http-headers/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/directory-listing/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/disable-symlinks/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/docker/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/error-pages/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/file-descriptor-socket-passing/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/graceful-shutdown/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/health-endpoint/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/http-https-redirect/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/http-methods/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/http1/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/http2-tls/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/ignore-files/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/logging/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/maintenance-mode/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/man-pages-completions/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/markdown-content-negotiation/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/multiple-index-files/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/security-headers/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/trailing-slash-redirect/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/url-redirects/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/url-rewrites/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/virtual-hosting/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/webassembly/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/windows-service/ - 2025-12-20 + 2026-01-08 https://static-web-server.net/features/worker-threads/ - 2025-12-20 + 2026-01-08 \ No newline at end of file diff --git a/docs/sitemap.xml.gz b/docs/sitemap.xml.gz index 35acbee7e89ac6daff6964ebc9051b6bff6276fd..4efe4e8c2561b1e90b31bb7ecf2b2b5fdaf5bb27 100644 GIT binary patch literal 678 zcmV;X0$KeZiwFpS^ImBJ|8r?{Wo=<_E_iKh0L@szZrd;nz4t4E+!ZH14Be2Xw|&9( z0gBAnLM724sk-_5QBKhHIt(!Di*1P(pQOhp(#MzgoHlPj9I-ra54-(#10`S|%kaGY z{p+WD+P<6~j+GLFY}B~$^L7-OestYD&pVC+*-yMf8afW6zHoPM?Do6w+tb5FuQtH# zX}OLG!>NtJIt+`K7~BkJ$3Ti@S3tUB*K9t2;#o5Gr^o%{H@82y{gay9+ipI(4=4Zb z?p$MPlXe%v>=-KrSQ|sYA{5V6bT4>q=RM9Pq3@k9y^BSSQu+!Qc1oowd0Blo1`uh4 z&|VVg*LI&`fWoWKI>&*0BjDPO+a6z#GCNOUQ~?Q6304mSf(c2qB~*%Xb&+jbE{4G+?p0(Fk8KH64L`D9BQQOxh-!c;T-fmTb*QCfTM>{4rDi#fCJ2aRl zy*Ev9H`A{G#Snw#UQ`y%HJi0LFZxJ&;38dBXju%7n@>o%$(ZA7R?UAkToM%!AT#$s zgybea7%F-@;am?KC{9WaEsJzXL_ulP8d5|v)Bu;79FJ96&0vN^!#2t2p!5^QB6c_y z4QaNH;1O}S$lTdsK;B;s(1X6BF z?po55z~l$dCp7fhRC7s6Tj@|>K#8{bdag;LE{%(&hj(M*epLU~X_=6ZY557IgOB#w zu3vtDU}F@s?vNozwDOkfwW34Yd|BHy9fN#MS33+Ev^ew97lWP7&PbZF|9{E9+!*Ba M2arfmaUU4~0R2BgQUCw| literal 678 zcmV;X0$KeZiwFn+=S67(|8r?{Wo=<_E_iKh0L@szZrd;nz4t4E+!dz*25du`-ueaG z2PiUQ3zbBJr0VAHM>#>)>oCBqFSaFGe3BlYNFSfybK1NCam4bpecbK08z=$$Sca$V z?_V$O`}XcdERjp$bRA-($H}j^@Y28W4GV^*q$CXdbI&= zPs?>o7*1^z)?rw@#NcK)I|foLy8_Z3yJqwG6VH;dKOOdmZ|?En4tq7Zx7~bnA5Q+= z-MPlpChabS*)diMur`K(MJS%D=w9&J&U>6oLf<=IdKZftrSugr?37AV^0NAF3?R}7 zp}i!~ukAj?0EJhdb&do1M!>Zlw>`cfWpLPPp9|fy? zmezh(X651P5JsGX=k@2VDtL)Qy|XZ2&P@$cETGI7YK9`_nA)4W#00iLpu9!G(w4-% zd5Zp=*59uS_*_Gs$;;3V12KMn;6mK4