simple-httpd is a small C++17 HTTP daemon that serves static files.
One process, one config file (INI, YAML, or JSON), one document root (or several, by Host). It speaks HTTP/1.1 and HTTP/2, optionally HTTPS. Reverse proxy, CGI, FastCGI, and SSI are opt-in extras and stay off unless you set them.
Version 0.8.0 — Apache License 2.0 · SimpleDaemons
./build/simple-httpd --root www --port 8080
# open http://127.0.0.1:8080/| Area | Capability |
|---|---|
| Protocol | HTTP/1.1 and HTTP/2 GET, HEAD, OPTIONS; keep-alive; ETag / Last-Modified / 304; single-range 206; chunked and Content-Length request bodies |
| Content | Document root, index files, MIME types (plus mime_type extras), optional directory listing, gzip / deflate, in-memory file cache |
| Sites | Name-based virtual hosts (Host header) |
| TLS | OpenSSL TLS 1.2+ (optional 1.3 floor), PEM cert + key, ECDHE/PFS ciphers, optional HSTS, ALPN (h2 / http/1.1) |
| Security | Path-traversal rejection, method allow-list, CSP and related headers, HTTP Basic, per-IP request/connection limits, CIDR allow/deny, security audit log |
| Routing | Prefix URL rewrites (/blog → /posts); optional HTTP reverse proxy with round-robin |
| Dynamic | Optional CGI, FastCGI (TCP), SSI (include / echo; no exec) |
| Operations | /healthz, Prometheus /metrics, JSON /status, auth-gated admin config/reload, SIGHUP soft reload, privilege drop / daemonize, access / error / security logs |
| Config | INI, YAML, or JSON file; CLI flags; SIMPLE_HTTPD_* environment |
It is not nginx or Apache. HTTP/3, HTTPS origin proxy, FastCGI process management, and a framed WebSocket stack are out of scope. See ROADMAP.md if you care about later protocol work.
| Guide | Contents |
|---|---|
| docs/installation.md | Dependencies, CMake, tests, install |
| docs/configuration.md | Every config key, CLI flag, and environment variable |
| docs/security.md | TLS, headers, Basic auth, rate limits, hardening |
| docs/operations.md | Logs, health, metrics, signals, systemd / launchd / Docker |
| docs/architecture.md | How a request moves through the process |
| docs/troubleshooting.md | Bind failures, TLS, 401/429, gzip, CSP |
| config/README.md | Ready-made simple / advanced / production / security profiles |
Internal planning lives under project/. Version policy: VERSIONING.md. History: CHANGELOG.md.
Needs a C++17 compiler and CMake 3.16+. OpenSSL, zlib, and nghttp2 are used when present (HTTPS, gzip, HTTP/2).
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DENABLE_PACKAGING=OFF -DENABLE_SSL=ON -DENABLE_HTTP2=ON
cmake --build build
ctest --test-dir build --output-on-failuremake build / make test wrap the same CMake flow.
The tree includes a tiny site in www/.
./build/simple-httpd --root www --port 8080
curl -i http://127.0.0.1:8080/
curl -I http://127.0.0.1:8080/style.css
curl --http2-prior-knowledge -i http://127.0.0.1:8080/Stop with SIGINT or SIGTERM (Ctrl+C). Foreground is the default; use -d / --daemonize on Unix if you need a background process (systemd is preferred).
./build/simple-httpd --root www --port 8443 \
--tls-cert /path/to/fullchain.pem \
--tls-key /path/to/privkey.pem \
--hstsBoth cert and key must be set. The listener is HTTPS-only in that mode (no plaintext on the same port).
cp config/examples/simple.conf.example simple-httpd.conf
# or: simple.yml.example / simple.json.example
./build/simple-httpd --config simple-httpd.conf --dump-config
./build/simple-httpd --config simple-httpd.conf --root wwwIf you omit --config, the daemon looks for SIMPLE_HTTPD_CONFIG, then ./simple-httpd.conf, ./config/simple-httpd.conf, and /etc/simple-httpd/simple-httpd.conf.
Precedence, later wins: defaults → config file → environment → CLI.
INI, YAML, and JSON accept the same keys. --config picks the parser from the path (.yml / .yaml, .json, otherwise INI).
listen_address = 127.0.0.1
listen_port = 8080
document_root = ./www
directory_listing = true
compression = true
ssl_cert = /etc/simple-httpd/tls/fullchain.pem
ssl_key = /etc/simple-httpd/tls/privkey.pem
hsts = true
csp = default-src 'self'; style-src 'self' 'unsafe-inline'
auth_basic_file = /etc/simple-httpd/htpasswd
rate_limit_enabled = true
rewrite = /blog:/posts
# proxy = /api:127.0.0.1:9000
# cgi_prefix = /cgi-bin
# ssi = true
[vhost:app.example]
document_root = /var/www/app.exampleProfiles:
| Profile | File | Intent |
|---|---|---|
| Simple | config/examples/simple.{conf,yml,json}.example |
HTTP on 127.0.0.1:8080, ./www, terminal logs |
| Advanced | config/examples/advanced.{conf,yml,json}.example |
Virtual hosts, gzip, cache headers, optional TLS |
| Production | config/examples/production.{conf,yml,json}.example |
HTTPS on 443, file logs, rate limits, vhosts |
| Security | config/examples/security.{conf,yml,json}.example |
TLS + HSTS + CSP, tight limits, method filter |
Full key list: docs/configuration.md.
| Flag | Meaning |
|---|---|
-c, --config PATH |
INI, YAML, or JSON config file |
-p, --port PORT |
Listen port (default 8080) |
-a, --address ADDR |
Listen address (default 0.0.0.0) |
-r, --root DIR |
Document root (default ./www) |
--directory-listing |
Autoindex when no index file exists |
--no-keep-alive |
Close after one response |
--tls-cert PATH |
PEM certificate chain (enables HTTPS) |
--tls-key PATH |
PEM private key |
--hsts |
Send Strict-Transport-Security on HTTPS |
--no-compression |
Disable gzip / deflate |
--log-level LEVEL |
debug, info, warn, error |
--access-log PATH |
Access log file, or - for stdout |
--error-log PATH |
Error log file, or - for stderr |
--dump-config |
Print resolved configuration and exit |
-d, --daemonize |
Double-fork to background (Unix) |
-f, --foreground |
Stay in the foreground (default) |
--user NAME |
Drop privileges to user after bind (Unix) |
--group NAME |
Drop privileges to group after bind (Unix) |
--pid-file PATH |
Write PID after a successful bind |
-v, --version |
Print version |
-h, --help |
Usage |
Environment: SIMPLE_HTTPD_CONFIG, SIMPLE_HTTPD_ADDRESS, SIMPLE_HTTPD_PORT, SIMPLE_HTTPD_ROOT, SIMPLE_HTTPD_LOG_LEVEL, SIMPLE_HTTPD_TLS_CERT, SIMPLE_HTTPD_TLS_KEY.
curl -s http://127.0.0.1:8080/healthz # "ok"
curl -s http://127.0.0.1:8080/metrics # Prometheus text
curl -s http://127.0.0.1:8080/status # JSON snapshot/healthz, /metrics, and /status skip HTTP Basic and the rate limiter so probes keep working. Access lines are Apache-style Combined. Details: docs/operations.md.
include/simple-httpd/ public headers
src/simple-httpd/ library
src/main.cpp CLI entry
tests/ ctest binaries
www/ sample document root
config/ example INI, YAML, and JSON profiles
docs/ user documentation
deployment/ systemd, launchd, logrotate, Docker examples
Product version lives in VERSION and is read by CMake. SemVer rules: VERSIONING.md. How to tag: RELEASING.md.
See CONTRIBUTING.md. Short version: C++17, match neighboring files, add tests, keep static files as the default product. Extras (proxy, CGI, SSI) stay opt-in.
Licensed under the Apache License, Version 2.0. See LICENSE.
Copyright 2024 SimpleDaemons.