Frontend HTTP behaviour
The public component is a pure static file server. It serves the active published website from a read-only document root. It has no knowledge of Kubernetes, the CMS, databases or publishing — it only ever reads files.
Runtime interface
Use runtime configuration for listener,
cache and timeout settings, and installation for
Kubernetes deployment. The document root is normally /www/current; mount the
published volume rather than pinning one release with Kubernetes subPath.
HTTP behaviour
- Methods:
GETandHEAD. Anything else returns405withAllow: GET, HEAD. - Status codes:
200,206,301(trailing-slash redirect),304,404,405,412,416,501(multipart/byteranges are not implemented),400(malformed paths). - Index files: directory requests serve the first existing configured
index file; a directory requested without a trailing slash redirects
(
301) to the slash form, preserving the query string. - MIME types: explicit overrides for
html, css, js, json, svg, woff2, woff, ico, txt, xml, png, jpg, webp, avif, gif, wasm, source maps, falling back tomime.TypeByExtensionand thenapplication/octet-stream. - Validation: strong ETag from
size-mtime("<size>-<mtimeUnixNano>"), plusLast-Modified. - Conditional requests (RFC 7232 precedence):
If-Match→ 412,If-Unmodified-Since→ 412,If-None-Match→ 304,If-Modified-Since→ 304. - Range requests (RFC 7233): single byte ranges (
bytes=a-b,bytes=a-,bytes=-n),Accept-Ranges: bytes,Content-Range,206for satisfiable ranges,416+Content-Range: bytes */<size>for unsatisfiable ranges,If-Rangesupport (ETag or date). Multi-range requests return501. - Caching:
Cache-Controldefaults topublic, max-age=0, must-revalidate; a separate value can be set for content-hashed assets. - Server identity: every response (including errors, redirects and admin) carries
Server: Viking Frontend <version>from the binary buildinfo. - Security headers: every response (public and admin) carries
X-Content-Type-Options: nosniffandReferrer-Policy: no-referrer.
Admin endpoints (:8081)
GET /healthz—200while the process is up.GET /readyz—200only when the document root resolves to an existing, readable directory; otherwise503. A readable directory without a homepage can be ready while/returns 404.GET /metrics— Prometheus text exposition:http_requests_total{method,code}(counter; bounded label cardinality — no URLs or Host headers; methods are folded to the fixed setGET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS, OTHERso arbitrary method tokens cannot create new series)http_request_duration_seconds(histogram)http_response_size_bytes(histogram)http_requests_in_flight(gauge)
Path safety
- NUL bytes and backslashes are rejected (
400). - Paths are normalized with
path.Clean; traversal (..) cannot escape the document root because the cleaned path is always joined underneath it. - Every path is fully resolved with
filepath.EvalSymlinksand verified to remain inside the resolved document root, so symlinks that point outside the root (or a symlinked index that escapes its directory) are refused with a generic404. 404bodies never leak filesystem details.
Published-directory contract
The root is typically /www/current, a symlink to releases/NNNNNN. The
server re-resolves this symlink with configurable freshness (default 1s), so
an atomic symlink swap to a new release is picked up without a restart, while
in-flight requests finish against the previously resolved release. See publishing.
Operations & security
- No application outbound calls, no shell, no filesystem writes: responses are streamed
with
io.Copy— no temporary files, no writable temp dir required. - Both listeners apply read/write/idle timeouts (defaults above) so a slow or idle client cannot hold a goroutine and an FD indefinitely. FD means an open file descriptor; timeout defaults are in the configuration reference.
- Access logs are JSON on stdout; operational logs are JSON on stderr.
Only the request path (no query), method, status, bytes, duration, protocol
and client address are logged.
X-Forwarded-Foris only trusted when--trust-proxyis set. --trust-proxymust only be enabled when the webserver is reachable exclusively through a trusted reverse proxy (e.g. the cluster ingress) that overwritesX-Forwarded-For; a directly exposed pod would let an attacker spoof the logged client address.- Container: multistage build →
scratch,USER 65532:65532, no CA bundle (no outbound TLS). Pod-level hardening (runAsNonRoot, read-only rootfs, drop all capabilities, seccompRuntimeDefault) is applied by the Deployment. Application behaviour does not enforce network egress policy; configure network egress restrictions through your cluster policy.
Uploaded files
Published CMS uploads at /api/assets/<id>/content use the adjacent immutable
metadata.json content type. Only supported image and document types are
accepted; other or missing types fall back to application/octet-stream.
Documents are delivered as attachments. Existing sites need a new publication
to add this metadata to their release; old retained releases remain readable.
