Skip to main content
BlindCast serves HTTP only. In production, put a reverse proxy in front of it to handle TLS termination, admin access control, and rate limiting.

Why use a reverse proxy

  • TLS termination — BlindCast doesn’t handle HTTPS. Your proxy terminates TLS and forwards HTTP to BlindCast.
  • Admin protection — The /admin dashboard serves static files without authentication. A proxy can gate it behind SSO, HTTP Basic Auth, or IP allowlists.
  • Rate limiting — Protect key derivation (/keys) and API endpoints from abuse.
  • Setup endpoint security — Block POST /api/v1/setup from the internet to prevent unauthorized admin key creation.

nginx

Quick start with Docker Compose

The server ships with a proxy overlay that adds nginx in front of BlindCast:
This does two things:
  1. Adds an nginx service on port 80 that proxies to BlindCast
  2. Sets TRUST_PROXY=1 on BlindCast so Express reads real client IPs from the forwarded header (trusts exactly one proxy hop)
In production, use a firewall rule to block direct access to port 4100 so all traffic flows through nginx.
The setup endpoint (POST /api/v1/setup) is blocked by default in the proxy config. Set ADMIN_API_KEY as an environment variable instead. See Setup wizard security.

Configuration reference

The proxy config is at docker/nginx-proxy.conf. Key sections: Forwarded headers — Every proxied route sends X-Forwarded-For, X-Forwarded-Proto, X-Real-IP, and Host to BlindCast:
Route breakdown: CORS headers — The proxy does not add CORS headers. Express handles CORS for all proxied routes. Adding CORS at both layers causes duplicate Access-Control-Allow-Origin headers, which browsers reject.

Protecting /admin with auth_request

Use nginx’s auth_request to gate the admin dashboard behind an external auth provider (Auth0, Okta, Azure AD via oauth2-proxy, etc.):
  1. Uncomment the auth_request lines in nginx-proxy.conf:
  1. Uncomment and configure the auth verification endpoint:
  1. Add your auth backend (e.g., oauth2-proxy) as a Docker service alongside nginx.

Protecting /admin with HTTP Basic Auth

For simple deployments, use nginx’s built-in basic auth:
Generate the password file:

Rate limiting

Uncomment the limit_req_zone directives at the top of nginx-proxy.conf:
Then uncomment limit_req in the relevant location blocks:

TLS termination

The config includes a commented-out TLS server block. Uncomment it and provide your certificate paths:
Mount certificates in the compose override:
For Let’s Encrypt, use certbot or a sidecar like nginx-proxy-acme.

Caddy

Caddy handles TLS automatically with Let’s Encrypt. A minimal Caddyfile:

Forward auth for /admin

Use Caddy’s forward_auth to protect the admin dashboard:

Blocking the setup endpoint

AWS ALB / Cloud Load Balancers

For AWS ALB, GCP Cloud Load Balancing, or Cloudflare:
  1. Target group — Point to BlindCast container on port 4100. Use /health for health checks.
  2. Listener rules — Route all paths to the BlindCast target group.
  3. Admin protection — Use the load balancer’s built-in auth integration:
  4. Set TRUST_PROXY — Cloud load balancers add their own X-Forwarded-For headers. Set TRUST_PROXY=true on the BlindCast container.

TRUST_PROXY environment variable

When BlindCast runs behind a proxy, set TRUST_PROXY so Express reads the real client IP from forwarded headers.
Prefer a numeric hop count (TRUST_PROXY=1) over true when the proxy topology is known. With true, Express trusts all entries in X-Forwarded-For, so a client can prepend a spoofed IP. With 1, Express only trusts the entry added by the immediate proxy. Never set any TRUST_PROXY value unless BlindCast is actually behind a proxy.

Setup wizard security

The setup endpoint (POST /api/v1/setup) creates the first admin API key. It has no authentication — it only works when zero API keys exist in the database. This creates a race condition: if the server is network-accessible before you run setup, an attacker could claim the admin key first. Set the ADMIN_API_KEY environment variable to bootstrap with a known key. This skips the setup wizard entirely and eliminates the race condition:
The bootstrap key works immediately and has admin scope. Store it in a secret manager (AWS Secrets Manager, HashiCorp Vault, Doppler).

Alternative: temporarily unblock the setup endpoint

If you prefer the setup wizard:
  1. Uncomment proxy_pass in the /api/v1/setup location block
  2. Run docker compose up and complete setup at /admin
  3. Re-block the endpoint by commenting out proxy_pass and reloading nginx:

Next steps