Reverse Proxy
Configure nginx or Caddy to proxy HTTPS traffic to HelloJohn, including TLS termination, WebSocket support, and rate limiting.
Reverse Proxy
HelloJohn listens on plain HTTP. A reverse proxy handles TLS termination, HTTP/2, and acts as a security boundary between the internet and HelloJohn.
Caddy (recommended)
Caddy automatically provisions and renews TLS certificates via Let's Encrypt. It requires zero SSL configuration.
Caddyfile
auth.yourdomain.com {
reverse_proxy localhost:3000 {
# Pass real client IP to HelloJohn
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
# Health check for load balancer
health_uri /health
health_interval 30s
}
# Security headers
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
-Server
}
# Gzip compression
encode gzip
# Rate limiting (requires caddy-ratelimit plugin)
# rate_limit {
# zone login {
# key {remote_host}
# events 20
# window 1m
# }
# match path /v1/auth/sign-in /v1/auth/sign-up /v1/auth/magic-link
# }
log {
output file /var/log/caddy/auth.yourdomain.com.log
format json
}
}Start Caddy:
caddy run --config /etc/caddy/CaddyfileCaddy handles certificate renewal automatically. No cron job needed.
nginx
Installation
# Ubuntu/Debian
apt install nginx certbot python3-certbot-nginx
# Obtain certificate
certbot --nginx -d auth.yourdomain.comnginx.conf snippet
Create /etc/nginx/sites-available/hellojohn:
upstream hellojohn {
server 127.0.0.1:3000;
keepalive 32;
}
# Redirect HTTP to HTTPS
server {
listen 80;
listen [::]:80;
server_name auth.yourdomain.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name auth.yourdomain.com;
# TLS (managed by certbot)
ssl_certificate /etc/letsencrypt/live/auth.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/auth.yourdomain.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
server_tokens off;
# Logging
access_log /var/log/nginx/hellojohn.access.log;
error_log /var/log/nginx/hellojohn.error.log;
# Proxy to HelloJohn
location / {
proxy_pass http://hellojohn;
proxy_http_version 1.1;
# Headers
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;
# Connection keep-alive
proxy_set_header Connection "";
# Timeouts
proxy_connect_timeout 10s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
# Buffer settings
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
}
# Health check endpoint (no auth, no logging)
location = /health {
proxy_pass http://hellojohn;
access_log off;
}
# Request size limit (e.g., for avatar uploads)
client_max_body_size 10M;
}Enable and reload:
ln -s /etc/nginx/sites-available/hellojohn /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginxRenew certificates automatically:
# Already added by certbot, verify with:
systemctl status certbot.timerTraefik
For Kubernetes or Docker Swarm deployments, Traefik is a popular choice.
Docker Compose labels:
services:
hellojohn:
image: ghcr.io/hellojohn/hellojohn:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.hellojohn.rule=Host(`auth.yourdomain.com`)"
- "traefik.http.routers.hellojohn.entrypoints=websecure"
- "traefik.http.routers.hellojohn.tls.certresolver=letsencrypt"
- "traefik.http.services.hellojohn.loadbalancer.server.port=3000"
- "traefik.http.middlewares.hellojohn-compress.compress=true"
- "traefik.http.routers.hellojohn.middlewares=hellojohn-compress"Configuring HelloJohn to trust the proxy
When running behind a reverse proxy, set these environment variables so HelloJohn reads the real client IP correctly:
TRUST_PROXY=true
TRUST_PROXY_HOPS=1 # number of proxy hops between internet and HelloJohnThis enables correct IP-based rate limiting and audit logging.
Multiple instances
For horizontal scaling, all instances must share:
- The same
DATABASE_URL— PostgreSQL is the source of truth - The same
REDIS_URL— for shared rate limiting and session state - The same JWT keys — so tokens issued by one instance are valid on another
upstream hellojohn {
# Sticky sessions are NOT required — stateless JWT auth
least_conn;
server 10.0.0.1:3000;
server 10.0.0.2:3000;
server 10.0.0.3:3000;
keepalive 64;
}Testing your setup
# Check TLS grade
curl https://www.ssllabs.com/ssltest/analyze.html?d=auth.yourdomain.com
# Verify HSTS header
curl -I https://auth.yourdomain.com | grep -i strict
# Check security headers
curl -I https://auth.yourdomain.com
# Test health endpoint
curl https://auth.yourdomain.com/health
# {"status":"ok","version":"1.2.3","db":"connected"}