Files
snippet-pastebin/CORS-GUIDE.md

410 lines
10 KiB
Markdown

# CORS Configuration & Testing Guide
This guide covers Cross-Origin Resource Sharing (CORS) configuration for CodeSnippet when deployed with separate frontend and backend domains.
## Understanding CORS
CORS is a security feature that controls which domains can make requests to your backend API. When your frontend (`https://frontend.example.com`) makes requests to your backend (`https://backend.example.com`), the browser enforces CORS policies.
## Backend CORS Configuration
### Environment Variable: `CORS_ALLOWED_ORIGINS`
The backend Flask application uses the `CORS_ALLOWED_ORIGINS` environment variable to control which origins can access the API.
#### Development (Allow All Origins)
```bash
CORS_ALLOWED_ORIGINS=*
```
**Warning:** Only use `*` in development. This allows ANY website to access your backend API.
#### Production (Specific Origins)
```bash
# Single origin
CORS_ALLOWED_ORIGINS=https://frontend.example.com
# Multiple origins (comma-separated)
CORS_ALLOWED_ORIGINS=https://frontend.example.com,https://app.example.com,https://staging.example.com
```
**Important:** Do NOT include trailing slashes in URLs.
### How It Works
The Flask backend (`backend/app.py`) reads this environment variable and configures CORS accordingly:
```python
ALLOWED_ORIGINS = os.environ.get('CORS_ALLOWED_ORIGINS', '*')
if ALLOWED_ORIGINS == '*':
CORS(app, origins='*', ...) # Development mode
else:
origins_list = [origin.strip() for origin in ALLOWED_ORIGINS.split(',')]
CORS(app, origins=origins_list, ...) # Production mode
```
### CORS Headers Returned
When properly configured, the backend returns these headers:
```
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
```
## Testing CORS Configuration
### Test 1: Health Check (No CORS)
Simple GET request to verify backend is running:
```bash
curl https://backend.example.com/health
```
Expected response:
```json
{
"status": "healthy",
"timestamp": "2024-01-01T12:00:00"
}
```
### Test 2: Preflight Request (OPTIONS)
Browsers send an OPTIONS request before the actual request to check CORS permissions:
```bash
curl -X OPTIONS https://backend.example.com/api/snippets \
-H "Origin: https://frontend.example.com" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: Content-Type" \
-v
```
Expected headers in response:
```
< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: https://frontend.example.com
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
< Access-Control-Allow-Headers: Content-Type, Authorization
```
### Test 3: Actual API Request with Origin
Test a real API request with Origin header:
```bash
curl https://backend.example.com/api/snippets \
-H "Origin: https://frontend.example.com" \
-v
```
Should return `Access-Control-Allow-Origin` header and snippet data.
### Test 4: Wrong Origin (Should Fail)
Test that unauthorized origins are blocked:
```bash
curl https://backend.example.com/api/snippets \
-H "Origin: https://malicious-site.com" \
-v
```
Expected: No `Access-Control-Allow-Origin` header (or browser would block).
### Test 5: Browser DevTools Test
1. Open frontend in browser: `https://frontend.example.com`
2. Open DevTools (F12) → Network tab
3. Create a new snippet or load snippets
4. Check the API request:
- Should show status 200 OK
- Response headers should include `Access-Control-Allow-Origin`
- No CORS errors in Console
## Common CORS Errors & Solutions
### Error: "CORS policy: No 'Access-Control-Allow-Origin' header"
**Cause:** Backend is not returning CORS headers
**Solutions:**
1. Verify `CORS_ALLOWED_ORIGINS` is set correctly in backend
2. Check backend logs for errors
3. Ensure Flask-CORS is installed: `pip install flask-cors`
4. Restart backend after environment variable changes
### Error: "CORS policy: Origin 'https://frontend.example.com' not allowed"
**Cause:** Frontend origin not in allowed list
**Solutions:**
1. Check `CORS_ALLOWED_ORIGINS` includes exact frontend URL
2. Ensure no trailing slash: `https://frontend.example.com` not `https://frontend.example.com/`
3. Verify HTTPS vs HTTP matches exactly
4. Check for typos in domain name
### Error: "CORS policy: Request header 'content-type' not allowed"
**Cause:** Backend not allowing required headers
**Solutions:**
1. Verify backend allows `Content-Type` header
2. Check Flask-CORS configuration in `app.py`
3. Ensure `allow_headers` includes `Content-Type`
### Error: Mixed Content (HTTP/HTTPS)
**Cause:** Frontend uses HTTPS but backend uses HTTP (or vice versa)
**Solutions:**
1. Ensure both frontend and backend use HTTPS in production
2. Update `VITE_FLASK_BACKEND_URL` to use `https://`
3. Enable HTTPS in CapRover for both apps
4. Verify Cloudflare SSL/TLS mode is "Full (strict)"
### Error: "CORS policy: Credential is not supported if origin is '*'"
**Cause:** Using `CORS_ALLOWED_ORIGINS=*` with `supports_credentials=True`
**Solutions:**
1. Set specific origins instead of `*`
2. Or disable credentials if not needed
## Deployment Scenarios
### Scenario 1: Separate Domains (Recommended for Production)
```
Frontend: https://frontend.example.com
Backend: https://backend.example.com
```
**Frontend Config:**
```bash
VITE_FLASK_BACKEND_URL=https://backend.example.com
```
**Backend Config:**
```bash
CORS_ALLOWED_ORIGINS=https://frontend.example.com
```
**Pros:** Clean separation, independent scaling
**Cons:** Requires CORS configuration
### Scenario 2: Single Domain with Proxy
```
Frontend: https://app.example.com
Backend: https://app.example.com/api (proxied)
```
**Frontend Config:**
```bash
VITE_FLASK_BACKEND_URL=/api
```
**Backend Config:**
```bash
CORS_ALLOWED_ORIGINS=* # Not needed if proxied through nginx
```
**Nginx Config:** (already configured in `nginx.conf`)
```nginx
location /api {
proxy_pass http://backend:5000;
}
```
**Pros:** No CORS issues (same-origin), simpler configuration
**Cons:** Tight coupling, single domain
### Scenario 3: Multiple Frontends
```
Frontend 1: https://app.example.com
Frontend 2: https://staging.example.com
Backend: https://api.example.com
```
**Frontend Config (both):**
```bash
VITE_FLASK_BACKEND_URL=https://api.example.com
```
**Backend Config:**
```bash
CORS_ALLOWED_ORIGINS=https://app.example.com,https://staging.example.com
```
## Cloudflare-Specific Configuration
### Cloudflare SSL/TLS Mode
Set to **"Full (strict)"** to ensure end-to-end encryption:
1. Cloudflare Dashboard → SSL/TLS → Overview
2. Select "Full (strict)"
3. Ensures both Cloudflare-to-origin and client-to-Cloudflare use SSL
### Cloudflare Always Use HTTPS
1. SSL/TLS → Edge Certificates
2. Enable "Always Use HTTPS"
3. Automatically redirects HTTP to HTTPS
### Cloudflare Transform Rules (Optional)
Add security headers using Transform Rules:
```
Header: Strict-Transport-Security
Value: max-age=31536000; includeSubDomains
Header: X-Content-Type-Options
Value: nosniff
Header: X-Frame-Options
Value: DENY
```
## Automated CORS Testing Script
Save this as `test-cors.sh`:
```bash
#!/bin/bash
FRONTEND_URL="https://frontend.example.com"
BACKEND_URL="https://backend.example.com"
echo "Testing CORS Configuration..."
echo "================================"
echo -e "\n1. Testing Health Endpoint..."
curl -s "$BACKEND_URL/health" | jq .
echo -e "\n2. Testing OPTIONS Preflight..."
curl -X OPTIONS "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-H "Access-Control-Request-Method: GET" \
-i -s | grep -i "access-control"
echo -e "\n3. Testing GET with Origin..."
curl -s "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-i | grep -i "access-control"
echo -e "\n4. Testing POST with Origin..."
curl -X POST "$BACKEND_URL/api/snippets" \
-H "Origin: $FRONTEND_URL" \
-H "Content-Type: application/json" \
-d '{"id":"test","title":"Test","code":"test","language":"JavaScript","createdAt":"2024-01-01T00:00:00","updatedAt":"2024-01-01T00:00:00"}' \
-i -s | grep -i "access-control"
echo -e "\nCORS tests complete!"
```
Make it executable and run:
```bash
chmod +x test-cors.sh
./test-cors.sh
```
## Frontend Storage Config Helper
The frontend automatically handles backend configuration through the `getStorageConfig()` function:
### Automatic Configuration
If `VITE_FLASK_BACKEND_URL` is set, the app automatically uses Flask backend:
```typescript
// src/lib/storage.ts
function getDefaultConfig(): StorageConfig {
const flaskUrl = import.meta.env.VITE_FLASK_BACKEND_URL
if (flaskUrl) {
return { backend: 'flask', flaskUrl: flaskUrl }
}
return { backend: 'indexeddb' }
}
```
### Manual Configuration
Users can also manually configure in Settings page (if no env var is set).
## Debugging Tips
### Enable Verbose Logging
Add logging to backend `app.py`:
```python
import logging
logging.basicConfig(level=logging.DEBUG)
@app.after_request
def after_request(response):
app.logger.debug(f"Response headers: {response.headers}")
return response
```
### Browser DevTools
1. Open DevTools (F12)
2. Network tab → Enable "Preserve log"
3. Filter by "Fetch/XHR"
4. Look for OPTIONS and GET/POST requests
5. Check Response Headers for `Access-Control-Allow-Origin`
6. Check Console for CORS error messages
### CapRover Logs
View real-time backend logs:
```bash
# Via CapRover CLI
caprover logs codesnippet-backend --lines 100 --follow
# Via Dashboard
Apps → codesnippet-backend → Logs
```
## Security Best Practices
### Production CORS Checklist
- [ ] Set specific origins (not `*`)
- [ ] Use HTTPS for all URLs
- [ ] Enable Cloudflare proxy (orange cloud)
- [ ] Set Cloudflare SSL mode to "Full (strict)"
- [ ] Remove trailing slashes from origin URLs
- [ ] Test with automated script
- [ ] Monitor for CORS errors in production logs
- [ ] Document allowed origins
### Regular Audits
Periodically review:
1. Which origins are allowed
2. Whether all origins are still needed
3. CORS-related errors in logs
4. Unauthorized access attempts
## Additional Resources
- [MDN CORS Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)
- [Flask-CORS Documentation](https://flask-cors.readthedocs.io/)
- [Cloudflare SSL/TLS Documentation](https://developers.cloudflare.com/ssl/)
- [CapRover Environment Variables](https://caprover.com/docs/app-configuration.html)