Skip to content

Behind a reverse proxy

BoardGameTracker serves plain HTTP on port 5444. Put a reverse proxy in front of it for HTTPS and a friendly hostname, then tell the application two things: which proxy to trust, and which address the outside world uses.

Set TRUSTED_PROXIES to the IP address or network of the proxy. Without it the forwarded headers are ignored: every visitor then shares the proxy’s IP address, so the login limits (10 requests per minute per address, and the lockout described under Authentication) apply to your whole household at once, HTTPS is not detected and the logs show the proxy instead of the client. With Docker, the proxy usually lives in the default bridge range:

docker-compose.yml
environment:
- TRUSTED_PROXIES=172.16.0.0/12

Several entries are separated by commas, semicolons or spaces (TRUSTED_PROXIES=10.0.0.5,172.16.0.0/12). Only IP addresses and networks are accepted: a container or host name such as traefik is ignored and logged as an error at startup, and when no entry is usable the forwarded headers stay off. The application logs a warning the first time forwarded headers arrive while no proxy is trusted.

Set the Public URL under Settings → General (or the PUBLIC_URL environment variable) to the address your users type in the browser, for example https://games.example.com. It is used to build the links in RSVP invitations and password-reset emails; until it is set those emails are not sent. It is never taken from the incoming request for these emails, because anyone can choose the Host header of a password-reset request.

Terminate TLS on the proxy. When TRUSTED_PROXIES is set the application sees the original scheme, sends the Strict-Transport-Security header on HTTPS requests and marks the sign-in cookies Secure.

Single sign-on only works over HTTPS (a localhost address excepted), so it needs this setup:

  • the proxy terminates TLS and sends X-Forwarded-Proto: https (Traefik and Caddy do this by themselves, nginx needs the line in the example below);
  • the proxy passes the original Host header (proxy_set_header Host $host in nginx); X-Forwarded-Host is not read;
  • TRUSTED_PROXIES contains the address the proxy connects from, otherwise the forwarded scheme is ignored;
  • the Public URL starts with https://.

Local password login keeps working over plain HTTP, for example on http://<server-ip>:5444 inside your own network, but the password is sent unencrypted. Anything reachable from outside your network belongs behind the TLS proxy. Raise the proxy’s request body limit for rulebook uploads: PDFs are sent in one request and the application itself accepts up to 1 GB. The examples below allow 100 MB, which is enough for most rulebooks.

docker-compose.yml
labels:
traefik.enable: true
traefik.http.routers.boardgametracker-rtr.entrypoints: websecure
traefik.http.routers.boardgametracker-rtr.rule: Host(`games.example.com`)
traefik.http.routers.boardgametracker-rtr.service: boardgametracker-svc
traefik.http.services.boardgametracker-svc.loadbalancer.server.port: 5444
nginx.conf
server {
listen 443 ssl;
server_name games.example.com;
client_max_body_size 100m;
location / {
proxy_pass http://boardgametracker:5444;
proxy_http_version 1.1;
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;
}
}
Caddyfile
games.example.com {
request_body {
max_size 100MB
}
reverse_proxy boardgametracker:5444
}

Caddy sets the X-Forwarded-* headers by itself.