1. O que é CORS de verdade
O navegador aplica a política de same-origin: scheme + host + porta. Se o frontend em https://app.exemplo.com chama https://api.exemplo.com, as origens são diferentes. Sem cabeçalhos CORS na resposta do servidor da API, o JavaScript da página não pode ler a resposta.
CORS não é firewall. A requisição pode chegar ao backend e até retornar 200; o bloqueio acontece no browser ao entregar o resultado ao seu código.
2. Sintomas no DevTools
- Console: Access to fetch … has been blocked by CORS policy
- Aba Network: request visível; às vezes preflight
OPTIONSem vermelho - No código:
TypeError: Failed to fetch(mensagem genérica)
3. Preflight (OPTIONS)
Para métodos não “simples” (POST com JSON, headers customizados como Authorization), o browser envia antes um OPTIONS. O servidor precisa responder com status 2xx e os headers Access-Control-Allow-*.
4. Correção no Express
import cors from "cors";
app.use(cors({
origin: ["https://app.exemplo.com"],
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
allowedHeaders: ["Content-Type", "Authorization"],
credentials: true, // só se usar cookies
}));
// ou manual:
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "https://app.exemplo.com");
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
if (req.method === "OPTIONS") return res.sendStatus(204);
next();
});
5. Correção no Nginx
location /api/ {
if ($request_method = OPTIONS) {
add_header Access-Control-Allow-Origin "https://app.exemplo.com";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
add_header Content-Length 0;
return 204;
}
proxy_pass http://127.0.0.1:3000;
add_header Access-Control-Allow-Origin "https://app.exemplo.com" always;
}
Access-Control-Allow-Origin: * com credentials: true. Em produção, liste origens explícitas.6. Desenvolvimento local
// vite.config.js — proxy evita CORS em dev
export default {
server: {
proxy: {
"/api": { target: "http://localhost:3000", changeOrigin: true }
}
}
}
7. Checklist final
- Origin do frontend está na allow-list
- OPTIONS responde 204/200 com Allow-Methods/Headers
- HTTPS consistente (sem misturar http/https)
- Se usa cookies:
Allow-Credentials+ origin explícita