Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

uwebserver: the static file server of Velocity

This page is for developers and testers who need a fast, safe static file server, or a baseline to measure the PHP server against. uwebserver is a small web server written in C that ships with the Velocity engine. It…

Download as PDF

Address: https://doc.exponential.earth/exphelp/viewer/features/6.0/velocity-uwebserver.md

On this page

uwebserver

This page is for developers and testers who need a fast, safe static file server, or a baseline to measure the PHP server against. uwebserver is a small web server written in C that ships with the Velocity engine. It answers HTTP/1.1 and HTTPS from one event loop per process, with no PHP at all. It is not the application server (that is sbin/qbixserver.php and the phar, which run PHP). Applies to Exponential Velocity 0.0.4.42 and later.

Nothing in a site starts it. The engine, its packages, the container image, the release workflows and the Exponential installation never run it, so upgrading changes nothing for a site. Only people and tests start it.

It is safe by default: it listens on 127.0.0.1:8000 only, refuses to start as root without --user, never starts on an option it does not understand, and has had four security reviews, a fuzzed request parser and sanitiser-clean tests (release 0.0.4.42).

Build and run in one minute

It needs a C compiler and OpenSSL 3 (libssl-dev or openssl-devel):

make -C native/uwebserver                         # builds sbin/uwebserver (hardened, -Werror)
sbin/uwebserver --root=./web                      # http://127.0.0.1:8000/

Check it from a second shell:

curl -sI http://127.0.0.1:8000/

Expected: HTTP/1.1 200 with Server: uwebserver (or 403 if ./web has no index.html and listings are off).

More:

sbin/uwebserver --help                            # every option, grouped
man docs/uwebserver.1                             # the manual page
make -C native/uwebserver install PREFIX=/usr/local
make -C native/uwebserver sanitize                # AddressSanitizer + UndefinedBehaviorSanitizer build for tests
make -C native/uwebserver fuzz                    # the request-parsing fuzzer

sbin/uwebserver as committed is built with GCC 11.5 on EL 9, x86-64, linked against the system's OpenSSL 3. On another system, build it again as above. bin/uwebserver is a forwarder at the former path that executes sbin/uwebserver.

The command line

GNU style: --name=VALUE, --name VALUE, the one-dash spellings (-root=/srv/www), bundled short options (-p8081), --no- forms, and -- to end options.

The whole command line is read first. An unknown option, a missing or malformed value, contradictory options or a stray word is reported on standard error with Try 'uwebserver --help' for more information. and exit status 2, and nothing starts. --help exits 0.

  • --check (--test-config) checks everything a start needs and serves nothing.
  • --print-config shows the effective configuration.
  • --config=FILE (or UWEBSERVER_CONFIG) reads the same names from a file.

Listening

OptionDefaultMeaning
-l, --listen=[ADDR:]PORTnoneHTTP there; repeatable
-b, --bind=ADDR127.0.0.1Address of --port and --tls-port
-p, --port=PORT8000HTTP on ADDR:PORT
--tls-listen=[ADDR:]PORTnoneHTTPS there; needs --cert and --key
--tls-port=PORT8443HTTPS on ADDR:PORT
--reuse-portoffLet other servers share the ports (SO_REUSEPORT); without it a second start fails with "Address already in use"
--backlog=N511Connections waiting to be accepted

ADDR is a numeric IPv4 address, an IPv6 address in brackets, * for every IPv4 address, or localhost. Host names are never resolved. Every interface is one explicit --listen='*:PORT' away.

Content (--root=DIR)

OptionDefaultMeaning
-r, --root=DIRnoneThe document root
--index=NAMESindex.htmlFiles that answer for a directory
--directory-listingoffList a directory with no index file (otherwise 403)
--hidden-filesoffServe names starting with a dot (otherwise 404)
--symlinks=inside|neverinsideFollow links that stay inside the root, or none
--mime-types=FILEnoneMore types in mime.types format
--default-type=TYPEapplication/octet-streamType of other files
--charset=NAMEutf-8Added to text types (empty: none)
--server-name=NAME, --server-headeruwebserver, onValue of the Server header, and whether it is sent
--cache-control=VALUEnoneCache-Control of files
--etagonETag, If-None-Match, If-Range
--gzip-staticoffServe FILE.gz to clients that accept gzip
-H, --header='NAME: VALUE'noneAdded to every response; repeatable

What it serves: GET and HEAD, Last-Modified and ETag with conditional requests (304), one byte range (206, or 416), a redirect to a directory's slash, and X-Content-Type-Options: nosniff on every file and error.

How it stays inside the root: a path cannot contain .., an encoded / or NUL (400); dot names are 404; files are opened with openat2(2) and RESOLVE_BENEATH, so the kernel refuses a path, link or race that leads out of the root (links inside the tree must be relative). Without --root, it gives the built-in answers of the benchmark.

Limits, TLS, logging, privileges

OptionDefaultMeaning
-w, --workers=N1 (at most 256)Worker processes; one that ends is replaced
--max-connections=N1024Per worker
--max-requests=N1000Requests on one connection, then Connection: close (0: no limit)
--max-header-size=SIZE8kOver it: 431
--max-uri-length=SIZE4kOver it: 414
--max-body-size=SIZE1mOver it: 413
--header-timeout=SECONDS10The whole head must arrive
--read-timeout, --write-timeout30, 30Between reads of a request body; without progress sending a response
--keepalive-timeout=SECONDS5Idle between requests
--tls-handshake-timeout=SECONDS10A TLS handshake
--cert, --key, --chainnonePEM files; the key is refused when another user may change it
--tls-min-version=1.2|1.31.2Oldest version accepted
--tls-ciphers=LIST, --tls-ciphersuites=LISTOpenSSL defaultsTLS 1.2 ciphers and TLS 1.3 suites, as OpenSSL lists them

TLS 1.2 offers only forward-secret AEAD ciphers; no session tickets, no compression, no renegotiation.

Other options: --access-log (common, combined or json), --error-log, --quiet, --verbose, --daemon, --pid-file (locked), --user, --group, --chroot, --allow-root. As root it serves only with --user (dropped after the ports are open) or --allow-root.

Upgrading from 0.0.4.41 or earlier

  • It listens on 127.0.0.1:8000 by default, not on every interface at 8080. To keep the old address: --listen='*:8080'. --port=N alone now binds 127.0.0.1:N.
  • As root it needs --user or --allow-root.
  • An unknown option starts nothing (exit 2). --cert without --key is an error, not plain HTTP.
  • The Server header is uwebserver (no version); the start-up line goes to standard error. /health keeps its keys.
  • Removed: the generated record types and the U runtime that were compiled in but never called, and the unused headers u_runtime.h, u_merkle_cache.h.

Limits

  • HTTP/1.1 only (ALPN http/1.1); no PHP.
  • Linux with OpenSSL 3. openat2 containment needs kernel 5.6 or later (before it, every component is opened with O_NOFOLLOW and no link is followed).

Become an Exponential Partner

Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x