Security Configuration
The [security] section controls SSRF protection, origin allowlisting, and rate limiting for render requests.
TOML Example
[security]
allowed_origins = []
block_private_cidrs = true
rate_limit_per_domain = 10
rate_limit_per_ip = 3
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
allowed_origins | Array of Strings | [] | Restrict rendering to specific origin domains. Empty = allow all |
block_private_cidrs | Boolean | true | Block requests to private/internal IP ranges (SSRF protection) |
rate_limit_per_domain | Integer | 10 | Maximum render requests per second per target domain |
rate_limit_per_ip | Integer | 3 | Maximum render requests per second per client IP. 0 = disabled |
Detailed Explanation
allowed_origins
When set, PRISM only renders pages whose origin matches one of the listed domains. Requests for other origins are rejected. When empty (default), rendering is allowed for any origin.
Entries are hostnames, not URLs. A scheme or a port never matches, and the
comparison is case-sensitive, so https://example.com and Example.com both
silently match nothing — which blocks every subresource rather than failing
loudly. A leading dot is not needed: each entry also matches its subdomains.
[security]
allowed_origins = ["example.com", "staging.example.com"]
rate_limit_per_ip
A fraction of the per-domain budget, so no single client can spend all of it. Charged only when a render actually happens, not on cache hits.
Size this before upgrading to 1.4.0. Concurrent requests for a URL the
origin refused to a shared cache — anything marked no-cache, private,
no-store or Vary: * — now each render, where one render used to answer all
of them. Sharing a response the origin said not to share was the bug; rendering
separately is the cost of fixing it.
Behind a CDN this compounds: every visitor resolves to the same edge address
unless server.trusted_proxies lists your proxy, so one burst on a no-cache
page can exhaust the budget for the whole site. Set trusted_proxies, raise
this, or both.
block_private_cidrs
Enabled by default. This prevents SSRF (Server-Side Request Forgery) attacks by blocking the headless browser from making requests to private/internal IP ranges:
10.0.0.0/8172.16.0.0/12192.168.0.0/16127.0.0.0/8(loopback)169.254.0.0/16(link-local)::1,fc00::/7(IPv6 private)
PRISM implements this protection at the CDP (Chrome DevTools Protocol) Fetch interception layer for the main page session. It sees that session's intercepted HTTP requests, not only the navigation URL, but that is not every connection Chrome can create.
Hostnames are resolved and classified there too, which is what catches a public name pointing at a private address. Since 1.4.0 a name PRISM cannot resolve is refused rather than waved through: PRISM cannot see what Chrome's own resolver will return, and a host it could not vet is exactly the one it cannot vouch for. The verdict is not cached in that case, so a slow or flaky lookup costs the one subresource in front of it rather than the host for the whole resolution window.
:::caution Network egress policy is required PRISM resolves a name and then Chrome connects separately, so the two can in principle receive different answers. WebSocket, WebRTC, popups and out-of-process iframe subresources can also use browser targets or protocols outside the main Fetch session. Close the boundary at the network and DNS layers: refuse private answers for public names and block private/link-local ranges outright. Treat this setting as defence in depth rather than the boundary. :::
rate_limit_per_domain
Limits how many render requests per second PRISM processes for each target domain. This protects both PRISM and the origin from overload. The default of 10 requests/second per domain is suitable for most deployments.
rate_limit_per_ip
Limits render requests per second from each client IP address. Disabled by default (0). Enable this to protect against individual clients overwhelming the render pipeline.
Example Use Cases
Production lockdown
[security]
allowed_origins = ["www.mysite.com"]
block_private_cidrs = true
rate_limit_per_domain = 20
rate_limit_per_ip = 5
Multi-tenant setup
[security]
allowed_origins = [
"tenant-a.example.com",
"tenant-b.example.com",
"tenant-c.example.com",
]
block_private_cidrs = true
rate_limit_per_domain = 5
rate_limit_per_ip = 2
Development (relaxed)
[security]
allowed_origins = []
block_private_cidrs = false
rate_limit_per_domain = 100
rate_limit_per_ip = 0