Reverse proxies
PassBeyond expects a public TLS reverse proxy in front of its private HTTP listener. The edge owns certificates, HTTP-to-HTTPS redirects, request-size and abuse controls, and trusted forwarding headers.
Internet → public TLS proxy → private PassBeyond → private backendThe safe design has no alternate public path to PassBeyond or the backend.
Required behavior
The edge proxy must:
- route every path for the protected hostname to PassBeyond;
- preserve the original public
Host; - overwrite
X-Forwarded-For,X-Real-IP, andX-Forwarded-Proto; - remove or overwrite client-supplied forwarding values;
- redirect plain HTTP to HTTPS;
- enforce suitable request-body, header, connection, and rate limits;
- support SAML POST requests to
/saml/acs; - avoid caching authentication responses and error pages.
PassBeyond independently overwrites X-Forwarded-For and X-Real-IP before proxying to the backend. Other forwarding headers, including X-Forwarded-Proto, remain the edge proxy's responsibility.
Match trustedProxies
Only list the direct peer addresses that are allowed to supply X-Forwarded-For:
proxy:
listenAddress: "127.0.0.1:8123"
targetURL: "http://127.0.0.1:8888"
trustedProxies:
- "127.0.0.1"
- "::1"For containers, use the exact proxy container address or a narrow, dedicated network CIDR. Do not trust an entire shared infrastructure range when unrelated workloads can connect to PassBeyond.
nginx
Define the WebSocket connection map once in the http context:
map $http_upgrade $passbeyond_connection {
default upgrade;
'' close;
}Then configure the virtual host:
upstream passbeyond_myapp {
server 127.0.0.1:8123;
keepalive 32;
}
server {
listen 80;
listen [::]:80;
server_name myapp.example.com;
return 307 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name myapp.example.com;
ssl_certificate /etc/letsencrypt/live/myapp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.example.com/privkey.pem;
# Configure HSTS only after HTTPS is verified for the whole domain.
add_header Strict-Transport-Security "max-age=31536000" always;
location / {
proxy_pass http://passbeyond_myapp;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Proxy "";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $passbeyond_connection;
proxy_buffering off;
proxy_redirect off;
client_max_body_size 16m;
}
}Choose client_max_body_size for the application, but remember that PassBeyond itself has a fixed 10-second request read timeout. See Runtime limits.
Rate limiting with nginx
Define a conservative zone in the http context and apply it to the public server according to expected traffic:
limit_req_zone $binary_remote_addr zone=passbeyond_auth:10m rate=5r/s;location /saml/ {
limit_req zone=passbeyond_auth burst=20 nodelay;
proxy_pass http://passbeyond_myapp;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
}When using a separate /saml/ location, repeat every required proxy header and transport option. Tune rates to the IdP and user population; an overly strict rule can block a legitimate login burst.
Apache HTTP Server
Enable the required modules:
sudo a2enmod ssl proxy proxy_http headers rewrite
sudo systemctl reload apache2Example virtual hosts:
<VirtualHost *:80>
ServerName myapp.example.com
Redirect permanent / https://myapp.example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName myapp.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/myapp.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/myapp.example.com/privkey.pem
ProxyPreserveHost On
ProxyRequests Off
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Host "%{HTTP_HOST}s"
RequestHeader set X-Forwarded-For expr=%{REMOTE_ADDR}
RequestHeader set X-Real-IP expr=%{REMOTE_ADDR}
RequestHeader unset Proxy
ProxyPass / http://127.0.0.1:8123/ connectiontimeout=5 timeout=35
ProxyPassReverse / http://127.0.0.1:8123/
</VirtualHost>The exact RequestHeader expression syntax depends on the Apache 2.4 build. Verify the resulting headers at a controlled backend before production.
For WebSockets, enable proxy_wstunnel where required. PassBeyond's fixed response timeout still applies.
Caddy
Caddy obtains and renews the public certificate by default:
myapp.example.com {
reverse_proxy 127.0.0.1:8123 {
header_up Host {host}
header_up X-Forwarded-Host {host}
header_up X-Forwarded-Proto {scheme}
header_up X-Forwarded-For {remote_host}
header_up X-Real-IP {remote_host}
header_up -Proxy
}
}Caddy normally manages X-Forwarded-* headers itself; the explicit directives make the trust behavior visible. Confirm the direct Caddy address matches trustedProxies.
Container edge proxy
On a container network:
- do not publish PassBeyond or backend ports to all host interfaces;
- place the edge proxy and PassBeyond on a dedicated network;
- keep the backend on an internal network;
- trust only the edge address or narrow subnet;
- ensure network recreation does not silently widen the trusted range.
See Container deployment for a topology example.
Backend TLS
For an HTTPS targetURL, PassBeyond verifies the backend certificate. Supply the issuing CA in the host or container trust store. Keep targetDisableSSLVerify: false.
The edge TLS certificate and backend TLS certificate protect different connections. A valid public certificate does not make an unverified internal backend connection safe.
Header trust checklist
- [ ] Clients cannot reach PassBeyond or the backend directly.
- [ ] The edge overwrites all forwarding headers.
- [ ]
Hostremains the expected public application hostname. - [ ]
trustedProxiescontains only direct edge peers. - [ ] The backend trusts identity headers only from PassBeyond.
- [ ] Basic authorization behavior matches
useBasicAuthandignoreAuthorizationHeader. - [ ] Request and rate limits have been tested with SAML POSTs and normal application traffic.
- [ ] WebSocket, upload, and long-response behavior has been tested against PassBeyond's fixed limits.
For header details, see Forwarded headers. For failures, see Troubleshooting.