A zero-config development web server in Rust, with CGI and .htaccess support.
Point it at a directory and you get static files, directory listings, classic CGI
scripts (Perl/PHP/TypeScript via bun), URL rewriting, basic auth, custom error
pages, and self-signed HTTPS — all driven by familiar Apache-style .htaccess
files.
Built for local development and quick prototyping. Not a production server.
- Static file serving with automatic MIME detection
- Directory listings (toggleable per-request and via
Options ±Indexes) - CGI/1.1 execution for
.pl,.php,.ts, and.jsscripts .htaccesssupport with per-directory overrides:DirectoryIndex,Options ±IndexesErrorDocumentfor custom error pagesAddType,AddDefaultCharsetRedirect(301/302)RewriteEngine,RewriteRule,RewriteCond(incl.[QSA],[L],[R])AuthType Basicwith.htpasswd(bcrypt, SHA-1, Apache MD5)
- HTTPS with auto-generated self-signed certificates (cached in
~/.config/webrunner/) or your own cert/key - Simultaneous HTTP + HTTPS listeners
- Graceful shutdown on
Ctrl+C/SIGTERM
brew install codedeviate/cli/webrunnercargo install webrunnerRequires Rust 1.75+ (2021 edition).
git clone https://github.com/codedeviate/webrunner.git
cd webrunner
cargo install --path .Or build a release binary:
cargo build --release
./target/release/webrunnerOptional runtime dependencies (only needed if you serve CGI scripts of that
type): perl, php, bun. Missing interpreters trigger a startup warning, and
the corresponding scripts return 500.
cd /path/to/your/site
webrunnerThen open http://localhost:8080.
webrunner [OPTIONS]
-p, --port <PORT> HTTP port [default: 8080]
--https Enable HTTPS (auto-generates a self-signed cert)
--https-port <PORT> HTTPS port [default: 8443]
--cert <PATH> Path to TLS certificate (PEM); requires --key
--key <PATH> Path to TLS private key (PEM); requires --cert
--no-index Disable directory listing (return 403 for dirs)
--examples Print rich usage examples and exit
-h, --help Show help
-V, --version Show version
Run webrunner --examples for a complete cookbook covering CGI, .htaccess,
auth, rewrites, and HTTPS.
webrunner -p 3000 --no-indexwebrunner --https
# https://localhost:8443 (cert cached in ~/.config/webrunner/)The certificate fingerprint is printed at startup so you can verify the exception your browser shows.
webrunner --cert ./cert.pem --key ./key.pemhello.pl (must be executable):
#!/usr/bin/perl
print "Content-Type: text/html\n\n";
print "<h1>Hello from Perl</h1>\n";Browse to http://localhost:8080/hello.pl.
Scripts receive standard CGI/1.1 environment variables (REQUEST_METHOD,
QUERY_STRING, CONTENT_TYPE, CONTENT_LENGTH, PATH_INFO, SCRIPT_NAME,
HTTP_*, …). The POST body is delivered on stdin. Output is parsed as
headers + blank line + body; use Status: 404 Not Found to set a non-200
response.
.htaccess:
DirectoryIndex index.php
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [L]
AuthType Basic
AuthName "Restricted Area"
AuthUserFile /absolute/path/to/.htpasswd
Require valid-user
ErrorDocument 404 /errors/404.htmlGenerate the password file with:
htpasswd -B .htpasswd aliceSupported hash formats: bcrypt ($2y$…), SHA-1 ({SHA}…), Apache MD5
($apr1$…).
For each request, webrunner walks from the document root down to the target
file's directory and merges the .htaccess files it finds. Deeper files
override shallower ones. Unknown directives are skipped with a warning on
stderr.
cargo test # run the test suite
cargo build # debug build
cargo build --releaseThe crate is organised by concern:
| Module | Responsibility |
|---|---|
cli |
Command-line parsing and validation |
server |
HTTP/HTTPS listeners and graceful shutdown |
handler |
Request dispatch and .htaccess evaluation |
static_files |
Static file serving and directory listings |
cgi |
CGI/1.1 process spawning and response parsing |
htaccess |
.htaccess parser and merger |
rewrite |
RewriteRule / RewriteCond engine |
auth |
HTTP Basic auth with .htpasswd verification |
mime |
MIME-type lookup |
tls |
Self-signed cert generation and loading |
- Not a production server. No request size limits, no rate limiting, no process isolation between CGI scripts. Use it for local development.
- CGI scripts inherit the parent's
PATHso interpreters resolve correctly. - Only a practical subset of
.htaccessis implemented — the directives listed in the Features section. Unknown directives are warned and ignored.
MIT © Thomas Björk