Security
qaws is intentionally small, but its defaults still matter.
Bind address
Default host is 0.0.0.0, which listens on all interfaces:
qawsFor local-only service, bind loopback:
qaws --host 127.0.0.1 --port 8080Use 0.0.0.0 only when other machines should connect directly.
No TLS
qaws does not terminate TLS. For public HTTPS, put it behind a reverse proxy, CDN, tunnel, or load balancer that handles certificates.
Example local tunnel shape:
qaws --host 127.0.0.1 --port 18086 --serve ./public
cloudflared tunnel --url http://localhost:18086Path traversal protection
qaws normalizes request paths and rejects traversal attempts before opening files.
curl --path-as-is -i http://127.0.0.1:8080/../build.zigExpected result: a rejected request, not the file outside the serve root.
Dotfile policy
Dotfile path segments are denied by default, except .well-known.
Do not put secrets under the public directory. The dotfile policy is a backstop, not a secret-management system.
Protected headers
Custom response headers cannot override runtime-owned headers:
Content-Length
Content-Type
Connection
Server
Allow
Location
ETag
Accept-Ranges
Content-Range
Content-Encoding
VaryThis avoids broken framing, validator, range, and representation metadata from conflicting config.
Request framing
HTTP/1.1 requests require a nonempty Host. qaws rejects conflicting
Content-Length, positive request bodies, all Transfer-Encoding, malformed
framing, and oversized headers. Rejected requests close defensively after an
error response when possible, including malformed requests buffered behind a
valid pipelined request.
Docker exposure
This command exposes qaws only on local loopback:
docker run --rm -p 127.0.0.1:8080:80 ghcr.io/nulldoubt/qaws:0.2.7This command exposes qaws on all host interfaces:
docker run --rm -p 8080:80 ghcr.io/nulldoubt/qaws:0.2.7Choose intentionally.
Privileged ports
Binding port 80 may require elevated privileges or OS-specific capabilities.
For development, prefer a high port:
qaws --port 8080