Skip to main content

Reverse Proxy Recipes

The CourseMaker app container binds to 127.0.0.1:${APP_HOST_PORT} (default 4210). It speaks plain HTTP. You must put a TLS terminator in front of it. Three working recipes below.

Caddy fetches and renews Let's Encrypt certificates automatically.

/etc/caddy/Caddyfile:

learn.client.com {
encode zstd gzip

# Bunny TUS uploads can exceed the default 10 MB limit
request_body {
max_size 5GB
}

reverse_proxy 127.0.0.1:4210 {
header_up X-Forwarded-Proto https
header_up X-Forwarded-Host {host}
}
}
sudo systemctl reload caddy

Nginx + Certbot

/etc/nginx/sites-available/coursemaker:

server {
listen 80;
server_name learn.client.com;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl http2;
server_name learn.client.com;

ssl_certificate /etc/letsencrypt/live/learn.client.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/learn.client.com/privkey.pem;

client_max_body_size 5G;
proxy_read_timeout 600s;
proxy_send_timeout 600s;

location / {
proxy_pass http://127.0.0.1:4210;
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 https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
sudo ln -s /etc/nginx/sites-available/coursemaker /etc/nginx/sites-enabled/
sudo certbot --nginx -d learn.client.com
sudo systemctl reload nginx

Cloudflare Tunnel

If the server has no public IP (NATted home lab, hardened VPS), run cloudflared on the host and route the public hostname to the local port. No inbound 80/443 needed.

cloudflared tunnel route dns coursemaker learn.client.com

/etc/cloudflared/config.yml:

tunnel: coursemaker
credentials-file: /etc/cloudflared/coursemaker.json

ingress:
- hostname: learn.client.com
service: http://127.0.0.1:4210
- service: http_status:404

Important headers

The app trusts the X-Forwarded-Proto header to know whether the client connection is HTTPS. Set it. If it's missing or wrong, session cookies will be issued without Secure and OAuth callbacks will redirect to HTTP.