HTTPS / Reverse Proxy
The default stack binds directly to localhost:4263 with plain HTTP. There is no built-in reverse proxy or TLS termination — bring your own (nginx, Traefik, Tailscale, etc.) for anything beyond your own LAN.
Reverse proxy options
Section titled “Reverse proxy options”If you already run nginx for other services, point a server block at the mc-gamertime container. Run nginx on the host (not in the Compose network) and target the published port:
# /etc/nginx/sites-available/boardsite — LAN / no TLSserver { listen 80; server_name games.local;
client_max_body_size 10m; # image uploads; the app allows up to 10 MB
location / { proxy_pass http://127.0.0.1:4263; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }}The two X-Forwarded-* headers are not optional: the app reads them to see each
visitor’s real IP (login rate limiting) and whether the request arrived over TLS (the
session cookie’s Secure flag). Without them every visitor shares one rate-limit bucket
and the cookie is never marked Secure, even behind HTTPS.
For a public domain with TLS, terminate TLS in nginx (e.g. via certbot --nginx) and proxy the same way:
# /etc/nginx/sites-available/boardsite — public domain, TLS via certbotserver { listen 443 ssl; server_name games.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/games.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/games.yourdomain.com/privkey.pem;
client_max_body_size 10m;
location / { proxy_pass http://127.0.0.1:4263; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }}
server { listen 80; server_name games.yourdomain.com; return 301 https://$host$request_uri;}Run certbot --nginx -d games.yourdomain.com once to obtain the certificate and wire up the 443 block automatically (it edits the file above in place).
Either way, nginx proxies to the host-published port, not the container’s internal one — it runs outside the Compose network. Both are 4263 by default, so they look identical; if you set APP_PORT in .env, use that value here instead.
For a Traefik setup, add labels to the mc-gamertime service via a Compose override file instead of editing docker-compose.yml directly:
services: mc-gamertime: ports: !reset [] # Traefik handles ingress; direct port 4263 not needed labels: - "traefik.enable=true" - "traefik.http.routers.boardsite.rule=Host(`games.yourdomain.com`)" - "traefik.http.routers.boardsite.entrypoints=websecure" - "traefik.http.routers.boardsite.tls.certresolver=letsencrypt" - "traefik.http.services.boardsite.loadbalancer.server.port=4263" networks: - traefik-net
networks: traefik-net: external: trueRun with both files:
docker compose -f docker-compose.yml -f docker-compose.traefik.yml up -dThis assumes Traefik itself is already running (e.g. as its own Compose stack) and joined to the traefik-net external network. The loadbalancer.server.port=4263 label targets the container’s internal port — Traefik reaches mc-gamertime over the Docker network, so !reset [] removes the host-published port the same way.
Run tailscale serve (or a Tailscale sidecar container) pointed at 127.0.0.1:4263. MagicDNS gives you a https://your-machine.tail-scale.ts.net URL with TLS handled automatically — no reverse-proxy config needed on your side.
Behind the proxy
Section titled “Behind the proxy”All three setups above work with the defaults. The app trusts X-Forwarded-For and
X-Forwarded-Proto from private and Docker-internal IP ranges (FORWARDED_ALLOW_IPS, see
Configuration), so:
- login rate limiting sees each visitor’s real IP rather than the proxy’s
- the session cookie gets its
Secureflag when TLS terminates at the proxy, even though the proxy talks plain HTTP to the container — see Security Model → Passwords and sessions