[security]
Default response headers, HSTS and Content-Security-Policy with per-response nonces, stamped by the Rust server on every response.
gio.toml
[security]
hsts = true
csp = "default-src 'self'; script-src 'self' 'nonce-{nonce}' 'strict-dynamic'; style-src 'self' 'unsafe-inline'; object-src 'none'; base-uri 'self'"
[security.headers]
permissions-policy = "camera=(), microphone=(), geolocation=()"Cross-site request protection has its own tables: [security.csrf] and [security.websocket]. The Security guide explains each protection in depth.
Reference
| Key | Default | Description |
|---|---|---|
default_headersboolean | true | Send X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN and Referrer-Policy: strict-origin-when-cross-origin on every response. false drops all three; [security.headers] entries are still sent and HSTS keeps its own setting. Turning them off brings back MIME sniffing of uploads, framing by other sites (clickjacking) and full URLs in cross-origin referrers. |
headerstable | {} | The [security.headers] table: header name to value. A name that is a default replaces it (x-frame-options = "DENY"), an empty value removes it, and any other name is added to every response (permissions-policy, cross-origin-opener-policy, ...). content-security-policy, content-security-policy-report-only and strict-transport-security are refused here: use csp, csp_report_only and hsts. |
hstsboolean | string | table | - | Strict-Transport-Security. Unset: max-age=31536000 only while [server.tls] is on. true: max-age=31536000 on every response (TLS terminated by a proxy). A string is sent as written. A table builds the value from the keys below. |
hsts.max_ageinteger | 31536000 | Seconds browsers remember the policy (one year). |
hsts.include_subdomainsboolean | false | Adds includeSubDomains: every subdomain must serve HTTPS too. |
hsts.preloadboolean | false | Adds preload, for submission to the browsers' preload lists. |
cspstring | - | Content-Security-Policy. Every {nonce} is replaced with a fresh 192-bit nonce per response, which every inline script GioJS writes carries. Line breaks and runs of spaces collapse to one space, so a multi-line TOML string works. |
csp_report_onlystring | - | Content-Security-Policy-Report-Only, same syntax as csp. Browsers report violations without blocking. Both may be set; they share the response's nonce. |
Behavior
- The headers are added as each response leaves the server - pages, cache hits, route handlers, static and
public/files, redirects, errors and the/_gioendpoints - and are never stored in the page cache, so a change to this section reaches cached pages at the next restart. - A header the response already has wins: one set by a route handler, by
getServerSidePropsor by a[[headers]]rule. A response header with an empty value removes the default from that response only. X-Powered-Byis always removed.- While a policy uses
{nonce}, every page is answeredCache-Control: private, no-cachewithout an ETag: a shared cache replaying a page would hand every visitor the same nonce. The page cache itself keeps working - the nonce is substituted into each response, cache hits included. - While nonces are on, a dynamic response that sets its own
Content-Encodingcannot be checked for the nonce placeholder and is replaced with a500; let GioJS compress it instead.
Startup warnings
| When | Startup warning |
|---|---|
default_headers = false | [security] default_headers = false: responses no longer carry x-content-type-options, x-frame-options or referrer-policy (MIME sniffing, clickjacking and full-URL referrers are back) unless [security.headers] sets them |
Startup also logs one security policy line listing the default header names, whether a CSP, report-only CSP and nonces are on, and the CSRF and WebSocket settings.
Errors
These stop startup (and fail --check-config):
[security.headers] "bad name" is not a valid header name, and[security.headers] x-foo: invalid header value.[security.headers] cannot set content-security-policy - use [security] csp instead(likewise forhstsandcsp_report_only).[security] csp: invalid header valuefor a policy that cannot be a header value (a control character).- An unknown key anywhere in the section, an
hststable included:unknown key `security.hsts.preloadd` - did you mean `security.hsts.preload`?
Examples
Let partners frame one section
Keep X-Frame-Options everywhere else, and remove it under /embed with a header rule:
gio.toml
[[headers]]
path = "/embed/*rest"
[headers.headers]
x-frame-options = ""HSTS behind a TLS proxy
gio.toml
[security]
hsts = { max_age = 63072000, include_subdomains = true }Roll out a CSP in report-only mode
gio.toml
[security]
csp_report_only = """
default-src 'self';
script-src 'self' 'nonce-{nonce}' 'strict-dynamic';
style-src 'self' 'unsafe-inline';
object-src 'none'
"""When the browser console stays quiet, rename the key to csp. Inline scripts you write need nonce={cspNonce()}; see cspNonce.
A CDN sets the headers
gio.toml
[security]
default_headers = false # the CDN adds nosniff, frame options and referrer policy
[security.headers]
x-content-type-options = "nosniff" # keep this one from the origin anywayGood to know
hstsunset andhsts = falsediffer: unset still sends HSTS while[server.tls]is on;falsenever does.- Header names in
[security.headers]are case-insensitive and values are trimmed. - Only enable
include_subdomainsandpreloadwhen every subdomain serves HTTPS: browsers keep the policy formax_ageseconds.
Not configurable
- CSP stays opt-in. A policy only you can write (your script and image origins), and nonces make every page private, so CDN caching and ETags are lost while it is on. See Content Security Policy.
- Production errors show only a digest. A failed render answers a generic page with a short error reference; the message and stack go to the server log under the same digest.
- The nonce length (192 bits) and the removal of
X-Powered-By.
Related
- Security guide
[security.csrf]and[security.websocket][[headers]]- per-path response headers- Content Security Policy and
cspNonce - Turning Protections On and Off
Version history
| Version | Changes |
|---|---|
v0.1.0-beta.8 | Introduced: default security headers, headers, hsts, csp and csp_report_only with per-response nonces, and default_headers, which logs a warning when turned off. |