--:--:--

Erro de CORS: como corrigir bloqueio entre frontend e API sem liberar tudo

Publicado em 2026-08-29T20:14:00Z · atualizado em 2026-08-29T19:14:25+00:00

O erro de CORS costuma aparecer quando um frontend funciona no Postman ou no `curl`, mas falha no navegador com mensagens como **blocked by CORS policy**, **No Access-Control-Allow-Origin header** ou **Response to preflight request doesn't

Fluxo de uma requisição CORS entre navegador, preflight e API
Imagem de apoio ao tema do artigo.

Erro de CORS: como corrigir bloqueio entre frontend e API sem liberar tudo

O erro de CORS costuma aparecer quando um frontend funciona no Postman ou no curl, mas falha no navegador com mensagens como blocked by CORS policy, No Access-Control-Allow-Origin header ou Response to preflight request doesn't pass access control check. O navegador recebeu uma resposta, porém decidiu não entregá-la ao JavaScript porque a API não autorizou aquela origem de acordo com o protocolo CORS.

A correção não é instalar uma extensão que desativa a proteção nem adicionar Access-Control-Allow-Origin: * em todo lugar. O caminho seguro é identificar a origem exata, observar se houve uma requisição OPTIONS, responder aos cabeçalhos solicitados e tratar credenciais de forma explícita. Este guia cria um laboratório mínimo, mostra como ler a aba Network e percorre configurações de Node.js, Nginx e desenvolvimento local.

Fluxo de uma requisição CORS entre navegador, preflight e API Legenda: em uma requisição não simples, o navegador consulta a política com OPTIONS antes de enviar a operação real.

O que CORS protege — e o que ele não protege

CORS, sigla de Cross-Origin Resource Sharing, é um protocolo HTTP usado pelo navegador para decidir se um script de uma origem pode ler uma resposta de outra. Uma origem combina esquema, host e porta. Portanto, https://app.exemplo.com, http://app.exemplo.com e https://app.exemplo.com:8443 são três origens diferentes.

A política de mesma origem existe no cliente. Ela não impede que o servidor receba requisições de outros programas. É por isso que curl pode funcionar enquanto o JavaScript falha. CORS também não substitui autenticação, autorização, validação, proteção contra CSRF ou limitação de tráfego. Permitir uma origem diz ao navegador que a resposta pode ser exposta àquela página; não transforma uma API privada em uma API autenticada.

Antes de alterar código, registre quatro dados: URL da página, URL da API, método HTTP e presença de cookies ou cabeçalho Authorization. Eles determinam qual resposta CORS é necessária.

Reproduza o erro em um laboratório pequeno

Crie uma API local em uma porta e uma página em outra. O servidor abaixo usa apenas módulos nativos do Node.js:

// api.mjs
import http from 'node:http';

http.createServer((req, res) => {
  res.writeHead(200, {'Content-Type': 'application/json'});
  res.end(JSON.stringify({status: 'ok'}));
}).listen(3001, () => console.log('API em http://localhost:3001'));
<!-- index.html -->
<button id="carregar">Carregar</button>
<pre id="saida"></pre>
<script>
document.querySelector('#carregar').onclick = async () => {
  const resposta = await fetch('http://localhost:3001/dados');
  document.querySelector('#saida').textContent = await resposta.text();
};
</script>

Sirva a página com python -m http.server 3000 e abra http://localhost:3000. A API responde, mas não declara que a origem http://localhost:3000 pode ler o resultado. O console mostra o bloqueio. Esse caso isolado evita confundir CORS com DNS, certificado ou rota ausente.

Leia a aba Network antes do console

Abra as ferramentas do desenvolvedor, selecione Network, repita a ação e procure a requisição. O console resume o problema; a rede mostra o que realmente ocorreu. Confira:

  • Request URL, método e código de status;
  • cabeçalho de requisição Origin;
  • resposta Access-Control-Allow-Origin;
  • existência de uma chamada OPTIONS imediatamente antes;
  • redirecionamento, erro TLS ou resposta do proxy;
  • cabeçalhos Access-Control-Request-Method e Access-Control-Request-Headers no preflight.

Um status 500 sem cabeçalhos CORS pode ser apresentado como erro de CORS, escondendo a falha original. Inspecione os logs da API no mesmo horário. Se nem há resposta HTTP, resolva conectividade primeiro. A ferramenta de inspeção de cabeçalhos HTTP do IATechNerds pode organizar os cabeçalhos de um endpoint público, mas não substitui a aba Network para uma sessão autenticada.

Entenda quando o navegador envia preflight OPTIONS

Certas requisições podem ser enviadas diretamente e verificadas pela resposta. Outras exigem uma consulta prévia chamada preflight. Ela usa OPTIONS para perguntar se a origem, o método e os cabeçalhos pretendidos são permitidos.

Um POST com Content-Type: application/json, um PUT, um DELETE ou um cabeçalho Authorization normalmente aciona preflight. Exemplo conceitual:

OPTIONS /pedidos HTTP/1.1
Origin: https://app.exemplo.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization,content-type

A resposta deve cobrir o que foi solicitado:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.exemplo.com
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Vary: Origin

Se o roteador exige login antes de responder ao OPTIONS, o navegador pode nunca enviar o POST. Trate a consulta de política antes da autenticação da operação real, sem liberar o recurso.

Corrija a API Node.js com uma lista de origens

Uma implementação mínima deve comparar a origem com uma lista conhecida, refletir somente valores aprovados e encerrar o preflight. Este exemplo não depende de framework:

const permitidas = new Set([
  'http://localhost:3000',
  'https://app.exemplo.com'
]);

function aplicarCors(req, res) {
  const origem = req.headers.origin;
  if (origem && permitidas.has(origem)) {
    res.setHeader('Access-Control-Allow-Origin', origem);
    res.setHeader('Vary', 'Origin');
    res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Authorization,Content-Type');
  }
  if (req.method === 'OPTIONS') {
    res.writeHead(204);
    res.end();
    return true;
  }
  return false;
}

Chame aplicarCors no início do servidor. Em Express, Fastify, Django, Flask, Laravel ou Spring, use o middleware oficial da plataforma, mas mantenha a mesma política: origens explícitas, métodos necessários e cabeçalhos previstos. Não aceite uma origem apenas porque termina com um texto parecido; malicioso-exemplo.com pode passar em comparações ingênuas com endsWith.

Credenciais não funcionam com origem curinga

Quando o frontend envia cookies entre origens, ele precisa declarar credenciais:

const resposta = await fetch('https://api.exemplo.com/perfil', {
  credentials: 'include'
});

A API deve responder com a origem exata e autorizar credenciais:

Access-Control-Allow-Origin: https://app.exemplo.com
Access-Control-Allow-Credentials: true
Vary: Origin

O valor * não é compatível com uma resposta credenciada. Além disso, cookies continuam sujeitos a atributos como Secure, HttpOnly e SameSite. CORS correto não conserta um cookie rejeitado pelo navegador. Verifique a seção Cookies nas ferramentas do desenvolvedor e diferencie “a resposta foi bloqueada” de “a sessão não foi enviada”.

Se a aplicação usa token em Authorization, o preflight deve permitir esse nome em Access-Control-Allow-Headers. Não coloque o token em query string para evitar o preflight; URLs aparecem em históricos e logs.

Configure o Nginx sem duplicar cabeçalhos

Quando Nginx e aplicação adicionam CORS ao mesmo tempo, podem surgir dois cabeçalhos Access-Control-Allow-Origin. O navegador espera um único valor válido. Defina um responsável pela política. Se o proxy for esse responsável, uma configuração simplificada pode ser:

location /api/ {
    if ($request_method = OPTIONS) {
        add_header Access-Control-Allow-Origin "https://app.exemplo.com" always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
        add_header Vary "Origin" always;
        return 204;
    }

    add_header Access-Control-Allow-Origin "https://app.exemplo.com" always;
    add_header Vary "Origin" always;
    proxy_pass http://aplicacao;
}

O exemplo permite uma origem fixa. Para múltiplas origens, prefira map com lista controlada em vez de refletir qualquer valor. Revise como add_header é herdado entre blocos e use always quando a política também precisa aparecer em respostas de erro. Teste a configuração antes de recarregar o serviço.

Redirecionamentos podem quebrar o preflight

Uma rota http://api.exemplo.com que redireciona para HTTPS, ou /api que redireciona para /api/, pode falhar de forma diferente entre navegadores e ambientes. Aponte o frontend diretamente para a URL canônica. Veja a cadeia na aba Network ou teste:

curl -i -X OPTIONS 'https://api.exemplo.com/pedidos' \
  -H 'Origin: https://app.exemplo.com' \
  -H 'Access-Control-Request-Method: POST' \
  -H 'Access-Control-Request-Headers: authorization,content-type'

O curl não aplica a política como um navegador, mas mostra a resposta bruta. Procure código 200 ou 204 e os cabeçalhos esperados. Se receber 301, 401, 403 ou 404, descubra qual camada respondeu. Não confunda um teste de cabeçalhos com prova de que o fluxo completo do navegador está correto.

Cache e CDN exigem Vary: Origin

Quando o servidor escolhe a origem permitida dinamicamente, a resposta varia conforme o cabeçalho Origin. Vary: Origin informa a caches intermediários que respostas para origens distintas não são intercambiáveis. Sem isso, uma CDN pode armazenar a resposta destinada a uma origem e entregá-la a outra, causando falha intermitente ou política incorreta.

O preflight pode ser armazenado pelo navegador com Access-Control-Max-Age, reduzindo chamadas OPTIONS. Use um período moderado durante mudanças. Um valor muito longo faz configurações antigas persistirem no cliente; um valor muito curto aumenta tráfego. Ao testar, desative temporariamente o cache na aba Network e confirme os cabeçalhos na resposta mais recente.

Matriz de decisão para origem, credenciais, cache e resposta CORS Legenda: a política correta depende da combinação entre origem aprovada, uso de credenciais e cabeçalhos solicitados.

CORS em desenvolvimento com proxy local

Servidores de desenvolvimento como Vite e ferramentas equivalentes podem encaminhar /api para outro processo. Para o navegador, página e endpoint parecem compartilhar a mesma origem; o proxy faz a chamada no servidor. Isso é útil para desenvolvimento e evita configurar origens temporárias, mas não é a política de produção.

Exemplo conceitual no Vite:

export default {
  server: {
    proxy: {
      '/api': {target: 'http://localhost:3001', changeOrigin: true}
    }
  }
};

No frontend, use fetch('/api/dados'). Documente a diferença entre ambientes e teste também contra a arquitetura real. Se produção usa domínios separados, ela continuará precisando de CORS. Não exponha uma rota de proxy aberta que aceite destino arbitrário.

Erros que parecem CORS, mas não são

Certificados inválidos, DNS, bloqueadores, extensão do navegador, Mixed Content e uma API fora do ar podem produzir mensagens próximas. Se uma página HTTPS chama uma API HTTP, o navegador pode bloquear conteúdo misto antes de CORS. Se a requisição aparece com status 404, a rota ou o método pode estar errado.

Também existe diferença entre CORS e CSP. Content Security Policy pode impedir que a página conecte a uma origem não listada em connect-src; nesse caso, alterar a API não basta. Confira a mensagem completa e os cabeçalhos da página. Use a ferramenta de análise de CSP para revisar a diretiva, sem colar segredos ou tokens.

Faça o diagnóstico em camadas: transporte, resposta HTTP, política da página e política CORS. Essa ordem evita mudar cinco configurações para corrigir uma porta errada.

Não use no-cors para “resolver” a leitura

Definir mode: 'no-cors' não torna uma resposta privada legível. O JavaScript recebe uma resposta opaca, sem acesso normal a status, cabeçalhos ou corpo. O modo serve a casos específicos de recursos que podem ser enviados sem inspeção do resultado, não a uma API JSON comum.

Extensões que desativam CORS só mudam o seu navegador. Elas não corrigem usuários reais e podem enfraquecer a navegação. JSONP é uma técnica antiga limitada a GET e não deve ser introduzida como contorno moderno. A solução pertence à arquitetura: mesma origem, proxy controlado ou política explícita na API.

Teste automaticamente origens permitidas e negadas

Uma boa suíte verifica tanto o caso que deve funcionar quanto aquele que deve falhar. O teste pode iniciar a aplicação e enviar preflight com origens distintas:

import assert from 'node:assert/strict';

async function preflight(origin) {
  return fetch('http://localhost:3001/pedidos', {
    method: 'OPTIONS',
    headers: {
      Origin: origin,
      'Access-Control-Request-Method': 'POST',
      'Access-Control-Request-Headers': 'authorization,content-type'
    }
  });
}

const aceita = await preflight('https://app.exemplo.com');
assert.equal(aceita.headers.get('access-control-allow-origin'),
             'https://app.exemplo.com');

const nega = await preflight('https://origem-invalida.test');
assert.equal(nega.headers.get('access-control-allow-origin'), null);

Inclua cenários com erro 500, rota inexistente e credenciais. Uma política que só funciona em respostas 200 cria diagnósticos confusos quando a aplicação falha.

Checklist de correção segura

Antes de considerar o incidente resolvido, confirme:

  1. A origem contém protocolo, host e porta exatos.
  2. A requisição OPTIONS chega à camada responsável e recebe 200 ou 204.
  3. Métodos e cabeçalhos permitidos cobrem somente o necessário.
  4. Credenciais usam origem explícita e Access-Control-Allow-Credentials: true.
  5. Há apenas um cabeçalho Access-Control-Allow-Origin.
  6. Respostas dinâmicas incluem Vary: Origin.
  7. Erros, redirecionamentos e cache foram testados.
  8. Uma origem não autorizada permanece sem acesso.

Registre a decisão no repositório. A lista de origens é parte da superfície de segurança, não um ajuste temporário de navegador. Para conferir URLs e portas usadas no deploy, a ferramenta de decomposição de URL ajuda a visualizar os componentes da origem.

Faça ainda um teste em janela anônima ou perfil limpo, porque service workers e extensões podem manter versões antigas do frontend. Compare a requisição que falha com uma que funciona, campo por campo, sem copiar cookies ou tokens para serviços externos. Em aplicações com múltiplos ambientes, mantenha listas separadas para desenvolvimento, homologação e produção. Uma origem de preview temporária não deve permanecer autorizada indefinidamente. Monitore recusas no servidor por categoria, mas evite registrar o valor completo de credenciais. Se a política depender de subdomínios criados dinamicamente, valide o host com um parser de URL e uma regra de domínio controlado; expressões regulares improvisadas podem aceitar nomes parecidos. Por fim, teste em ao menos dois navegadores suportados e em dispositivo móvel quando o produto for usado ali. A implementação do protocolo é padronizada, mas cache, cookies e ferramentas de diagnóstico podem mostrar sintomas diferentes.

Documentação primária

Autoria, revisão e método de produção

Autoria editorial: Equipe IATechNerds. Revisão técnica e editorial: pendente, obrigatória antes da publicação. Este rascunho inédito foi produzido com apoio de IA generativa, consulta à documentação primária e exemplos fictícios reproduzíveis. Nenhuma configuração foi aplicada automaticamente a servidor real; comandos, política de origens e links internos devem ser verificados pelo editor responsável.

Camada extra: como tomar uma decisão melhor neste cenário

Para levar Erro de CORS: como corrigir bloqueio entre frontend e API sem liberar tudo além de uma receita de passos, vale transformar o procedimento em um pequeno método: observar, isolar, alterar uma variável e conferir o resultado.

O erro de CORS costuma aparecer quando um frontend funciona no Postman ou no `curl`, mas falha no navegador com mensagens como **blocked by CORS policy**, **No Access-Control-Allow-Origin header** ou **Response to preflight request doesn't A ideia central pode ser testada com quatro perguntas: o que foi observado, qual hipótese explica o sintoma, qual mudança é reversível e qual evidência prova que funcionou. Esse encadeamento evita que uma coincidência seja confundida com causa e torna o procedimento repetível por outra pessoa.

Separe exposição, probabilidade e impacto

Em segurança e privacidade, um alerta isolado não mede o risco inteiro. Para CORS, HTTP, frontend, API, registre primeiro o que está exposto: conta, arquivo, dispositivo, sessão, metadado ou autorização. Depois avalie como o abuso poderia acontecer e o que seria perdido se acontecesse. Essa separação ajuda a priorizar uma credencial comprometida acima de um aviso meramente visual e, ao mesmo tempo, evita transformar qualquer comportamento incomum em prova de ataque.

Prefira respostas reversíveis no primeiro minuto: interromper uma transação, revogar uma sessão, remover uma permissão, confirmar a identidade por outro canal ou trabalhar em uma cópia. Só depois faça mudanças permanentes. O objetivo não é reagir com medo, mas reduzir a janela de exposição enquanto você coleta evidências.

Mini matriz de decisão

SinalO que verificarAção inicial
Pedido inesperado ou comportamento fora do padrãoIdentidade, domínio, origem e contextoNão confirmar pela mesma mensagem
Permissão ou acesso excessivoNecessidade real e escopoReduzir ao mínimo necessário
Dado sensível envolvidoOnde será processado e armazenadoPreferir fluxo local ou serviço confiável

O teste que evita falsa sensação de segurança

Depois da correção, repita o cenário original de forma controlada. Verifique se a conta continua funcional, se a permissão removida não reapareceu, se o arquivo não contém a informação que deveria ter sido removida e se o canal alternativo de confirmação realmente funciona. Segurança sem teste de recuperação é apenas configuração; a validação transforma a configuração em controle.

Checklist de fechamento

  • Registre o estado inicial antes de alterar qualquer coisa.
  • Faça uma mudança por rodada e anote o efeito.
  • Teste um caso normal, um caso-limite e o cenário que originalmente falhou.
  • Confirme que a solução continua válida depois de reiniciar, reabrir ou repetir o fluxo.
  • Guarde uma forma de voltar ao estado anterior quando a alteração for destrutiva.

Esse método acrescenta profundidade sem transformar o artigo em teoria abstrata: o leitor entende por que cada passo existe e como reconhecer quando a situação exige uma decisão diferente.

#CORS #HTTP #frontend #API #segurança