Skip to content

Latest commit

 

History

History
283 lines (209 loc) · 13.3 KB

File metadata and controls

283 lines (209 loc) · 13.3 KB

Configuration

simple-httpd reads INI, YAML (.yml / .yaml), or JSON. --config chooses the parser from the file path. Unknown keys are ignored.

INI section names other than [vhost:hostname] are ignored; keys are global. YAML and JSON use a virtual_hosts array instead. Comments: # / ; in INI, # in YAML. JSON has no comments.

Precedence, later wins:

  1. Compiled defaults
  2. Config file (--config, SIMPLE_HTTPD_CONFIG, or a well-known path)
  3. Environment (SIMPLE_HTTPD_*)
  4. Command-line flags
./build/simple-httpd --config config/examples/simple.conf.example --dump-config
./build/simple-httpd --config config/examples/simple.yml.example --dump-config
./build/simple-httpd --config config/examples/simple.json.example --dump-config

--dump-config prints the resolved values after file + env + CLI. Use it before starting a production listener.

Profiles

Copy a file from config/examples/ (.conf.example, .yml.example, or .json.example), edit paths, then pass --config.

File Use when
simple (yml, json) Local HTTP, ./www, logs on the terminal
advanced (yml, json) Several Host names, gzip, cache, optional TLS
production (yml, json) HTTPS on 443, file logs, rate limits, vhosts
security (yml, json) Tight timeouts, TLS + HSTS + CSP, method filter
htpasswd.example user:password lines for auth_basic_file

config/simple-httpd.conf.example matches the simple profile. See config/README.md.

TLS starts only when both ssl_cert and ssl_key are set. The files must exist by the time start() runs.

Listener and site

Key Default Meaning
listen_address 0.0.0.0 Bind address (127.0.0.1, ::, …)
listen_port 8080 Bind port. 0 means ephemeral (tests)
document_root ./www Default site root
index_files index.html, index.htm Tried in order for a directory
directory_listing false HTML autoindex when no index file exists
follow_symlinks false If false, a symlink target is refused
server_name simple-httpd/<version> Server response header

Virtual hosts: a section [vhost:app.example] with document_root = …. The name is matched against the request Host header (port stripped, case-insensitive). Unknown hosts use the default document_root.

document_root = /var/www/html

[vhost:app.example]
document_root = /var/www/app.example

YAML / JSON use a list instead:

virtual_hosts:
  - name: app.example
    document_root: /var/www/app.example
"virtual_hosts": [
  { "name": "app.example", "document_root": "/var/www/app.example" }
]

The process still has one listen socket. HTTPS, if enabled, applies to that socket for every host. Per-vhost certificates are not supported.

Connections and limits

Key Default Meaning
worker_threads 4 Size of the connection worker pool
http2 true Speak HTTP/2 (h2c prior knowledge, TLS ALPN h2) when the build has nghttp2
max_connections 128 Concurrent accepted connections
request_timeout_seconds 30 Read timeout while parsing a request
keep_alive true Honor HTTP/1.1 persistent connections
keep_alive_timeout_seconds 5 Idle timeout between keep-alive requests
max_request_size 65536 Max bytes for request line + headers + body
max_header_count 100 Max header fields

POST / PUT bodies are parsed when framed with Content-Length or Transfer-Encoding: chunked. Static files still hit the method allow-list (default: 405). Proxy, CGI, and FastCGI skip the allow-list so backends can accept POST.

TLS

Key Default Meaning
ssl_cert / tls_cert / cert empty PEM certificate chain
ssl_key / tls_key / key empty PEM private key
hsts false Send Strict-Transport-Security (HTTPS only)
hsts_max_age 31536000 HSTS max-age in seconds

CLI: --tls-cert, --tls-key, --hsts. Env: SIMPLE_HTTPD_TLS_CERT, SIMPLE_HTTPD_TLS_KEY.

Protocol floor is TLS 1.2. Cipher selection is OpenSSL's server defaults for this build. When HTTP/2 is enabled, ALPN prefers h2 then http/1.1. The server SSL session cache is on.

Compression and cache

Key Default Meaning
compression / gzip true Compress when zlib is linked and the client sends Accept-Encoding
compression_min_length 256 Skip compression below this body size
cache_max_age 0 If > 0, send Cache-Control: public, max-age=N
file_cache true Keep small static bodies in memory
file_cache_max_bytes 2097152 Cap for the whole cache
file_cache_max_entry 262144 Skip files larger than this
file_cache_ttl_seconds 60 Drop an entry after this many seconds
mime_type / mime_types empty Extra mappings .ext:type (comma-separated)

--no-compression turns compression off. gzip is preferred when the client offers it; otherwise deflate. Types that compress are the usual text / JSON / JS / SVG set; already-compressed formats are left alone. The file cache keys on path + size + mtime and is skipped for range responses.

mime_type = .wasm:application/wasm, .foo:text/x-foo

Optional extras

These are off or unused unless you set them. The product is still a static file server; CGI, FastCGI, SSI, and reverse proxy are extra surface area.

Key Default Meaning
ssi false Expand <!--#include --> / <!--#echo --> in HTML
cgi_prefix empty URL prefix whose files are executed as CGI
cgi_timeout_seconds 10 Kill CGI / FastCGI after this many seconds
fastcgi empty Prefix → TCP FastCGI backend (/app:127.0.0.1:9000)
proxy empty Prefix → HTTP backends (/api:127.0.0.1:9000,127.0.0.1:9001)
proxy_timeout_seconds 30 Upstream read/connect timeout
proxy_max_body 1048576 Max proxied response body

Reverse proxy and load balancing

proxy = /api:127.0.0.1:9000,127.0.0.1:9001; /other:10.0.0.2:80

Comma-separated hosts on one rule are round-robin. A connect failure tries the next backend. Several rules are separated with ;. YAML/JSON may use a list of the same strings (do not join them with commas — that would smash backends together). Backends are HTTP only (http:// optional). https:// origins are rejected. Hop-by-hop headers are stripped. WebSocket Upgrade is tunneled on HTTP/1.1 only (not HTTP/2).

CGI

cgi_prefix = /cgi-bin
allow_methods = GET, HEAD, OPTIONS, POST

The path under the prefix must map to an executable file inside the document root. The daemon forks (POSIX) or CreateProcess (Windows), sets a CGI environment, and parses Status / Content-Type from the script's stdout. <!--#exec --> in SSI is not CGI.

FastCGI

fastcgi = /app:127.0.0.1:9000

Minimal FastCGI client over TCP. There is no unix-socket helper, no php-fpm supervisor, and no connection pool. Run the application process yourself.

SSI (templates)

ssi = true

HTML (text/html) responses expand:

  • <!--#include virtual="/frag.html" --> or <!--#include file="frag.html" --> (confined to the document root, depth 3)
  • <!--#echo var="DATE_GMT" -->, DOCUMENT_URI, DOCUMENT_NAME, QUERY_STRING

<!--#exec --> is refused. There is no other template engine.

Built-in endpoints

/healthz, /metrics, and /status are the ops handlers. Optional auth-gated {admin_path}/config and {admin_path}/reload are the management surface. There is no GraphQL or application plugin router.

Security

Key Default Meaning
security_headers true X-Frame-Options, Referrer-Policy, Permissions-Policy
x_frame_options DENY Value for X-Frame-Options
referrer_policy strict-origin-when-cross-origin Referrer-Policy
csp empty Content-Security-Policy; omitted when empty
allow_methods GET, HEAD, OPTIONS Methods that reach static files
auth_basic_realm simple-httpd WWW-Authenticate realm
auth_basic_file empty user:password file; empty disables auth
rate_limit_enabled false Per-IP request window
rate_limit_requests 120 Max requests per window
rate_limit_window_seconds 60 Window length
allow_ips empty Comma-separated CIDRs; empty = all (after deny)
deny_ips empty Comma-separated CIDRs; deny wins
max_connections_per_ip 0 Concurrent connections per IP (0 = unlimited)
connection_rate_limit_enabled false Cap new accepts per IP
connection_rate_limit 60 Max new connections per window
connection_rate_window_seconds 60 Connection-rate window
security_log empty Security audit file (- or empty → stderr with errors)
tls_min_version 1.2 1.2 or 1.3 (floor; 1.3 preferred when available)
admin_path empty Base path for management API (requires auth_basic_file)
user / group empty Drop privileges after bind (Unix)
daemonize false Double-fork to background (Unix; prefer systemd)
pid_file empty Write PID after bind

X-Content-Type-Options: nosniff is always sent on responses from the static handler.

Directory listing injects inline CSS. A CSP of default-src 'self' will blank the listing styles unless you add style-src 'self' 'unsafe-inline' (the security profile does this).

Auth file format: one user:password per line, # comments, no hashing. chmod 600. If the file is set but missing, unreadable, or empty of users, the server refuses to start.

/healthz, /metrics, and /status skip auth and request rate limits. Admin endpoints never skip Basic auth.

Rewrite:

rewrite = /blog:/posts, /old:/index.html

Prefix match: /blog becomes /posts; /blog/a becomes /posts/a. A longer path that only shares a prefix (/blogging) is not rewritten. First matching rule wins.

Operations endpoints

Key Default Meaning
health_path /healthz Plain-text ok\n (empty string disables)
metrics_path /metrics Prometheus text (empty string disables)
status_path /status JSON process snapshot (empty string disables)
admin_path empty Management base path; empty disables
tcp_nodelay true Set TCP_NODELAY on accepted sockets
sendfile true Prefer kernel sendfile for cleartext static bodies

/healthz, /metrics, and /status skip auth and request rate limits. When admin_path is set (e.g. /admin), these require Basic auth (auth_basic_file mandatory):

Method Path Action
GET {admin_path}/config Dump resolved configuration (plaintext)
POST {admin_path}/reload Request a soft reload (same as SIGHUP)

Logging

Key Default Meaning
log_level info debug, info, warn, error
access_log - Path, or - for stdout
error_log - Path, or - for stderr
security_log empty Security events; empty/- shares stderr with errors

Access lines look like Combined Log Format with RFC 1123 dates in GMT. Security lines are TIMESTAMP SECURITY event=… detail….

Command line

See the README table. Flags only override a key when that flag is actually passed. An empty CLI does not reset access_log or TLS paths loaded from the file.

Environment

Variable Sets
SIMPLE_HTTPD_CONFIG Default config path when --config is omitted
SIMPLE_HTTPD_ADDRESS listen_address
SIMPLE_HTTPD_PORT listen_port
SIMPLE_HTTPD_ROOT document_root
SIMPLE_HTTPD_LOG_LEVEL log_level
SIMPLE_HTTPD_TLS_CERT ssl_cert
SIMPLE_HTTPD_TLS_KEY ssl_key

Validation

validate() fails when:

  • listen_address or document_root is empty
  • worker_threads or max_connections is 0
  • max_request_size is under 1024
  • index_files or allow_methods is empty
  • only one of ssl_cert / ssl_key is set
  • TLS is configured in a build compiled without OpenSSL
  • a virtual host is missing name or document_root
  • rate limiting is on and rate_limit_requests is 0
  • connection rate limiting is on and connection_rate_limit is 0
  • allow_ips / deny_ips is set but has no valid CIDR entries
  • tls_min_version is not 1.2 / 1.3
  • admin_path is set without auth_basic_file, or does not start with /

At process start, the default document root and every vhost root must exist as filesystem paths.