Skip to main content

Reverse Proxy Configuration

A reverse proxy handles SSL termination and routes traffic to your relay. This page covers setup for popular options.

Why Use a Reverse Proxy?

  • SSL/TLS termination - Handle HTTPS/WSS encryption
  • Automatic certificates - Let's Encrypt integration
  • Load balancing - Distribute traffic across multiple relays
  • Security - Hide relay server, add rate limiting
  • Static content - Serve NIP-11 info and landing pages

Caddy is the simplest option with automatic HTTPS.

Installation

# Ubuntu/Debian
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy

Basic Configuration

Edit /etc/caddy/Caddyfile:

relay.example.com {
reverse_proxy localhost:7777
}

That's it! Caddy automatically obtains and renews SSL certificates.

Advanced Configuration

relay.example.com {
# Handle WebSocket upgrades
@websocket {
header Upgrade websocket
}
reverse_proxy @websocket localhost:7777

# Handle NIP-11 info requests
@nostr_info {
header Accept application/nostr+json
}
reverse_proxy @nostr_info localhost:7777

# Serve static landing page
file_server {
root /var/www/relay
}

# Enable compression
encode gzip

# Logging
log {
output file /var/log/caddy/relay.log
}
}

Apply Configuration

sudo systemctl reload caddy

nginx

More control, but requires manual certificate management.

Installation

sudo apt install nginx certbot python3-certbot-nginx

Get SSL Certificate

sudo certbot certonly --nginx -d relay.example.com

Configuration

Create /etc/nginx/sites-available/relay:

# Redirect HTTP to HTTPS
server {
listen 80;
server_name relay.example.com;
return 301 https://$server_name$request_uri;
}

# Main server block
server {
listen 443 ssl http2;
server_name relay.example.com;

# SSL configuration
ssl_certificate /etc/letsencrypt/live/relay.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/relay.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

# WebSocket proxy
location / {
proxy_pass http://127.0.0.1:7777;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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;

# WebSocket timeouts
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}

Enable Site

sudo ln -s /etc/nginx/sites-available/relay /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Auto-Renew Certificates

# Test renewal
sudo certbot renew --dry-run

# Add to crontab
sudo crontab -e
# Add: 0 3 * * * certbot renew --quiet && systemctl reload nginx

HAProxy

For high-availability and load balancing.

Configuration

global
log stdout format raw local0
maxconn 4096

defaults
mode http
timeout connect 5s
timeout client 60s
timeout server 60s
option http-server-close

frontend https_front
bind *:443 ssl crt /etc/haproxy/certs/relay.pem

# Detect WebSocket
acl is_websocket hdr(Upgrade) -i websocket

# Route to relay backend
use_backend relay_backend if is_websocket
default_backend relay_backend

backend relay_backend
# Enable WebSocket
option http-server-close
timeout tunnel 1h

# Health check
option httpchk GET /

# Relay servers
server relay1 127.0.0.1:7777 check
server relay2 127.0.0.1:7778 check backup

Traefik

Modern edge router with Docker integration.

docker-compose.yml

version: '3'

services:
traefik:
image: traefik:v2.10
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=admin@example.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
ports:
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./letsencrypt:/letsencrypt"

relay:
image: hoytech/strfry
labels:
- "traefik.enable=true"
- "traefik.http.routers.relay.rule=Host(`relay.example.com`)"
- "traefik.http.routers.relay.entrypoints=websecure"
- "traefik.http.routers.relay.tls.certresolver=letsencrypt"
- "traefik.http.services.relay.loadbalancer.server.port=7777"

Testing

Verify WebSocket

# Using websocat
websocat wss://relay.example.com

# Send test request
["REQ", "test", {"limit": 1}]

Verify NIP-11

curl -H "Accept: application/nostr+json" https://relay.example.com/

Check SSL

openssl s_client -connect relay.example.com:443

Common Issues

"502 Bad Gateway"

  • Relay not running on expected port
  • Check: curl http://localhost:7777

"WebSocket connection failed"

  • Missing upgrade headers
  • Check nginx/Caddy WebSocket configuration

"Certificate errors"

  • Wrong domain in certificate
  • Expired certificate
  • Check: certbot certificates

See Also