## Problem CORS error: "Access-Control-Allow-Origin header contains multiple values" caused by both Nginx and NestJS sending CORS headers. ## Solution - NestJS handles CORS exclusively (main.ts) - Nginx acts as SSL proxy only (no CORS headers) - Updated .env.production.example with HTTPS origins - Created GUIA-CORS-PRODUCCION.md with complete documentation ## Files Changed - .gitignore: Allow .env.*.example files - apps/backend/.gitignore: Allow .env.*.example files - apps/backend/.env.production.example: HTTPS CORS config - apps/frontend/.env.production.example: HTTPS/WSS config - docs/95-guias-desarrollo/GUIA-CORS-PRODUCCION.md: Full guide ## Production Steps 1. Update .env.production files with HTTPS origins 2. Remove CORS headers from Nginx config 3. Rebuild frontend, restart backend 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
4.5 KiB
4.5 KiB
GUIA-CORS-PRODUCCION.md
Configuración CORS para Producción - GAMILIT
Fecha: 2025-12-18
Problema resuelto: Error Access-Control-Allow-Origin contains multiple values
1. Descripción del Problema
Al hacer requests desde el frontend (puerto 3005) al backend (puerto 3006) en producción con HTTPS, se recibe el error:
Access to XMLHttpRequest at 'https://74.208.126.102:3006/api/v1/auth/register'
from origin 'https://74.208.126.102:3005' has been blocked by CORS policy:
The 'Access-Control-Allow-Origin' header contains multiple values
'https://74.208.126.102:3005, https://74.208.126.102:3005', but only one is allowed.
2. Causa Raíz
El header Access-Control-Allow-Origin se está enviando DOS VECES:
- Nginx (proxy SSL) agrega headers CORS
- NestJS (backend) también agrega headers CORS via
app.enableCors()
Cuando ambos envían el header, el navegador ve valores duplicados y rechaza la respuesta.
3. Solución Definitiva
Regla de Oro: Solo NestJS maneja CORS
Nginx debe actuar únicamente como proxy SSL sin agregar headers CORS.
3.1 Configuración Backend (.env.production)
# CORS - CONFIGURACIÓN CRÍTICA
# ============================================================================
# ADVERTENCIA: HEADERS CORS DUPLICADOS
# ============================================================================
# Si usas Nginx como proxy SSL, NO agregar headers CORS en Nginx.
# NestJS maneja CORS internamente en main.ts.
# Headers duplicados causan error:
# "The 'Access-Control-Allow-Origin' header contains multiple values"
# ============================================================================
# Incluye HTTPS y HTTP para compatibilidad durante transición
CORS_ORIGIN=https://74.208.126.102:3005,https://74.208.126.102,http://74.208.126.102:3005,http://74.208.126.102
ENABLE_CORS=true
3.2 Configuración Frontend (.env.production)
# SSL CONFIGURADO - Usar HTTPS/WSS
VITE_API_HOST=74.208.126.102:3006
VITE_API_PROTOCOL=https
VITE_WS_HOST=74.208.126.102:3006
VITE_WS_PROTOCOL=wss
3.3 Configuración Nginx (SIN CORS)
# /etc/nginx/sites-available/gamilit-backend
server {
listen 3006 ssl;
server_name 74.208.126.102;
ssl_certificate /etc/ssl/certs/gamilit.crt;
ssl_certificate_key /etc/ssl/private/gamilit.key;
location / {
proxy_pass http://127.0.0.1:3007; # PM2 cluster interno
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
proxy_cache_bypass $http_upgrade;
# ⚠️ NO AGREGAR headers CORS aquí
# NestJS los maneja internamente
}
}
4. Verificación
4.1 Verificar headers con curl
# Verificar que solo hay UN header Access-Control-Allow-Origin
curl -I -X OPTIONS \
-H "Origin: https://74.208.126.102:3005" \
-H "Access-Control-Request-Method: POST" \
https://74.208.126.102:3006/api/v1/auth/register
# Salida esperada (UN solo header):
# Access-Control-Allow-Origin: https://74.208.126.102:3005
# Access-Control-Allow-Credentials: true
# Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS
4.2 Verificar configuración Nginx
# Buscar headers CORS duplicados en configuración
grep -r "Access-Control" /etc/nginx/
Si encuentra líneas como add_header Access-Control-Allow-Origin, elimínelas.
5. Pasos de Deploy
- Backend: Actualizar
.env.productioncon HTTPS origins - Frontend: Actualizar
.env.productioncon HTTPS/WSS - Nginx: Remover cualquier header CORS
- Reiniciar servicios:
sudo systemctl reload nginx pm2 restart gamilit-backend cd /path/to/frontend && npm run build
6. Troubleshooting
Error persiste después de la configuración
- Verificar que Nginx no tenga headers CORS en ningún include
- Revisar si hay otro proxy (CloudFlare, etc.) agregando headers
- Limpiar cache del navegador (F12 > Network > Disable cache)
Error "CORS blocked request" en logs
- El origen que hace la petición no está en CORS_ORIGIN
- Verificar que el protocolo coincide (https vs http)
7. Referencias
- Backend CORS:
apps/backend/src/main.tslíneas 27-46 - Config CORS:
apps/backend/src/config/app.config.ts - Ejemplo .env:
apps/backend/.env.production.example