CORS: por que esse erro aparece e como resolver de verdade
Entenda o que é CORS, por que o navegador bloqueia a requisição mesmo quando a API responde normalmente, e como configurar os headers certos no backend pra resolver o erro sem desligar a proteção com um curinga solto em produção.
Poucos erros aparecem tão cedo na carreira de quem mexe com frontend e backend juntos quanto o famoso blocked by CORS policy. A API funciona perfeitamente no Postman, responde certinho, mas o navegador recusa entregar a resposta pro seu código JavaScript. Isso confunde bastante gente, porque parece que a API está quebrada quando na real o problema é uma proteção do próprio navegador.
O que é CORS
CORS significa Cross-Origin Resource Sharing, e é um mecanismo de segurança do navegador, não do servidor. A regra por trás dele é o same-origin policy: por padrão, um site só pode fazer requisição livre pra própria origem (mesmo protocolo, mesmo domínio, mesma porta). Se o seu frontend está em https://meusite.com e a API está em https://api.meusite.com, isso já conta como origens diferentes, e o navegador trata essa requisição como cross-origin.
Isso existe pra evitar um cenário específico: um site malicioso, aberto no seu navegador, fazendo requisição silenciosa pra outro site onde você está logado, usando os seus cookies sem você saber. Sem essa proteção, qualquer página aberta numa aba poderia tentar ler dado de outro site em seu nome.
O erro não é do servidor, é do navegador
Isso explica por que o Postman funciona e o navegador não: o Postman não é um navegador, não aplica same-origin policy nenhuma. A API de fato responde, o dado de fato chega pela rede, só que o navegador intercepta a resposta antes de entregar ela pro seu JavaScript, e bloqueia se o servidor não autorizou explicitamente aquela origem.
Como o navegador confere a permissão
Quando o frontend faz uma requisição cross-origin, o navegador confere se a resposta do servidor inclui o header certo:
Access-Control-Allow-Origin: https://meusite.com
Se esse header não vier, ou vier com uma origem diferente da que fez a requisição, o navegador descarta a resposta e mostra o erro de CORS no console. A correção não acontece no frontend, é o backend que precisa devolver esse header dizendo quais origens têm permissão de ler aquela resposta.
Em Node com Express, o pacote cors resolve isso de forma direta:
import cors from "cors";
app.use(
cors({
origin: "https://meusite.com",
})
);Requisição preflight
Pra requisições mais sensíveis (métodos como PUT ou DELETE, ou com headers customizados tipo Authorization), o navegador manda uma requisição extra antes da real, chamada preflight, usando o método OPTIONS. Essa requisição pergunta pro servidor, com antecedência, se aquele método, aquele header e aquela origem específica têm permissão, antes de mandar a requisição de verdade com o corpo de dado.
OPTIONS /api/usuarios
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: authorization
Origin: https://meusite.com
O servidor responde confirmando o que é permitido:
Access-Control-Allow-Origin: https://meusite.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: authorization, content-type
Só depois dessa troca o navegador manda a requisição real. Isso explica outro sintoma comum: abrir a aba de rede e ver uma requisição OPTIONS estranha antes da requisição que você esperava, sem entender de onde ela veio.
O erro de simplesmente liberar tudo
A solução mais rápida, e mais perigosa, é usar um curinga:
app.use(cors({ origin: "*" }));Isso libera qualquer origem a ler a resposta da API, o que resolve o erro na hora, mas em produção isso anula boa parte da proteção que o CORS oferece, principalmente em rotas que dependem de cookie de sessão pra autenticar (o curinga nem funciona junto com credentials: true, o navegador recusa essa combinação por ser perigosa demais). O caminho correto é listar explicitamente as origens que realmente deveriam ter acesso:
const origensPermitidas = ["https://meusite.com", "https://admin.meusite.com"];
app.use(
cors({
origin: (origin, callback) => {
if (origensPermitidas.includes(origin)) {
callback(null, true);
} else {
callback(new Error("Origem não permitida"));
}
},
credentials: true,
})
);Fechando
CORS não é um bug irritante que aparece por acaso, é uma proteção do navegador funcionando exatamente como deveria, só que exigindo que o servidor declare de forma explícita quem pode consumir aquela API. O erro no console é, na prática, o navegador avisando que o backend esqueceu de dar essa permissão. Resolver de verdade é configurar a origem certa, entender o preflight quando ele aparecer, e resistir à tentação do curinga solto assim que o projeto sair do ambiente de desenvolvimento.
Leia também
- Webhooks: o que são e como validar com segurança12 de setembro de 2026 · 4 min
- OAuth: como funciona o login com Google por trás dos panos11 de setembro de 2026 · 4 min
- 3 comandos nativos para gastar menos token no Claude Code10 de setembro de 2026 · 6 min
- Agent loops: fazendo o Claude Code se corrigir sozinho8 de setembro de 2026 · 8 min