Skip to main content

Reverse Proxy Configuration

In production, PRISM sits behind a reverse proxy (nginx, Apache, or Caddy) that handles TLS termination, static assets, and request routing.

warning

Your reverse proxy must strip the X-Prism-Bypass header from incoming requests. This header is used internally by PRISM to prevent render loops. If an external client sends it, they can bypass rendering entirely.

Nginx

:::danger Put every proxy_set_header in the same block

proxy_set_header directives are not merged across levels. A location that defines even one of its own discards all of them from the enclosing server block. Writing the bypass strip at server level and proxy_set_header Connection "" inside location / therefore silently drops the strip, and a client can forge X-Prism-Bypass.

Verified: with the directives split across levels a forged header arrives at the backend intact; with them together it arrives empty. :::

upstream prism {
server 127.0.0.1:4000;
keepalive 32;
}

server {
listen 443 ssl;
http2 on;
server_name example.com;

ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;

# Timeouts — allow time for Chrome rendering
proxy_read_timeout 30s;
proxy_connect_timeout 5s;

location / {
proxy_pass http://prism;
proxy_http_version 1.1;

# Every header this proxy sets must be listed here, in this block.
# Moving any of them to `server` level would take the rest with it.
proxy_set_header Connection "";

# CRITICAL: strip the bypass header so a client cannot forge it
proxy_set_header X-Prism-Bypass "";

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

# Serve static assets straight from the origin, bypassing rendering.
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|wasm|map)$ {
proxy_pass http://your-origin:3000;

# Repeated deliberately: this block has its own proxy_set_header, so it
# inherits nothing from above.
proxy_set_header X-Prism-Bypass "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

Check the strip is working before trusting it:

curl -s -H 'X-Prism-Bypass: forged' https://example.com/ -o /dev/null -w '%{http_code}\n'
# then confirm the origin logged no X-Prism-Bypass value

Apache

Requires proxy, proxy_http, headers and ssl:

a2enmod proxy proxy_http headers ssl # Debian/Ubuntu
<VirtualHost *:443>
ServerName example.com

SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.pem
SSLCertificateKeyFile /etc/ssl/private/example.com.key

# Never act as a forward proxy.
ProxyRequests Off

# CRITICAL: strip the bypass header so a client cannot forge it.
# `early` runs before the proxy hands the request on; without it the unset
# can be evaluated too late to matter on some configurations.
RequestHeader unset X-Prism-Bypass early

ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"

ProxyPass / http://127.0.0.1:4000/
ProxyPassReverse / http://127.0.0.1:4000/

# Allow time for Chrome rendering
ProxyTimeout 30

# Reuse upstream connections
<Proxy http://127.0.0.1:4000/>
ProxySet connectiontimeout=5 timeout=30 keepalive=On
</Proxy>
</VirtualHost>

mod_proxy appends the client address to X-Forwarded-For itself, so it does not need setting; ProxyPreserveHost On passes the original Host.

Caddy

example.com {
# Strip bypass header
request_header -X-Prism-Bypass

reverse_proxy 127.0.0.1:4000 {
header_up X-Forwarded-Proto {scheme}
transport http {
keepalive 30s
keepalive_idle_conns 32
}
}
}

X-Prism-Variant for CDN Cache Keying

When viewport-aware rendering is enabled, PRISM returns an X-Prism-Variant header with the value mobile or desktop. If you have a CDN layer between nginx and clients, add this header to your Vary response or use it as a CDN cache key component.

In nginx, pass it through:

location / {
proxy_pass http://prism;
proxy_http_version 1.1;
proxy_set_header Connection "";

# Pass X-Prism-Variant to CDN for cache keying
add_header Vary "X-Prism-Variant" always;
}

See CDN Integration for CDN-specific configuration.

Trusted Proxies

When PRISM sits behind a reverse proxy, configure trusted_proxies so PRISM only accepts X-Forwarded-Proto from your proxy IPs:

[server]
trusted_proxies = ["127.0.0.1", "10.0.0.0/8", "172.16.0.0/12"]

When empty (the default), all proxies are trusted for backward compatibility.