Skip to content

http

Key Type Default
http.access_logging.*
http.access_logging.level string info
http.access_logging.log_user_agent bool true
http.access_logging.log_request_header bool true
http.access_logging.log_client_ip bool true
http.access_logging.log_request_id bool false
http.access_logging.log_request_body bool false
http.access_logging.log_response_body bool false
http.access_logging.log_response_header bool false
http.access_logging.log_span_id bool false
http.access_logging.log_trace_id bool false
http.access_logging.log_json bool false
http.address string 127.0.0.1
http.port int 8080
http.unix_socket string empty
http.proxy_protocol list empty
http.trusted_proxies list empty
http.public_url string empty
http.cookie_key string empty
http.cookie_same_site string strict
http.cookie_max_age duration 9h
http.cookie_idle_timeout duration 30m
http.rate_limit int 60
http.rate_duration duration 1m
http.rate_limit_disable_for_dev bool false
http.cert_request_rate_limit.user number 10
http.cert_request_rate_limit.service_enroll number 5
http.cert_request_rate_limit.pam number 10
http.cert_request_rate_limit.console number 10
http.service_code_rate_limit.limit number 1
http.console_code_rate_limit.limit number 1
http.console_code_rate_limit.burst int 5
http.hsts string max-age=31536000; includeSubDomains
http.tls.certificate_file string empty
http.tls.private_key_file string empty
http.tls.cipher_suites list empty
http.tls.min_version string TLS1.3
http.tls.curves list empty
http.tls.reload_interval duration 0

string, default 127.0.0.1

The network interface to bind to, e.g. “127.0.0.1” to listen only locally, or “0.0.0.0” for all interfaces. Used with Port to form the server’s listen address.

http:
address: "127.0.0.1"

int, default 8080

The TCP port to listen on, e.g. 80 for HTTP or 443 for HTTPS. Port 0 is reserved for testing and tells the OS to pick an unused port; the actual port is logged on startup. Ignored when unix_socket is set.

http:
port: 8080

string, default empty

when set, is a filesystem path to listen on a Unix domain socket instead of TCP: Address and Port are ignored. Mutually exclusive with proxy_protocol (PROXY protocol is a TCP-connection concept; it has nothing to prefix on a Unix socket).

Every peer on the socket is trusted to name the client, whatever trusted_proxies says, and the client IP is the rightmost X-Forwarded-For entry (or X-Real-IP). The proxy in front must append the address it saw (nginx $proxy_add_x_forwarded_for): one that passes the client’s own header on untouched lets the client choose the address that source_policy, allowed_networks, rate limits and the audit log use, and one that sends neither header leaves every request with no client address. Keep the socket writable only by the proxy.

http:
unix_socket: ""

list, default empty

Lists the CIDR ranges of reverse proxies trusted to prefix connections with a PROXY protocol v1/v2 header (so the real client address survives a TCP-level proxy, as opposed to trusted_proxies below which is for HTTP-level proxies setting X-Forwarded-For). Empty disables PROXY protocol support entirely; connections from any other source are rejected outright once this is set. Ignored (must be empty) when unix_socket is set.

http:
proxy_protocol: []

list, default empty

Lists the CIDR ranges of reverse proxies trusted to set X-Forwarded-For/X-Forwarded-Proto, passed to gin’s SetTrustedProxies. Empty means no proxy is trusted, so c.ClientIP() always reports the direct connection’s address and a client-supplied X-Forwarded-For is ignored.

That only holds because the router passes this to SetTrustedProxies unconditionally — gin’s own default is to trust every proxy, so skipping the call for an empty list would mean the opposite.

Keep it empty or limited to the real proxies: a peer inside it picks its own client IP with X-Forwarded-For, so it passes nss.source_allow_list, forges the audited source_ip and gets a fresh nss.rate_limit bucket per address. On http.unix_socket gin trusts every peer whatever this says, so the proxy in front must set X-Forwarded-For itself.

http:
trusted_proxies: []

string, default empty

The scheme and host browsers actually reach this deployment at, e.g. “https://ssh.example.com”. Required: it is the one place the browser-visible identity of this deployment is written down, and it will differ from what Address/Port/TLS describe in every reverse-proxy deployment, since the proxy terminates TLS on 443 while this process listens on plain HTTP somewhere else.

Everything that has to know how the browser sees this server is derived from it: the OIDC redirect URI handed to the identity provider, the origin the CSRF middleware compares the browser’s Origin header against, the host name requests must be addressed to (anything else is rejected with 421 Misdirected Request; health endpoints are exempt so probes can reach the server by IP), and whether the deployment is HTTPS at all, which decides the session cookie’s Secure attribute.

Origin only: a path, query, or fragment is rejected at startup. Serving the app under a sub-path would need the frontend’s base to move with it, which is not supported, so accepting one here would only produce a redirect URI that silently does not work.

http:
public_url: ""

string, default empty

The secret used to sign and encrypt session cookies. If empty, a key is generated once and persisted in the server_secrets table, so sessions survive a restart and instances sharing a database share the key. Configure an explicit value to key it from outside the database.

http:
cookie_key: ""

string, default strict

Controls the session cookie’s SameSite attribute: “strict” (default), “lax”, or “none”. Strict is right for this server because nothing legitimately navigates into it from another site — the OIDC callback is a redirect the browser follows to a URL this server issued, not a cross-site form post.

This is defence in depth, not the CSRF control: SameSite does nothing against an attacker page on a different origin of the same site, so state-changing routes are additionally guarded by the CSRF middleware.

http:
cookie_same_site: "strict"

duration, default 9h

The absolute ceiling on a session’s lifetime, measured from login. Activity does not extend it: once a session is this old the next request is unauthenticated regardless of how recently it was used, and the user signs in again. Zero uses the built-in default.

http:
cookie_max_age: 9h

duration, default 30m

How long a session survives without a request. SessionAuthMiddleware slides this window on activity (re-saving the session past half the window, which reissues the cookie), so an actively-used session only ever ends at cookie_max_age, while an abandoned browser expires after this much quiet. Zero uses the built-in default. Must not exceed cookie_max_age - an idle window longer than the absolute cap cannot ever be reached.

http:
cookie_idle_timeout: 30m

int, default 60

The maximum number of requests per rate_duration allowed per client IP. Zero or negative disables rate limiting entirely. Localhost (127.0.0.1, ::1) bypasses the limit, so health probes and tests from the local frontend are not throttled.

Two kinds of path are not counted against this bucket: the NSS lookups (carried by their own per-fleet-key limiter) and the content-hashed frontend assets under /_app/immutable/. The assets are immutable static files with no database or audit cost, and a single cold page load pulls dozens of them, which would otherwise exhaust the burst for a user who is only browsing. Flooding those paths is the reverse proxy’s or firewall’s job, not this limit’s.

Keep it on for an internet-facing deployment. Some requests write an audit event without a session, such as a console request from outside cert_options.console.allowed_networks, and this limit is what stops one address writing them as fast as it can send.

http:
rate_limit: 60

duration, default 1m

The time window for rate limiting calculations. With rate_limit=60 and rate_duration=1m, each IP is allowed 60 requests per minute. Typical values are in seconds to minutes.

http:
rate_duration: 1m

bool, default false

Disables all rate limiting (global and per-endpoint) when true. Only effective when Production=false, so it cannot be silently enabled in production. Used for local development to avoid throttling during testing.

http:
rate_limit_disable_for_dev: false

Holds the rate limit configuration for service enrollment code redemption, keyed on the enrollment code to protect against brute-forcing. Zero or negative disables the specific limit.

number, default 1

Requests per second per enrollment code. Zero or negative disables this limit.

http:
service_code_rate_limit:
limit: 1

Holds the rate limit configuration for console user-code submission, keyed on the submitting session and its source address to protect against brute-forcing.

Keyed on the submitting session rather than the source address, and on the address as well, so a single compromised account cannot grind through the code space from many addresses and a single address cannot do it across many accounts. At 40 bits with a handful of live codes even a generous limit leaves an infeasible search; this exists to make that margin independent of how many requests happen to be pending.

number, default 1

Submissions per second per session and per source address. Zero or negative disables this limit.

http:
console_code_rate_limit:
limit: 1

int, default 5

How many submissions may arrive back to back before the per-second rate starts holding them back. Small on purpose: a human typing a code they misread retries once or twice, not ten times.

http:
console_code_rate_limit:
burst: 5

string, default max-age=31536000; includeSubDomains

(HTTP Strict-Transport-Security) is sent in the Strict-Transport-Security header on every response, /healthz and /ping included, whether this process terminates TLS itself or a reverse proxy in front does. Browsers ignore HSTS over a connection they see as plain HTTP, so sending it behind a proxy is harmless. Empty disables the header, useful when a TLS-terminating reverse proxy in front sets its own policy. Typical values include “max-age=31536000; includeSubDomains”.

http:
hsts: "max-age=31536000; includeSubDomains"