GioJSdocs
On this page

[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

KeyDefaultDescription
default_headersbooleantrueSend 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.0 / false / empty: The three built-in headers are not sent. Warns.
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.0 / false / empty: An empty value removes that header
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.0 / false / empty: false or "": never sent, even with TLS
hsts.max_ageinteger31536000Seconds browsers remember the policy (one year).
hsts.include_subdomainsbooleanfalseAdds includeSubDomains: every subdomain must serve HTTPS too.
hsts.preloadbooleanfalseAdds 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.0 / false / empty: Unset or "": no policy
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.0 / false / empty: Unset or "": no policy

Behavior

  • The headers are added as each response leaves the server - pages, cache hits, route handlers, static and public/ files, redirects, errors and the /_gio endpoints - 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 getServerSideProps or by a [[headers]] rule. A response header with an empty value removes the default from that response only.
  • X-Powered-By is always removed.
  • While a policy uses {nonce}, every page is answered Cache-Control: private, no-cache without 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-Encoding cannot be checked for the nonce placeholder and is replaced with a 500; let GioJS compress it instead.

Startup warnings

WhenStartup 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 for hsts and csp_report_only).
  • [security] csp: invalid header value for a policy that cannot be a header value (a control character).
  • An unknown key anywhere in the section, an hsts table 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 anyway

Good to know

  • hsts unset and hsts = false differ: unset still sends HSTS while [server.tls] is on; false never does.
  • Header names in [security.headers] are case-insensitive and values are trimmed.
  • Only enable include_subdomains and preload when every subdomain serves HTTPS: browsers keep the policy for max_age seconds.

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.

Version history

VersionChanges
v0.1.0-beta.8Introduced: default security headers, headers, hsts, csp and csp_report_only with per-response nonces, and default_headers, which logs a warning when turned off.