Sua API funciona em desenvolvimento, mas falha em produção comA solicitação de comprovação do CORS não passa na verificação de controle de acesso ou a solicitação OPTIONS retorna 404/405. Falhas de comprovação são um problema comum de CORS somente em produção. Veja como diagnosticá-los e corrigi-los.
📋 Table of Contents
- O que é uma solicitação de comprovação?
- Causa 1: o servidor não controla OPÇÕES
- Causa 2: OPÇÕES de faixas ou blocos de proxy reverso
- Causa 3: Origem do curinga com credenciais
- Causa 4: Cabeçalhos permitidos ausentes
- Causa 5: O middleware de autenticação é executado antes do CORS
- Depurando falhas de comprovação
- Perguntas Frequentes
- Conclusão
O que é uma solicitação de comprovação?
Para solicitações “não simples” (PUT, DELETE, cabeçalhos personalizados ou tipo de conteúdo JSON), o navegador envia umOPÇÕES request first — o “preflight” — para perguntar ao servidor se a solicitação real é permitida. Se a simulação falhar, a solicitação real nunca será executada. As falhas de comprovação geralmente aparecem apenas na produção devido a diferentes configurações do servidor.
# Browser automatically sends this before your POST:
OPTIONS /api/users HTTP/1.1
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization
# Server MUST respond with matching CORS headers and 2xx status
Causa 1: o servidor não controla OPÇÕES
// 🐛 Express route only handles POST, not OPTIONS preflight
app.post('/api/users', createUser);
// The OPTIONS preflight hits no handler → 404/405 → preflight fails
// ✅ Use the cors middleware which handles OPTIONS automatically
const cors = require('cors');
app.use(cors({
origin: 'https://app.example.com',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}));
// cors() responds to preflight OPTIONS requests before your routes
Causa 2: OPÇÕES de faixas ou blocos de proxy reverso
# nginx — explicitly handle OPTIONS preflight
location /api/ {
# Handle preflight
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400 always;
add_header 'Content-Length' 0;
return 204; # respond to preflight with 204 No Content
}
proxy_pass http://localhost:3000;
add_header 'Access-Control-Allow-Origin' 'https://app.example.com' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
}
Causa 3: Origem do curinga com credenciais
// 🐛 Wildcard origin CANNOT be used with credentials
app.use(cors({
origin: '*', // ❌ fails when credentials: true
credentials: true,
}));
// Browser error: "Access-Control-Allow-Origin cannot be '*' when
// credentials mode is 'include'"
// ✅ Specify the exact origin
app.use(cors({
origin: 'https://app.example.com', // exact origin, not *
credentials: true,
}));
// ✅ For multiple origins, echo the matching one
const allowed = ['https://app.example.com', 'https://admin.example.com'];
app.use(cors({
origin: (origin, cb) => {
if (!origin || allowed.includes(origin)) cb(null, true);
else cb(new Error('Not allowed by CORS'));
},
credentials: true,
}));
Causa 4: Cabeçalhos permitidos ausentes
// The preflight lists headers the real request will send.
// The server must allow ALL of them, or preflight fails.
// 🐛 Real request sends Authorization but server doesn't allow it
allowedHeaders: ['Content-Type'], // missing Authorization
// ✅ Allow every custom header your client sends
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'X-API-Key'],
// Check the browser's preflight to see what's requested:
// Access-Control-Request-Headers: authorization, content-type
// The server's Access-Control-Allow-Headers must include all of these
Causa 5: O middleware de autenticação é executado antes do CORS
// 🐛 Auth middleware rejects the OPTIONS preflight (no auth header on preflight!)
app.use(authenticate); // blocks OPTIONS with 401
app.use(cors());
// ✅ CORS must come BEFORE auth so preflight passes
app.use(cors({ origin: 'https://app.example.com', credentials: true }));
app.use(authenticate); // preflight already handled by cors()
// Preflight OPTIONS requests carry no credentials by design —
// they must not be blocked by auth
Depurando falhas de comprovação
# Simulate the preflight with curl to see the server response
curl -X OPTIONS https://api.example.com/users \
-H "Origin: https://app.example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
-i
# Check the response has:
# HTTP/1.1 204 (or 200) ← must be 2xx
# Access-Control-Allow-Origin: https://app.example.com ← matches
# Access-Control-Allow-Methods: ...POST... ← includes your method
# Access-Control-Allow-Headers: ...content-type, authorization...
Perguntas Frequentes
P: Por que funciona no desenvolvimento, mas falha na produção?
R: O desenvolvimento geralmente usa um proxy (Vite/CRA) que evita totalmente o CORS ou uma configuração dev CORS permissiva. A produção tem um proxy reverso real (nginx) e uma configuração mais rígida onde o tratamento de comprovação deve ser explícito.
P: Por que minha solicitação OPTIONS retorna 401?
R: Seu middleware de autenticação está sendo executado antes do CORS e rejeitando a simulação (que não carrega credenciais por design). Coloque o middleware CORS antes da autenticação para que as solicitações de simulação passem.
P: Qual código de status a simulação deve retornar?
R: 204 (sem conteúdo) ou 200. Qualquer 2xx funciona. Um 404, 405 ou 401 significa que a simulação falhou e o navegador bloqueia a solicitação real.
P: Defino cabeçalhos CORS no nginx ou no aplicativo – não em ambos?
R: Escolha um lugar. DuplicarAccess-Control-Allow-Origin cabeçalhos (do nginx e do Express) fazem com que o navegador rejeite a resposta. Use nginx OU o aplicativo, não ambos.
P: Como posso permitir credenciais (cookies) de origem cruzada?
R: DefinirAccess-Control-Allow-Credentials: true E especifique uma origem exata (não*) no servidor e usecredentials: 'include' na busca do cliente. Todos os três são necessários juntos.
Conclusão
As falhas de comprovação do CORS na produção geralmente vêm de: o servidor não manipula OPTIONS, um proxy reverso bloqueando-o, origem curinga com credenciais, falta de cabeçalhos permitidos ou middleware de autenticação rejeitando a comprovação. As correções:use ocors middleware (ou manipulação explícita de OPTIONS nginx), especifique origens exatas ao usar credenciais, permita todos os cabeçalhos que seu cliente envia e coloque CORS antes do middleware de autenticação. Depure com umcurl -X OPTIONS solicitação para ver exatamente o que o servidor retorna — a simulação deve responder 2xx com cabeçalhos CORS correspondentes.
🔗 Share this article
✍️ Leave a Comment