- CORS Origin Mismatch: Backend was configured for
http://localhost:5173but frontend is served fromhttps://uia-bobinsight.pinont.me - Mixed Content: HTTPS frontend trying to reach HTTP backend through Cloudflare Tunnel
- CSP Violation: Content Security Policy blocking connections with
connect-src 'none' - Single Origin Limitation: Backend only accepted one hardcoded origin
1. Backend CORS Configuration (apps/backend/src/server.ts)
Before:
const FRONTEND_URL = process.env.FRONTEND_URL || 'http://localhost:5173';
await fastify.register(cors, {
origin: FRONTEND_URL,
credentials: true,
// ...
});After:
// Support multiple frontend origins with wildcard patterns
const FRONTEND_URLS = process.env.FRONTEND_URL
? process.env.FRONTEND_URL.split(',').map(url => url.trim())
: ['http://localhost:5173'];
await fastify.register(cors, {
origin: (origin, callback) => {
if (!origin) {
callback(null, true);
return;
}
const isAllowed = FRONTEND_URLS.some(allowedOrigin => {
if (origin === allowedOrigin) return true;
// Pattern match for wildcards (e.g., *.pinont.me)
const pattern = allowedOrigin.replace(/\*/g, '.*');
const regex = new RegExp(`^${pattern}$`);
return regex.test(origin);
});
if (isAllowed) {
callback(null, true);
} else {
logger.warn(`CORS blocked origin: ${origin}`);
callback(new Error('Not allowed by CORS'), false);
}
},
credentials: true,
// ...
});Key Improvements:
- ✅ Supports multiple origins via comma-separated list
- ✅ Wildcard pattern matching for subdomains (
*.pinont.me) - ✅ Logs blocked origins for debugging
- ✅ Allows requests with no origin (mobile apps, Postman)
Before:
environment:
- FRONTEND_URL=http://localhost:5173
- VITE_API_URL=http://localhost:3000After:
environment:
- FRONTEND_URL=${FRONTEND_URL:-http://localhost:5173,https://uia-bobinsight.pinont.me,https://*.pinont.me}
- VITE_API_BASE_URL=${VITE_API_BASE_URL:-http://localhost:3000}Key Improvements:
- ✅ Reads from
.envfile with sensible defaults - ✅ Includes production domains by default
- ✅ Supports environment variable override
3. Nginx Configuration (apps/frontend/nginx.conf)
Before:
add_header Referrer-Policy "no-referrer-when-downgrade" always;After:
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# CORS headers
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With" always;
# Content Security Policy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' http://localhost:3000 https://*.pinont.me http://*.pinont.me ws://localhost:* wss://*.pinont.me; frame-ancestors 'self';" always;Key Improvements:
- ✅ Stricter referrer policy for security
- ✅ Explicit CORS headers for API requests
- ✅ CSP allows connections to backend domains
- ✅ Supports both HTTP (dev) and HTTPS (prod)
- ✅ WebSocket support for future features
Added:
# Frontend URL (for CORS) - Comma-separated list
# Supports wildcards: https://*.pinont.me
FRONTEND_URL=http://localhost:5173,https://uia-bobinsight.pinont.me,https://*.pinont.me
# Frontend API Base URL (for production deployment)
VITE_API_BASE_URL=http://localhost:3000# Create .env from example
cp .env.example .env
# Edit .env and set:
FRONTEND_URL=https://uia-bobinsight.pinont.me,https://*.pinont.me
VITE_API_BASE_URL=https://your-backend-tunnel.pinont.me# Stop existing containers
docker-compose down
# Rebuild with new configuration
docker-compose build --no-cache
# Start services
docker-compose up -d# Check container status
docker-compose ps
# View logs
docker-compose logs -f backend frontend
# Look for this in backend logs:
# "DEBUG: Allowed CORS origins: [...]"# From browser console on https://uia-bobinsight.pinont.me
fetch('https://your-backend-tunnel.pinont.me/health')
.then(r => r.json())
.then(console.log)Solution:
- Check backend logs:
docker-compose logs backend | grep CORS - Verify environment:
docker-compose exec backend env | grep FRONTEND_URL - Ensure frontend origin matches exactly (including protocol)
Solution:
- Check browser console for blocked resource
- Update CSP
connect-srcinnginx.conf - Rebuild frontend:
docker-compose up -d --build frontend
Solution:
- Ensure backend is accessible via HTTPS through Cloudflare Tunnel
- Update
VITE_API_BASE_URLto usehttps:// - Rebuild frontend with new env var
- Backend accepts requests from
https://uia-bobinsight.pinont.me - No CORS errors in browser console
- No CSP violations in browser console
- API requests complete successfully
- Health check endpoint responds:
/health - Analyze endpoint accepts requests:
/api/analyze
- Wildcard Origins: The
*.pinont.mepattern allows any subdomain. For production, consider listing specific subdomains. - HTTPS Only: Always use HTTPS in production to prevent MITM attacks.
- API Keys: Never commit
.envfiles. Use environment variables or secrets management. - Rate Limiting: Current limit is 10 requests per minute. Adjust in
.envif needed.
- Full deployment guide:
DEPLOYMENT.md - Backend server code:
apps/backend/src/server.ts - Frontend API client:
apps/frontend/src/services/apiClient.ts - Nginx configuration:
apps/frontend/nginx.conf
Made with Bob