Security
The default middleware stack ships three security layers, safe-by-default
and each overridable through config. Combined with
CSRF protection and the signed session cookie, a
generated project is hardened without writing a line of it yourself.
Security headers
Altair::Middleware::SecurityHeaders stamps conservative headers on every
response:
X-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINReferrer-Policy: strict-origin-when-cross-origin
Each header is only written when the application did not set it itself.
The set is driven by config.security_headers, so it is a plain hash you
can extend or replace:
config.security_headers["X-Content-Type-Options"] = "nosniff"
config.security_headers["Content-Security-Policy"] = "default-src 'self'"
Set the hash to {} of String => String to disable the layer entirely.
Request ids
Altair::Middleware::RequestId gives every request an identifier you can
carry end-to-end:
- An inbound
X-Request-Idheader is honored (echoed back, not replaced). - Otherwise a fresh UUID is generated.
- The value is exposed as
request.request_id, echoed back on the response header, and appended to the request log line — so a log entry and the response header correlate across distributed traces.
The header name is configurable via config.request_id_header:
config.request_id_header = "X-Correlation-Id"
CORS
Altair::Middleware::Cors is a pass-through until you opt in by naming
the origins you trust. Fill config.cors.origins and it stamps
Access-Control-Allow-* on permitted requests and answers preflight
OPTIONS directly (methods, headers, credentials, max age):
config.cors.origins = ["https://app.example.com"]
config.cors.credentials = true # admit cookies cross-origin
config.cors.max_age = 3600 # seconds, preflight caching
Or allow any origin:
config.cors.origins = ["*"]
Notes:
- Origins are exact-matched against the
Originheader;"*"grants any. - With
credentials = trueand a"*"entry, the exact origin is echoed instead of the wildcard — browsers refuse wildcard-plus-credentials. config.cors.methodsandconfig.cors.headerstailor the preflight answer; requested headers are honored when the client names extra ones.- Combine CORS with the framework's signed session cookie, which carries
SameSite=Laxby default, to keep cross-origin sessions honest.
The full stack
The default config.middleware runs, in order:
| Layer | Job |
|---|---|
Logger |
Request logging with the request id appended |
RequestId |
Assign and echo a request identifier |
SecurityHeaders |
Stamp safe-by-default response headers |
Cors |
Opt-in cross-origin support (pass-through by default) |
Static |
Serve public/ files as-is |
See Configuration for building your own stack.