# 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`: ```nginx 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 ```bash 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 ```bash sudo certbot --nginx -d your-domain.com ``` ### Option 2: Docker Compose with Nginx Container Create an nginx container in your docker-compose.yml: ```yaml # 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: ```yaml # 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) ```bash # 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) ```bash # 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**: ```bash cat .env | grep REACT_APP_API_URL # Should show: REACT_APP_API_URL=https://your-domain.com/api ``` 2. **Verify reverse proxy is working**: ```bash curl -I https://your-domain.com/api/health # Should return 200 OK ``` 3. **Check container logs**: ```bash docker-compose logs frontend docker-compose logs backend docker-compose logs nginx # if using nginx container ``` 4. **Test internal container communication**: ```bash 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