Configuration
qaws configuration is explicit and strict.
- It never auto-loads
qaws.json. - Pass config with
--config <file>. - Unknown keys are errors.
- Invalid types are errors.
- CLI flags override config values.
Validate a file:
qaws check --config qaws.jsonStart with a file:
qaws --config qaws.jsonComplete shape
{
"listen": { "host": "0.0.0.0", "port": 80 },
"serve": "./public",
"daemon": { "enabled": false, "pid_file": null, "log_file": null },
"logging": { "format": "plain", "access": true },
"security": { "dotfiles": "deny_except_well_known" },
"cache": {
"enabled": true,
"max_file_bytes": 262144,
"max_total_bytes": 16777216,
"revalidate_ms": 1000
},
"headers": {
"Cache-Control": "public, max-age=3600"
},
"http": {
"last_modified": true,
"trailing_slash_redirect": true,
"keep_alive": true,
"keep_alive_timeout_ms": 5000,
"max_requests_per_connection": 1000,
"max_connections": 1024,
"workers": 4,
"sendfile": true,
"etag": true,
"range_requests": true,
"precompressed": true
}
}Minimal config
{
"listen": { "host": "127.0.0.1", "port": 8080 },
"serve": "./public"
}listen
| Key | Type | Default | Notes |
|---|---|---|---|
host | string | 0.0.0.0 | Bind address. Use 127.0.0.1 for local-only service. |
port | integer | 80 | TCP port. Low ports may require privileges. |
serve
serve is a top-level string:
{ "serve": "./public" }It must point to an existing directory. In daemon mode, qaws resolves the serve directory before detaching so relative paths do not break after the process changes directory.
daemon
| Key | Type | Default | Notes |
|---|---|---|---|
enabled | boolean | false | Same behavior as -d. |
pid_file | string or null | null | Explicit daemon identity. |
log_file | string or null | null | Explicit daemon log path. |
logging
| Key | Type | Default | Values |
|---|---|---|---|
format | string | plain | plain, jsonl |
access | boolean | true | Enables or disables access logs. |
security
| Key | Type | Default | Values |
|---|---|---|---|
dotfiles | string | deny_except_well_known | deny_except_well_known |
Dotfile path segments are denied by default except .well-known.
cache
| Key | Type | Default | Notes |
|---|---|---|---|
enabled | boolean | true | Enables small-file cache. |
max_file_bytes | integer | 262144 | Maximum cached file size. |
max_total_bytes | integer | 16777216 | Total cached body byte budget. |
revalidate_ms | integer | 1000 | Stat TTL before cache revalidation. |
If the total limit is reached, qaws serves additional files uncached.
headers
headers is an object of custom response headers:
{
"headers": {
"Cache-Control": "public, max-age=3600",
"Cross-Origin-Resource-Policy": "same-origin"
}
}Protected headers are rejected because qaws owns them at runtime:
Content-Length
Content-Type
Connection
Server
Allow
Location
ETag
Accept-Ranges
Content-Range
Content-Encoding
Varyhttp
| Key | Type | Default | Notes |
|---|---|---|---|
last_modified | boolean | true | Enables Last-Modified and If-Modified-Since. |
trailing_slash_redirect | boolean | true | Redirects directories like /docs to /docs/. |
keep_alive | boolean | true | HTTP/1.1 keep-alive support. |
keep_alive_timeout_ms | integer | 5000 | Idle timeout. |
max_requests_per_connection | integer | 1000 | Keep-alive request cap. |
max_connections | integer | 1024 | Active plus queued connection cap. |
workers | integer | automatically detected | Explicit event-worker or fallback-worker count. |
sendfile | boolean | true | Platform sendfile for uncached GET bodies. |
etag | boolean | true | Weak representation ETags and If-None-Match. |
range_requests | boolean | true | Single byte ranges and If-Range. |
precompressed | boolean | true | Brotli/gzip sidecar negotiation. |
Zero or negative-like tuning values are invalid.
Automatic worker selection uses the highest-capacity CPU cluster on
Linux/Android, macOS performance-level CPU information when available, and the
logical CPU count as a portable fallback. --workers remains the explicit
override.