1 Reverse Proxy Setup
SluberskiHomeLab edited this page 2025-10-13 07:56:19 -05:00

Reverse Proxy Setup

For production deployments, you'll want to run Enspotification behind a reverse proxy for SSL/TLS termination, load balancing, and better security. Here are configurations for popular reverse proxy solutions:

Prerequisites for Reverse Proxy Setup

Before configuring your reverse proxy, make sure to:

  1. Update your Spotify App settings with the new redirect URI:

    https://your-domain.com/callback
    
  2. Update your .env file:

    SPOTIFY_REDIRECT_URI=https://your-domain.com/callback
    

Only if Running Reverse Proxy in same Docker Network 3. Modify docker-compose.yml to not expose port 3000 publicly:

services:
  enspotification:
    ports:
      - "127.0.0.1:3000:3000"  # Only bind to localhost

Caddy

Caddy automatically handles SSL certificates via Let's Encrypt and has a simple configuration.

Caddyfile:

your-domain.com {
    reverse_proxy localhost:3000
    
    # Optional: Enable compression
    encode gzip
    
    # Optional: Add security headers
    header {
        # Enable HSTS
        Strict-Transport-Security max-age=31536000;
        # Prevent clickjacking
        X-Frame-Options DENY
        # Prevent MIME type sniffing
        X-Content-Type-Options nosniff
        # XSS Protection
        X-XSS-Protection "1; mode=block"
    }
    
    # Health check endpoint
    handle /health {
        reverse_proxy localhost:3000
    }
}

Docker Compose with Caddy:

version: '3.8'

services:
  enspotification:
    build: .
    ports:
      - "127.0.0.1:3000:3000"
    env_file:
      - .env
    restart: unless-stopped
    networks:
      - enspotification

  caddy:
    image: caddy:2-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - enspotification
    restart: unless-stopped

networks:
  enspotification:
    driver: bridge

volumes:
  caddy_data:
  caddy_config:
  enspotification_data:

Traefik

Traefik excels at automatic service discovery and SSL certificate management.

docker-compose.yml with Traefik:

version: '3.8'

services:
  traefik:
    image: traefik:v3.0
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=your-email@example.com"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      # Optional: Enable access logs
      - "--accesslog=true"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"  # Traefik dashboard
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "letsencrypt:/letsencrypt"
    networks:
      - traefik
    restart: unless-stopped

  enspotification:
    build: .
    env_file:
      - .env
    restart: unless-stopped
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.enspotification.rule=Host(`your-domain.com`)"
      - "traefik.http.routers.enspotification.entrypoints=websecure"
      - "traefik.http.routers.enspotification.tls.certresolver=myresolver"
      - "traefik.http.services.enspotification.loadbalancer.server.port=3000"
      # Redirect HTTP to HTTPS
      - "traefik.http.routers.enspotification-http.rule=Host(`your-domain.com`)"
      - "traefik.http.routers.enspotification-http.entrypoints=web"
      - "traefik.http.routers.enspotification-http.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

networks:
  traefik:
    external: true

volumes:
  letsencrypt:
  enspotification_data:

Setup Commands:

# Create external network
docker network create traefik

# Start services
docker-compose up -d

Nginx

Traditional and highly configurable reverse proxy solution.

nginx.conf:

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

server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # SSL Certificate configuration
    ssl_certificate /path/to/your/certificate.crt;
    ssl_certificate_key /path/to/your/private.key;
    
    # SSL Security settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    
    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;

    # Proxy settings
    location / {
        proxy_pass http://localhost:3000;
        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;
        proxy_cache_bypass $http_upgrade;
        
        # Timeout settings
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Health check endpoint (optional monitoring)
    location /health {
        proxy_pass http://localhost:3000/health;
        access_log off;
    }
    
    # Optional: Enable gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/json
        application/javascript
        application/xml+rss
        application/atom+xml
        image/svg+xml;
}

Docker Compose with Nginx:

version: '3.8'

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./ssl:/etc/nginx/ssl  # Mount your SSL certificates
    depends_on:
      - enspotification
    networks:
      - enspotification
    restart: unless-stopped

  enspotification:
    build: .
    expose:
      - "3000"
    env_file:
      - .env
    restart: unless-stopped
    networks:
      - enspotification

networks:
  enspotification:
    driver: bridge

volumes:
  enspotification_data:

Nginx Proxy Manager (NPM)

Web-based GUI for managing Nginx reverse proxies with automatic SSL certificates.

docker-compose.yml with NPM:

version: '3.8'

services:
  nginx-proxy-manager:
    image: 'jc21/nginx-proxy-manager:latest'
    ports:
      - '80:80'
      - '81:81'    # NPM Admin Panel
      - '443:443'
    volumes:
      - npm_data:/data
      - npm_letsencrypt:/etc/letsencrypt
    networks:
      - npm
    restart: unless-stopped

  enspotification:
    build: .
    expose:
      - "3000"
    env_file:
      - .env
    restart: unless-stopped
    networks:
      - npm

networks:
  npm:
    driver: bridge

volumes:
  npm_data:
  npm_letsencrypt:
  enspotification_data:

NPM Setup Steps:

  1. Access NPM Admin Panel: http://your-server-ip:81
  2. Default Login:
    • Email: admin@example.com
    • Password: changeme
  3. Add Proxy Host:
    • Domain Names: your-domain.com
    • Scheme: http
    • Forward Hostname/IP: enspotification
    • Forward Port: 3000
    • Cache Assets:
    • Block Common Exploits:
  4. SSL Tab:
    • SSL Certificate: Request a new SSL Certificate
    • Force SSL:
    • Email: your-email@example.com
    • I Agree to the Let's Encrypt Terms of Service:

Load Balancing (Multiple Instances)

For high availability, you can run multiple Enspotification instances:

docker-compose.yml with load balancing:

version: '3.8'

services:
  enspotification-1:
    build: .
    env_file: .env
    networks: [app]
    restart: unless-stopped

  enspotification-2:
    build: .
    env_file: .env
    networks: [app]
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports: ["80:80", "443:443"]
    volumes:
      - ./nginx-lb.conf:/etc/nginx/conf.d/default.conf
    depends_on: [enspotification-1, enspotification-2]
    networks: [app]
    restart: unless-stopped

networks:
  app:

volumes:
  enspotification_data:

nginx-lb.conf:

upstream enspotification {
    least_conn;
    server enspotification-1:3000;
    server enspotification-2:3000;
}

server {
    listen 80;
    server_name your-domain.com;
    
    location / {
        proxy_pass http://enspotification;
        # ... (other proxy settings as above)
    }
}

Security Considerations

When running behind a reverse proxy:

  1. Firewall: Block direct access to port 3000
  2. SSL/TLS: Always use HTTPS in production
  3. Rate Limiting: Configure rate limiting in your reverse proxy
  4. Access Logs: Monitor access patterns
  5. Security Headers: Implement CSP, HSTS, etc.
  6. Regular Updates: Keep your reverse proxy updated

Monitoring

Add monitoring endpoints to your reverse proxy:

# Nginx monitoring location
location /nginx-status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    deny all;
}

This allows you to monitor both Enspotification (/health) and Nginx status.