Files
scoffer/HTTPS_SETUP.md

6.7 KiB

HTTPS Setup Guide for Container Deployment

Understanding the SSL Error

The net::ERR_SSL_PROTOCOL_ERROR occurs because:

  1. Frontend tries to make HTTPS requests to the backend
  2. Backend container only serves HTTP (port 5000)
  3. SSL termination should happen at the reverse proxy, not individual containers

Correct Architecture

Internet (HTTPS) → Reverse Proxy (SSL Termination) → Containers (HTTP)
  • External traffic: HTTPS to reverse proxy
  • Internal traffic: HTTP between containers
  • SSL termination: Handled by reverse proxy (nginx/traefik)

Solution Options

Option 1: Nginx Reverse Proxy with SSL

1. Create nginx configuration

Create /etc/nginx/sites-available/recipe-app:

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 Configuration
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    
    # 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;
    ssl_prefer_server_ciphers off;
    
    # Frontend (React App)
    location / {
        proxy_pass http://localhost:3000;
        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;
    }
    
    # Backend API
    location /api/ {
        proxy_pass http://localhost:5000/api/;
        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;
        
        # CORS headers (if needed)
        add_header Access-Control-Allow-Origin "https://your-domain.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
        
        if ($request_method = 'OPTIONS') {
            return 204;
        }
    }
}

2. Enable the site

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

3. Get SSL certificate with Let's Encrypt

sudo certbot --nginx -d your-domain.com

Option 2: Docker Compose with Nginx Container

Create an nginx container in your docker-compose.yml:

  # Nginx Reverse Proxy
  nginx:
    image: nginx:alpine
    container_name: scoffer-nginx
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - /etc/letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - frontend
      - backend
    networks:
      - recipe-network

Option 3: Traefik (Automatic SSL)

Add traefik to your docker-compose.yml:

  # Traefik Reverse Proxy
  traefik:
    image: traefik:v2.10
    container_name: scoffer-traefik
    restart: unless-stopped
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "--certificatesresolvers.letsencrypt.acme.email=your-email@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-ssl-certs:/letsencrypt
    networks:
      - recipe-network

  # Update frontend service
  frontend:
    # ... existing config ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.frontend.rule=Host(\`your-domain.com\`)"
      - "traefik.http.routers.frontend.entrypoints=websecure"
      - "traefik.http.routers.frontend.tls.certresolver=letsencrypt"
      - "traefik.http.services.frontend.loadbalancer.server.port=80"

  # Update backend service  
  backend:
    # ... existing config ...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.backend.rule=Host(\`your-domain.com\`) && PathPrefix(\`/api\`)"
      - "traefik.http.routers.backend.entrypoints=websecure"
      - "traefik.http.routers.backend.tls.certresolver=letsencrypt"
      - "traefik.http.services.backend.loadbalancer.server.port=5000"

volumes:
  traefik-ssl-certs:

Environment Configuration

For Production (.env)

# Frontend points to EXTERNAL HTTPS URL
REACT_APP_API_URL=https://your-domain.com/api

# Backend and DB use internal container names (HTTP)
MONGODB_HOST=mongodb
MONGODB_PORT=27017
JWT_SECRET=your-production-secret

For Local Testing (.env.local)

# Frontend points to localhost (HTTP)
REACT_APP_API_URL=http://localhost:5000/api

# Backend and DB use container names
MONGODB_HOST=mongodb
MONGODB_PORT=27017
JWT_SECRET=your-dev-secret

Troubleshooting

Still getting SSL errors?

  1. Check your .env file:

    cat .env | grep REACT_APP_API_URL
    # Should show: REACT_APP_API_URL=https://your-domain.com/api
    
  2. Verify reverse proxy is working:

    curl -I https://your-domain.com/api/health
    # Should return 200 OK
    
  3. Check container logs:

    docker-compose logs frontend
    docker-compose logs backend
    docker-compose logs nginx  # if using nginx container
    
  4. Test internal container communication:

    docker-compose exec frontend ping backend
    docker-compose exec backend ping mongodb
    

Common Issues

  • Mixed content errors: Ensure all API calls use HTTPS in production
  • CORS errors: Configure CORS headers in nginx or backend
  • Certificate issues: Verify SSL certificate is valid and not expired
  • Port conflicts: Ensure ports 80/443 are not used by other services

Security Best Practices

  1. Use strong SSL configuration (TLS 1.2+)
  2. Enable HSTS headers
  3. Configure proper CORS policies
  4. Use environment-specific secrets
  5. Regular certificate renewal (certbot auto-renewal)

Quick Fix Summary

  1. Set up reverse proxy (nginx/traefik) with SSL termination
  2. Point frontend to HTTPS domain: REACT_APP_API_URL=https://your-domain.com/api
  3. Keep container communication as HTTP: containers talk to each other via HTTP
  4. SSL happens at proxy level: not at individual containers