Como Corrigir Erros CORS: Depurando Access-Control-Allow-Origin

Encontrando um erro de CORS? Saiba o que o desencadeia, como o preflight funciona, as 6 falhas mais comuns de Access-Control-Allow-Origin e a correção exata para cada uma.

Ashley Innocent

Ashley Innocent

31 agosto 2026

Como Corrigir Erros CORS: Depurando Access-Control-Allow-Origin

Apidog para empresas

Implantação local

SSO & RBAC

Conforme SOC 2

Explorar Apidog Enterprise

Você lança um novo frontend, abre o console, e lá está: um erro CORS vermelho dizendo que a requisição foi “bloqueada pela política CORS”. Sua API funciona bem no Apidog ou no curl, mas o navegador se recusa a entregar a resposta ao seu JavaScript. Frustrante? Sim. Misterioso? Não, uma vez que você sabe onde o erro se encontra.

Aqui está o fato central que a maioria dos tutoriais esconde: um erro CORS é imposto pelo navegador, mas causado pelo servidor. O navegador bloqueia a resposta porque seu servidor não enviou os cabeçalhos Access-Control-Allow-Origin corretos. Portanto, a correção quase sempre acontece na configuração do servidor, não no seu código frontend.

Este guia aborda o que o CORS faz, como funciona a requisição de pré-checagem (preflight), as seis mensagens de erro CORS mais comuns com a correção exata para cada uma, e configurações funcionais para Express, Spring Boot e Nginx. Você também verá como depurar fora do navegador, que é a maneira mais rápida de diferenciar “servidor mal configurado” de “navegador bloqueado”.

O que é um erro CORS (e o que não é)

CORS significa Cross-Origin Resource Sharing (Compartilhamento de Recursos de Origem Cruzada). Por padrão, os navegadores impõem a política de mesma origem: JavaScript em execução em https://app.example.com não pode ler respostas de https://api.example.com, porque o esquema, host ou porta são diferentes. CORS é o mecanismo que os servidores usam para relaxar essa regra intencionalmente. Os detalhes completos estão na documentação CORS do MDN, e o algoritmo subjacente é definido na especificação Fetch.

Três pontos esclarecem a maioria das confusões:

Então, quando você vir um erro CORS, não procure por uma solução alternativa no frontend. Leia a mensagem de erro e, em seguida, corrija o cabeçalho ausente ou incorreto no servidor.

Anatomia da requisição de pré-checagem (preflight)

Antes de certas requisições de origem cruzada, o navegador envia um 'batedor': uma requisição OPTIONS chamada preflight (pré-checagem). Ela é disparada quando sua requisição usa métodos além de GET, HEAD ou POST, envia cabeçalhos personalizados como Authorization, ou usa um Content-Type como application/json.

O preflight se parece com isto:

OPTIONS /v1/orders HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: authorization, content-type

O navegador está perguntando: “Uma página em app.example.com quer fazer um POST aqui com estes cabeçalhos. Permitido?” Uma resposta correta do servidor:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Max-Age: 86400
Vary: Origin

Se alguma parte estiver faltando, o navegador cancela a requisição real antes mesmo de ela ser disparada. Seu endpoint da API nunca é executado, seus logs não mostram nada além de um hit de OPTIONS, e o console exibe um erro CORS. Access-Control-Max-Age informa ao navegador para armazenar em cache este veredito (86400 segundos aqui), para que requisições repetidas pulem o preflight.

Mantenha esta dança de dois passos em mente. Metade de toda a depuração CORS se resume a uma pergunta: o preflight falhou ou a requisição real falhou?

Os 6 erros CORS mais comuns e como corrigir cada um

Os navegadores escrevem mensagens de erro CORS surpreendentemente precisas. Compare a sua com a lista abaixo.

1. Nenhum cabeçalho ‘Access-Control-Allow-Origin’ está presente

O clássico. Seu servidor enviou uma resposta sem nenhum cabeçalho CORS. O navegador não tinha nada para avaliar, então bloqueou o acesso.

Correção: Configure o servidor para enviar Access-Control-Allow-Origin com a origem solicitante específica ou * para APIs públicas e sem credenciais:

Access-Control-Allow-Origin: https://app.example.com

Uma armadilha: respostas de erro frequentemente ignoram os cabeçalhos CORS, mesmo quando as respostas de sucesso os incluem. Se sua API retornar um 500 e o middleware apenas decorar 200s, o console exibirá um erro CORS em vez do erro real do servidor. Certifique-se de que os cabeçalhos CORS estejam anexados a cada resposta, incluindo páginas 403 Forbidden e 500.

2. Wildcard ‘*’ não pode ser usado com credenciais

A mensagem diz: “O valor do cabeçalho ‘Access-Control-Allow-Origin’ não deve ser o wildcard ‘*’ quando o modo de credenciais da requisição é ‘include’.”

Seu frontend envia cookies ou cabeçalhos de autenticação com credentials: 'include', mas o servidor responde com Access-Control-Allow-Origin: *. A especificação Fetch proíbe essa combinação; um wildcard mais credenciais permitiria que qualquer site na internet lesse respostas autenticadas.

Correção: Repita a origem exata em vez do wildcard, e adicione o cabeçalho de credenciais:

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

Valide a Origin de entrada contra uma lista de permissões antes de repeti-la. Refletir origens arbitrárias com credenciais habilitadas anula toda a proteção.

3. Resposta à requisição de pré-checagem (preflight) não passa na verificação de controle de acesso

Seu servidor nunca tratou a requisição OPTIONS. Talvez a rota apenas defina POST, então OPTIONS retorna um 404 ou 405. Talvez um middleware de autenticação a rejeitou com um 401 porque o preflight não carrega nenhum token (navegadores nunca anexam credenciais a preflights).

Correção: Trate OPTIONS explicitamente e retorne um 2xx com o conjunto completo de cabeçalhos CORS antes que a autenticação seja executada. Na maioria dos frameworks, montar o middleware CORS primeiro resolve. Se você estiver escrevendo manualmente:

app.options('/v1/orders', (req, res) => {
  res.set({
    'Access-Control-Allow-Origin': 'https://app.example.com',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
    'Access-Control-Allow-Headers': 'Authorization, Content-Type'
  });
  res.sendStatus(204);
});

4. O valor do cabeçalho não é igual à origem fornecida

O servidor envia um cabeçalho Access-Control-Allow-Origin, mas ele nomeia a origem errada. Causas comuns: uma origem de produção hardcoded enquanto você testa de http://localhost:5173, uma comparação de lista de permissões falhando em http vs https, ou uma barra final errante (https://app.example.com/ não é um valor de origem válido).

Correção: Compare o cabeçalho Origin da requisição com sua lista de permissões exatamente, repita a correspondência e envie Vary: Origin para que caches e CDNs não sirvam o cabeçalho de uma origem para outra:

const allowed = ['https://app.example.com', 'http://localhost:5173'];
if (allowed.includes(req.headers.origin)) {
  res.set('Access-Control-Allow-Origin', req.headers.origin);
  res.set('Vary', 'Origin');
}

5. Campo de cabeçalho da requisição ou método não permitido

Duas mensagens irmãs: “Campo de cabeçalho da requisição authorization não é permitido por Access-Control-Allow-Headers na resposta do preflight” e “Método PUT não é permitido por Access-Control-Allow-Methods.”

O preflight foi bem-sucedido, mas sua resposta não cobriu o que sua requisição precisa. Você adicionou um cabeçalho Authorization ou um X-Request-Id, e a lista de permissões do servidor nunca o mencionou.

Correção: Estenda a resposta do preflight para incluir cada cabeçalho e método que seu frontend envia:

Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Authorization, Content-Type, X-Request-Id

Os nomes dos cabeçalhos aqui não diferenciam maiúsculas de minúsculas. Os métodos diferenciam maiúsculas de minúsculas e são em maiúsculas.

6. Redirecionamento não é permitido para uma requisição de pré-checagem (preflight)

O preflight atingiu uma URL retornando 301 ou 302, e os navegadores se recusam a seguir redirecionamentos durante o preflight. Culpados típicos: uma URL http redirecionando para https, uma barra final ausente que seu framework “utilmente” redireciona, ou um gateway redirecionando /v1/orders para /v1/orders/.

Correção: Aponte seu frontend diretamente para a URL final. Use https desde o início, combine a convenção de barra final do seu roteador e confirme com uma chamada OPTIONS manual para verificar se o endpoint responde com um 2xx em vez de um 3xx.

Exemplos de configuração do servidor

Aqui está a configuração CORS correta em três stacks comuns.

Express

Use o middleware cors oficial em vez de criar os cabeçalhos manualmente:

const express = require('express');
const cors = require('cors');
const app = express();

app.use(cors({
  origin: ['https://app.example.com', 'http://localhost:5173'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Authorization', 'Content-Type'],
  credentials: true,
  maxAge: 86400
}));

Monte-o antes do seu middleware de autenticação para que os preflights nunca sejam rejeitados por falta de tokens. Desenvolvedores Python obtêm o mesmo padrão da extensão Flask-CORS, que encapsula a lógica de cabeçalho idêntica para aplicativos Flask.

Spring Boot

Configuração global via WebMvcConfigurer:

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/v1/**")
            .allowedOrigins("https://app.example.com")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("Authorization", "Content-Type")
            .allowCredentials(true)
            .maxAge(86400);
    }
}

Usando Spring Security? Chame .cors(Customizer.withDefaults()) na sua cadeia de filtros de segurança também, ou a camada de segurança bloqueará os preflights antes mesmo que a configuração MVC os veja. Consulte a documentação CORS do Spring para o conjunto completo de opções.

Nginx

Quando o Nginx termina as requisições na frente do seu aplicativo, responda aos preflights na borda:

location /v1/ {
    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 "Authorization, Content-Type" always;
        add_header Access-Control-Max-Age 86400 always;
        return 204;
    }
    add_header Access-Control-Allow-Origin "https://app.example.com" always;
    add_header Vary "Origin" always;
    proxy_pass http://backend;
}

A flag always é importante. Sem ela, o Nginx ignora as diretivas add_header em respostas 4xx e 5xx, o que recria o erro número um em cada requisição falha. E escolha uma camada para gerenciar o CORS: se tanto o Nginx quanto seu aplicativo adicionarem cabeçalhos, os navegadores verão duplicatas como Access-Control-Allow-Origin: *, * e rejeitarão a resposta.

Depure CORS fora do navegador com Apidog

O erro do console informa que o navegador bloqueou algo. Ele não diz o que o servidor enviou. A maneira mais rápida de ver a verdade é tirar o navegador do circuito.

Apidog é um cliente de API de desktop, então suas requisições não estão sujeitas a verificações CORS do navegador. Isso lhe dá um experimento limpo: envie a mesma requisição do Apidog que seu frontend estava fazendo. Se ela for bem-sucedida lá, sua lógica de API está boa e o problema é puramente a falta de cabeçalhos CORS. Se falhar lá também, você tem um bug comum na API disfarçado de CORS, e técnicas gerais de teste de API se aplicam.

Uma sessão de depuração CORS no Apidog se parece com isto:

  1. Repita a requisição real. Copie a requisição falha da aba Rede do seu navegador e recrie-a no Apidog com o mesmo método, cabeçalhos e corpo. Verifique o status e o corpo. Um 500 aqui significa que CORS nunca foi seu problema.
  2. Teste o preflight manualmente. Crie uma nova requisição, defina o método como OPTIONS e adicione os cabeçalhos que um navegador enviaria: Origin: https://app.example.com, Access-Control-Request-Method: POST e Access-Control-Request-Headers: authorization, content-type. Envie-a.
  3. Inspecione os cabeçalhos da resposta. No painel de resposta, procure por Access-Control-Allow-Origin, Access-Control-Allow-Methods e Access-Control-Allow-Headers. Compare cada valor com o que seu frontend precisa. Um cabeçalho ausente, uma origem errada ou um status 3xx salta à vista imediatamente, sem a necessidade de adivinhação no console.
  4. Verifique a correção. Após alterar a configuração do servidor, reenvie a mesma requisição OPTIONS salva e observe os cabeçalhos serem atualizados. Sem necessidade de reimplantar frontends, sem rituais de limpeza de cache.

Este fluxo de trabalho também resolve o eterno argumento “funciona no meu cliente de API, falha no navegador” em segundos, o mesmo enigma por trás da pergunta de teste CORS do Postman. O cliente funciona porque ignora o CORS. O navegador falha porque seu servidor não pronunciou as palavras mágicas. Baixe o Apidog gratuitamente e mantenha a requisição OPTIONS salva ao lado de seus testes de endpoint regulares; futuros incêndios CORS serão apagados em um clique.

Uma checklist CORS de 30 segundos

Antes de registrar o bug, revise esta lista:

Nove em cada dez vezes, uma dessas seis linhas é sua resposta. Verifique com uma requisição OPTIONS manual no Apidog, corrija a configuração do servidor e volte a desenvolver.

FAQ

Por que recebo um erro CORS apenas no navegador?

Porque apenas os navegadores impõem o CORS. A política de mesma origem protege os usuários de páginas maliciosas que leem seus dados autenticados, então os navegadores verificam Access-Control-Allow-Origin em cada resposta de origem cruzada. curl, serviços de backend e clientes de desktop não possuem tal regra. Se uma requisição é bem-sucedida em todos os lugares, exceto no navegador, seu servidor está faltando ou configurando incorretamente os cabeçalhos CORS; a própria API está saudável.

O CORS se aplica ao Postman ou Apidog?

Não. Postman e Apidog são aplicativos de desktop, não páginas web executadas dentro de uma sandbox de navegador, então suas requisições ignoram completamente o CORS. Isso é precisamente o que os torna úteis para depuração CORS: eles mostram os cabeçalhos de resposta brutos do servidor sem a filtragem do navegador. A confusão do teste CORS do Postman geralmente começa aqui; uma requisição bem-sucedida em um cliente de desktop não prova nada sobre o comportamento do navegador, mas isola a camada que está falhando.

Um erro CORS é um recurso de segurança ou um bug?

Um recurso. Erros CORS significam que o navegador está fazendo seu trabalho: recusando-se a expor dados de resposta de origem cruzada a scripts, a menos que o servidor consinta. Desativar o CORS no navegador com flags ou extensões esconde o sintoma na sua máquina enquanto cada usuário ainda encontra o problema. Corrija os cabeçalhos do servidor em vez disso.

Posso usar Access-Control-Allow-Origin: * em todos os lugares?

Apenas para APIs públicas e somente leitura, sem cookies ou autenticação. O wildcard é rejeitado sempre que credenciais são incluídas, e ele anuncia que seus dados estão abertos para qualquer origem na web. Para qualquer coisa autenticada, mantenha uma lista de permissões de origem, repita a origem correspondente e envie Vary: Origin para que caches compartilhados mantenham as respostas separadas.

Pratique o design de API no Apidog

Descubra uma forma mais fácil de construir e usar APIs