Skip to main content

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​

ParameterTypeDefaultDescription
allowed_originsArray of Strings[]Restrict rendering to specific origin domains. Empty = allow all
block_private_cidrsBooleantrueBlock requests to private/internal IP ranges (SSRF protection)
rate_limit_per_domainInteger10Maximum render requests per second per target domain
rate_limit_per_ipInteger3Maximum 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/8
  • 172.16.0.0/12
  • 192.168.0.0/16
  • 127.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