update for https traffic and setup
This commit is contained in:
@@ -10,8 +10,13 @@ MONGODB_DATABASE=scoffer
|
|||||||
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
|
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
|
||||||
|
|
||||||
# Frontend API URL for server deployment
|
# Frontend API URL for server deployment
|
||||||
|
# IMPORTANT: This should point to your EXTERNAL domain with HTTPS
|
||||||
|
# The reverse proxy handles SSL termination and forwards HTTP to containers
|
||||||
# Replace 'your-domain.com' with your actual domain
|
# Replace 'your-domain.com' with your actual domain
|
||||||
REACT_APP_API_URL=https://your-domain.com/api
|
REACT_APP_API_URL=https://your-domain.com/api
|
||||||
|
|
||||||
# Alternative: If backend runs on different port/subdomain
|
# Alternative: If backend runs on different port/subdomain
|
||||||
# REACT_APP_API_URL=https://api.your-domain.com
|
# REACT_APP_API_URL=https://api.your-domain.com
|
||||||
|
|
||||||
|
# For local testing with containers (no SSL)
|
||||||
|
# REACT_APP_API_URL=http://localhost:5000/api
|
||||||
|
|||||||
@@ -42,10 +42,17 @@ docker-compose logs -f
|
|||||||
|
|
||||||
### 3. Server Configuration
|
### 3. Server Configuration
|
||||||
|
|
||||||
|
⚠️ **IMPORTANT**: If you get `net::ERR_SSL_PROTOCOL_ERROR`, see `HTTPS_SETUP.md` for detailed SSL configuration.
|
||||||
|
|
||||||
|
#### SSL Architecture
|
||||||
|
```
|
||||||
|
Internet (HTTPS) → Reverse Proxy (SSL Termination) → Containers (HTTP)
|
||||||
|
```
|
||||||
|
|
||||||
#### Option A: Single Domain Setup
|
#### Option A: Single Domain Setup
|
||||||
If serving everything from one domain (e.g., `https://yourapp.com`):
|
If serving everything from one domain (e.g., `https://yourapp.com`):
|
||||||
|
|
||||||
1. Set up reverse proxy (nginx/traefik) to route:
|
1. Set up reverse proxy (nginx/traefik) with SSL termination to route:
|
||||||
- `/` → Frontend container (port 3000)
|
- `/` → Frontend container (port 3000)
|
||||||
- `/api/*` → Backend container (port 5000)
|
- `/api/*` → Backend container (port 5000)
|
||||||
|
|
||||||
@@ -58,17 +65,21 @@ If serving everything from one domain (e.g., `https://yourapp.com`):
|
|||||||
If using subdomains (e.g., `api.yourapp.com`):
|
If using subdomains (e.g., `api.yourapp.com`):
|
||||||
|
|
||||||
1. Set up DNS for subdomains
|
1. Set up DNS for subdomains
|
||||||
2. Update your `.env`:
|
2. Configure SSL for both domains
|
||||||
|
3. Update your `.env`:
|
||||||
```
|
```
|
||||||
REACT_APP_API_URL=https://api.yourapp.com
|
REACT_APP_API_URL=https://api.yourapp.com
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. SSL/HTTPS Setup
|
### 4. SSL/HTTPS Setup
|
||||||
|
|
||||||
For production, ensure HTTPS is configured:
|
**Critical**: SSL termination must happen at the reverse proxy level, NOT at individual containers.
|
||||||
- Use Let's Encrypt with certbot
|
|
||||||
- Configure your reverse proxy for SSL termination
|
- **Containers**: Serve HTTP internally (port 5000 for backend, port 80 for frontend)
|
||||||
- Update `REACT_APP_API_URL` to use `https://`
|
- **Reverse Proxy**: Handles HTTPS externally, forwards HTTP to containers
|
||||||
|
- **Frontend Config**: Points to external HTTPS URL (`https://your-domain.com/api`)
|
||||||
|
|
||||||
|
For detailed SSL setup instructions, see `HTTPS_SETUP.md`.
|
||||||
|
|
||||||
## Container Port Mapping
|
## Container Port Mapping
|
||||||
|
|
||||||
|
|||||||
241
HTTPS_SETUP.md
Normal file
241
HTTPS_SETUP.md
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
# 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
|
||||||
Reference in New Issue
Block a user