05 de nov. de 2025·8 min de leitura

Erros de CORS após o deploy: causas reais e correções

Erros de CORS após o deploy normalmente vêm de falhas no preflight, configurações de credenciais ou origens curinga. Aprenda um checklist repetível para consertar.

Erros de CORS após o deploy: causas reais e correções

Por que o CORS pode falhar de repente depois do deploy

CORS é uma regra do navegador, não do servidor. Seu JavaScript no frontend roda numa aba do navegador, e é o navegador quem decide se pode ler a resposta vinda de outra origem (domínio, protocolo ou porta). Se o backend não retornar os cabeçalhos CORS corretos, o navegador bloqueia a resposta mesmo que o servidor tenha enviado dados.

É por isso que erros de CORS após o deploy parecem confusos: nada “mudou” no código, mas a origem mudou. Localmente você pode chamar http://localhost:5173 para http://localhost:3000. Depois do deploy vira algo como https://app.yourdomain.com chamando https://api.yourdomain.com. Domínios diferentes, HTTPS, e às vezes portas diferentes significam uma origem diferente, então o navegador reavalia as permissões.

Sintomas comuns geralmente se parecem com um destes:

  • “Blocked by CORS policy” mesmo que a API funcione no Postman ou curl
  • Uma requisição de preflight (OPTIONS) falha antes da requisição real
  • Cabeçalhos CORS estão ausentes em respostas de erro (401/403/500), então só falha quando algo dá errado
  • Requisições com cookies param de funcionar de repente

O deploy também adiciona peças móveis invisíveis. Um reverse proxy, CDN ou configuração da plataforma pode sobrescrever cabeçalhos, removê-los ou armazenar em cache uma versão errada. Mudar para HTTPS altera o comportamento de cookies (SameSite e Secure), o que afeta requisições com “credentials”.

Um exemplo realista: seu frontend começa a enviar fetch(..., { credentials: 'include' }) na produção para manter os usuários logados. Se o backend responder com Access-Control-Allow-Origin: *, o navegador rejeita porque origens curinga não podem ser usadas com credenciais.

Se você herdou um setup frontend-backend gerado por IA, esses desalinhamentos são comuns. Na FixMyMess, o ganho mais rápido normalmente é verificar a origem implantada e garantir que todo caminho de resposta (incluindo erros) retorne cabeçalhos CORS consistentes.

Preflight 101: a requisição OPTIONS que você não percebeu

Muitos erros de CORS depois do deploy não têm nada a ver com sua chamada real à API. O navegador frequentemente envia uma requisição “preflight” silenciosa primeiro. Essa é uma requisição OPTIONS que pergunta ao servidor: “Se eu enviar esta requisição real a partir dessa origem, você vai permitir?”

Requisições simples vs preflight

Algumas requisições são “simples” e pulam o preflight. Muitas chamadas modernas do frontend não são simples, então o navegador faz preflight.

O preflight é acionado por coisas como:

  • Métodos diferentes de GET/POST/HEAD (por exemplo PUT, PATCH, DELETE)
  • Enviar JSON com Content-Type: application/json
  • Qualquer cabeçalho personalizado (como Authorization, X-Requested-With ou X-Api-Key)
  • Usar opções de fetch que adicionam cabeçalhos que você não esperava

Isso significa que você pode acessar seu endpoint diretamente e ver “200 OK”, mas o navegador ainda bloqueia a chamada. Por quê? Porque o navegador nunca passou pela etapa OPTIONS. Se OPTIONS retornar 404, 401, 500 ou faltar cabeçalhos CORS, a requisição real nunca é enviada.

Por que redirecionamentos frequentemente quebram o preflight

Uma falha comum só em produção é um redirecionamento inesperado no OPTIONS. Por exemplo, sua API pode redirecionar HTTP para HTTPS, adicionar ou remover “www”, ou enviar requisições não autenticadas para uma rota de login. Navegadores lidam mal com redirecionamentos no preflight e muitos tratam como falha mesmo que a URL redirecionada funcionasse.

Um exemplo prático: seu frontend em https://app.example.com chama https://api.example.com. A API está bem, mas o OPTIONS para /v1/data retorna 301 para /v1/data/ (barra final). Seu GET pode ainda funcionar em testes, mas o navegador bloqueia porque o preflight não teve uma resposta limpa aprovada por CORS.

A correção geralmente é: garantir que OPTIONS retorne um sucesso direto (frequentemente 204 ou 200), inclua os mesmos cabeçalhos CORS que as respostas reais e nunca redirecione.

Origens: o que deve corresponder e o que as pessoas entendem errado

Muitos erros de CORS após deploy acontecem porque a origem permitida que você configurou não é a mesma string que o navegador está enviando.

A coisa chave que as pessoas confundem é Origin vs Host. Seu servidor recebe um cabeçalho Host (para onde a requisição vai) e o navegador envia um cabeçalho Origin (onde a página que faz a requisição está hospedada). Decisões de CORS são baseadas em Origin, não em Host.

Uma origem não é apenas um domínio. É um trio exato: esquema + domínio + porta.

Correspondência exata de origem (esquema, domínio, porta)

Se seu frontend roda em https://app.example.com e sua API é https://api.example.com, o navegador vai enviar Origin: https://app.example.com. Seu backend precisa responder com Access-Control-Allow-Origin: https://app.example.com (ou uma allowlist no servidor que resulte nesse valor exato).

http vs https é a armadilha clássica de deploy. Localmente você pode testar de http://localhost:3000. Depois do deploy, o site vira https://... e sua allowlist ainda inclui só a versão http, então o navegador bloqueia.

Desalinhamentos comuns para investigar

Procure por estas pequenas diferenças que quebram o CORS:

  • https://example.com vs https://www.example.com
  • Uma porta ausente: https://example.com vs https://example.com:8443
  • Domínios de staging: https://staging.example.com vs https://app.example.com
  • Origens locais inesperadas: http://127.0.0.1:3000 vs http://localhost:3000
  • Builds mobile ou de preview que usam um domínio diferente do de produção

Um cenário do mundo real: seu frontend é servido de https://www.brand.com, mas você só permitiu https://brand.com. Para um humano tudo parece igual, mas para o navegador é uma origem diferente.

Se você herdou um backend gerado por IA, frequentemente ele tem uma lista de origens hardcoded que nunca foi atualizada para o domínio ao vivo. Na FixMyMess normalmente começamos lendo os valores reais de Origin nos logs de produção e alinhando a allowlist para essas strings exatas.

Cabeçalhos de resposta CORS que devem estar corretos sempre

CORS não é uma configuração única. O navegador checa cabeçalhos específicos em toda requisição cross-origin, e é rígido quanto a isso. Se um cabeçalho faltar em apenas uma rota (frequentemente a rota OPTIONS), você pode ter erros de CORS que parecem aleatórios.

O navegador quer basicamente saber: minha Origin é permitida, e para este tipo de requisição, o método e os cabeçalhos são permitidos? Isso significa que sua API deve retornar Access-Control-Allow-Origin que combine com a origem solicitante (por exemplo https://app.example.com). Se você retornar a origem errada ou esquecer em alguns endpoints, o navegador bloqueia a resposta mesmo que o servidor retorne 200.

Falhas de preflight geralmente vêm de Access-Control-Allow-Headers. A requisição preflight inclui Access-Control-Request-Headers com o que o navegador planeja enviar. Se sua resposta não incluir todos esses cabeçalhos (erros comuns: Authorization, Content-Type, X-Requested-With), o navegador para antes da requisição real.

Cache também pode tornar o CORS inconsistente. Se você permite múltiplas origens, adicione Vary: Origin para que CDNs e proxies não armazenem a resposta CORS de uma origem e a sirvam para outra depois.

Para preflight OPTIONS, retornar 204 No Content é aceitável, mas os cabeçalhos ainda precisam estar presentes. Uma resposta preflight limpa geralmente inclui:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Methods (inclua o método que você vai usar, como GET, POST, PATCH)
  • Access-Control-Allow-Headers (cubra o que o navegador solicitou)
  • Vary: Origin
  • Opcional: Access-Control-Max-Age para reduzir preflights repetidos

Uma dica prática: assegure-se de que o middleware de CORS execute antes do roteamento, checagens de autenticação e handlers de erro. Caso contrário, respostas 401/403/500 podem pular os cabeçalhos CORS e gerar mensagens confusas de “CORS blocked” que na verdade são erros de autenticação ou do servidor.

Credenciais e curingas: a armadilha mais comum em produção

Rebuild the right way
When patching is painful, we can rebuild into a maintainable, production-ready codebase.

Muitos erros de CORS após deploy vêm de uma regra que as pessoas só aprendem em produção: você não pode combinar credenciais com origem curinga.

Se o navegador envia cookies (ou você manda que ele envie credentials), o servidor precisa responder com uma origem específica, não *. Assim isto falhará:

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Credentials: true

Cookies vs Authorization: por que se comportam diferente

Cookies são anexados automaticamente pelo navegador, por isso são fortemente controlados. Se seu frontend e API estão em domínios diferentes, cookies não serão enviados a menos que você opte por isso no frontend e permita no backend.

Um cabeçalho Authorization: Bearer ... é diferente. Ele não depende de cookies, mas frequentemente aciona um preflight porque não é uma requisição simples. Isso também pode quebrar, porém a armadilha “curinga + credenciais” normalmente envolve cookies.

No frontend, esta linha muda toda a exigência:

fetch("https://api.example.com/me", {
  credentials: "include"
})

Depois que você adiciona credentials: "include" (ou withCredentials: true no Axios), o navegador rejeitará respostas a menos que o backend seja bem explícito.

O que o backend deve enviar (toda vez)

Assegure-se de que as respostas da API incluam:

  • Access-Control-Allow-Origin: https://your-frontend.com (correspondência exata)
  • Access-Control-Allow-Credentials: true
  • Vary: Origin (para que caches não misturem origens)

Cenário de exemplo: localmente você chama http://localhost:3000 para http://localhost:8000 e funciona. Em produção você muda para https://app.yourdomain.com e começa a usar cookies para login. Se o servidor ainda responde com Access-Control-Allow-Origin: *, o navegador bloqueia a resposta mesmo que a API retorne 200.

Se você herdou código backend gerado por IA, essa má configuração é comum. A FixMyMess frequentemente encontra os cabeçalhos corretos em uma rota, mas faltando em refresh de autenticação ou respostas de erro, o que faz o bug parecer aleatório.

Proxies, CDNs e configurações de plataforma que sobrescrevem seu app

Erros de CORS depois do deploy frequentemente aparecem porque seu frontend não fala mais diretamente com seu servidor de aplicação. Um reverse proxy, load balancer, CDN ou camada de “segurança” da plataforma pode alterar requisições e respostas de maneiras que seu setup local nunca fez.

Quando a borda altera seus cabeçalhos

Muitos proxies podem adicionar, remover ou duplicar cabeçalhos. Se seu app retorna o Access-Control-Allow-Origin correto, mas o proxy o sobrescreve (ou adiciona um segundo), navegadores podem rejeitar a resposta. Outro problema silencioso: o proxy comprime ou redireciona respostas de forma diferente, e a resposta de redirecionamento está sem cabeçalhos CORS mesmo que o endpoint final esteja correto.

Uma falha comum só em produção é que requisições OPTIONS nunca chegam ao seu app. Algumas plataformas tratam OPTIONS como suspeita, bloqueiam com uma regra de firewall, ou a roteiam para um handler padrão que retorna 404/405 sem cabeçalhos CORS. Do lado do navegador, parece que “CORS está quebrado”, mas o problema real é roteamento.

Cache do CDN pode servir cabeçalhos CORS errados

Se um CDN cacheia respostas de API sem variar por Origin, ele pode servir por engano uma resposta com cabeçalhos CORS destinados a outro site. Exemplo: usuário A aciona sua API de https://app.example.com e o CDN cacheia a resposta com essa origem permitida. Usuário B faz a mesma URL de https://admin.example.com e recebe os cabeçalhos cacheados, que não correspondem, então o navegador bloqueia.

O que verificar, em ordem:

  • Checar os cabeçalhos de resposta na borda (CDN/proxy) e no servidor da aplicação, e confirmar que batem.
  • Confirmar que requisições OPTIONS são permitidas e roteadas ao seu app, não bloqueadas ou tratadas por uma regra padrão.
  • Garantir que redirecionamentos (http para https, apex para www) também retornem cabeçalhos CORS.
  • Se houver cache, certifique-se de que as respostas variem por Origin ou desative cache para rotas de API.
  • Comparar regras de proxy entre staging e produção linha a linha.

Se você herdou um backend gerado por IA, esse desalinhamento na camada de proxy é um culpado frequente que a FixMyMess encontra em auditoria de código, porque o app “parece certo” mas a configuração na borda discorda silenciosamente.

Variáveis de ambiente e allowlists de domínio: pontos de quebra silenciosos

Erros de CORS após deploy frequentemente se rastreiam até uma mudança chata: seu frontend agora chama uma URL diferente da que você pensa. Localmente, um servidor dev pode proxyar requisições e esconder o problema. Em produção, o navegador fala direto com a API, então qualquer desalinhamento aparece imediatamente.

Um erro comum é enviar o frontend com a base URL da API errada. Por exemplo, sua build pega API_URL=https://staging-api... (ou uma URL antiga de preview) porque a variável de ambiente de produção não foi definida, ou a plataforma hospedou um build em cache. O navegador então envia requisições do seu domínio ao staging API que não permite aquela origem.

Outro ponto silencioso é a allowlist no backend. Equipes adicionam http://localhost:3000 durante o desenvolvimento e esquecem de adicionar o domínio real depois. Fica pior com múltiplos domínios: www vs sem www, um domínio de marketing, um subdomínio de app e um domínio de preview. Se sequer uma origem estiver faltando, aquele ambiente “vai falhar aleatoriamente”.

Para evitar drift, centralize suas origens permitidas e trate elas como superfície de configuração real, não strings espalhadas.

Maneira prática de prevenir drift de configuração

Mantenha regras em um só lugar e estritas:

  • Use uma variável de ambiente para origens permitidas (separadas por vírgula), parseada no startup.
  • Normalize domínios (inclua esquema e host exatos que você serve: https://app.example.com).
  • Mantenha valores separados para dev, staging e produção, e documente qual URL do frontend mapeia para qual API.
  • Logue a allowlist resolvida no boot (e o Origin da requisição em falhas de CORS).
  • Adicione um smoke test rápido após cada deploy: uma chamada de API do domínio real.

Se você herdou um app gerado por IA, esse é um diagnóstico frequente da FixMyMess: o frontend aponta para um ambiente enquanto o CORS do backend aponta para outro, e ninguém percebe até o primeiro deploy real.

Estratégia repetível para consertar CORS (passo a passo)

Find exposed secrets now
Our audit flags exposed secrets and risky config that often ship with generated code.

Quando aparecem erros de CORS após deploy, trate como uma tarefa de debug, não um palpite. O objetivo é achar a requisição exata que o navegador está bloqueando e o cabeçalho exato que falta ou está em desacordo.

Comece no painel Network do DevTools do navegador. Filtre pela chamada de API que falha e então procure uma requisição OPTIONS logo antes dela. Se você ver OPTIONS, está lidando com um preflight. Se não, o erro geralmente está na requisição real (muitas vezes falta um cabeçalho numa resposta 401/500).

Use esta sequência e não pule passos:

  1. Reproduza e capture a requisição que falha: copie os detalhes do request do Network (método, URL, status code e se houve preflight OPTIONS).
  2. Confirme a Origin exata: leia o cabeçalho Origin e anote exatamente (esquema + domínio + porta). Muitos problemas de “parece que bate” são na verdade http vs https ou www vs sem www.
  3. Cheque cabeçalhos em ambas as respostas: abra a resposta OPTIONS e a resposta da requisição real. Ambas devem incluir os cabeçalhos CORS corretos (especialmente Access-Control-Allow-Origin, e para cookies: Access-Control-Allow-Credentials).
  4. Elimine redirecionamentos e surpresas do middleware: se a API redireciona (301/302) ou força uma barra final, o preflight frequentemente falha porque a resposta redirecionada não inclui os cabeçalhos CORS. Faça OPTIONS retornar 200/204 direto com os cabeçalhos.
  5. Troque * por uma allowlist explícita: configure uma lista curta de origens permitidas para produção, reteste, e então adicione apenas o que realmente precisa.

Checagem rápida de sanidade: se você usa cookies ou headers de auth, não pode usar Access-Control-Allow-Origin: *. Deve ecoar a origem específica permitida e habilitar credentials.

Se seu backend foi gerado por uma ferramenta de IA e a lógica de CORS está espalhada por rotas, proxies e configurações de plataforma, a FixMyMess pode localizar a fonte única da verdade e corrigi-la com segurança após uma auditoria de código gratuita.

Erros comuns que mantêm o CORS quebrado

Muitos erros de CORS após deploy são auto-infligidos. As coisas parecem bem localmente porque você está em uma origem só, usando requisições simples, e seu servidor dev é permissivo. Produção é mais rígida: domínios diferentes, HTTPS, cookies e às vezes um proxy ou CDN na frente.

Erros que aparecem com mais frequência quando um frontend fala com um backend separado:

  • Permitir qualquer origem durante o desenvolvimento, e depois ativar cookies ou auth na produção sem ajustar o CORS (não dá para usar * com credentials).
  • Adicionar cabeçalhos CORS só no caminho feliz, mas não nas respostas 401/403/500, então o navegador esconde o erro real atrás de uma mensagem de CORS.
  • Esquecer que o navegador envia um preflight OPTIONS para muitas requisições, e seu servidor/roteador/middleware não responde com os mesmos cabeçalhos CORS.
  • Confiar em defaults do framework (ou em um snippet de CORS copiado) sem checar os cabeçalhos reais em produção.
  • Tentar consertar no frontend mudando fetch/axios, mesmo que CORS seja imposto pelo navegador e deva ser corrigido no servidor.

Uma armadilha fácil de perder: você adiciona Access-Control-Allow-Origin para seu GET /api/me, mas sua camada de auth bloqueia a requisição cedo. A resposta 401 não inclui cabeçalhos CORS, então o navegador reporta um erro de CORS em vez de “unauthorized”. Parece que o CORS está quebrado, mas o problema real é “cabeçalhos CORS faltando em erros”.

Outra armadilha comum é misturar credenciais e curingas. Se seu frontend usa cookies (ou Authorization headers) e você definiu withCredentials: true, seu backend deve retornar uma origem específica (por exemplo https://app.example.com) e também permitir credentials. Retornar * vai falhar.

Se você herdou código gerado por IA, essas questões frequentemente estão espalhadas por middleware, funções serverless e reverse proxies. A FixMyMess frequentemente vê projetos onde o CORS está “configurado” em um lugar, mas sobrescrito em outro. O caminho mais rápido é verificar respostas reais de produção para OPTIONS e para a requisição final, incluindo casos de erro.

Exemplo: frontend funciona localmente, quebra no domínio ao vivo

Turn AI prototypes into prod
FixMyMess repairs AI-generated apps so they deploy cleanly and behave consistently.

Uma história comum: você construiu um app React em http://localhost:3000 e uma API em http://localhost:8080. Você faz login, a API seta um cookie e todas as requisições funcionam.

Então você faz o deploy. O app React vai para https://app.yourdomain.com, a API fica em https://api.yourdomain.com, e de repente aparecem erros de CORS após o deploy. O confuso é que o código não mudou.

O que mudou é a regra do navegador. Cookies cross-site e verificações de preflight são mais rígidas em origens HTTPS reais. Localmente você pode não acionar preflight, ou seu servidor dev pode proxyar requisições de forma silenciosa para que o navegador nunca veja uma chamada cross-origin.

Aqui está o que tipicamente corrige esse setup:

  • Defina Access-Control-Allow-Origin para a origem exata do frontend (https://app.yourdomain.com), não *.
  • Defina Access-Control-Allow-Credentials: true tanto no preflight (OPTIONS) quanto na resposta real.
  • Garanta que seu servidor responda a OPTIONS para a mesma rota com 200/204 e os mesmos cabeçalhos CORS.
  • No frontend, envie cookies intencionalmente (para fetch: credentials: "include"; para Axios: withCredentials: true).
  • Certifique-se de que os cookies são compatíveis com requisições cross-site (frequentemente SameSite=None; Secure).

Como confirmar a correção em produção: abra DevTools Network, encontre a requisição que falhava e cheque duas entradas. Primeiro, o preflight OPTIONS deve retornar sucesso e incluir os cabeçalhos allow-origin e allow-credentials. Segundo, a chamada real da API deve retornar os mesmos cabeçalhos e realmente setar/enviar o cookie.

Se um backend gerado por IA estiver inconsistente (OPTIONS tratado por uma camada, requisição real por outra), plataformas como FixMyMess costumam encontrar o desalinhamento rapidamente numa auditoria de código e aplicar o patch seguro.

Checklist rápido e próximos passos

Quando você vê erros de CORS após deploy, trate como um problema de correspondência de cabeçalhos, não um mistério. Comece checando o que o navegador realmente recebeu na requisição que falhou e (se houver) no preflight.

Aqui estão verificações rápidas que pegam a maioria dos problemas reais:

  • Confirme que a Origin combina exatamente com o que o servidor permite (esquema + domínio + porta). https://app.com e https://www.app.com são origens diferentes.
  • Se você envia cookies ou auth, assegure que as regras de credentials estão corretas: a resposta deve incluir Access-Control-Allow-Credentials: true e a origem não pode ser *.
  • Abra o painel de rede e inspecione o preflight OPTIONS: deve retornar um status de sucesso (tipicamente 200 ou 204), com os mesmos cabeçalhos CORS da requisição real.
  • Verifique se Access-Control-Allow-Headers inclui o que você realmente envia (erros comuns: Authorization, Content-Type, X-Requested-With).
  • Assegure que o preflight não está sendo redirecionado (301/302). Redirecionamentos frequentemente acontecem após o deploy por causa de HTTP->HTTPS ou falta de barra final.

Se isso parece certo mas ainda falha, o problema costuma estar uma camada acima do seu código: um proxy, CDN ou configuração da plataforma reescrevendo cabeçalhos, cacheando uma resposta antiga ou respondendo OPTIONS de forma diferente que seu app.

Próximos passos para impedir que volte:

  • Escreva as origens permitidas por ambiente (local, staging, produção) e mantenha-as em config, não espalhadas pelos arquivos.
  • Adicione um teste de integração simples que rode contra o domínio implantado e verifique os cabeçalhos de preflight.
  • Decida uma abordagem de autenticação (cookies ou tokens) e alinhe CORS e sessões a essa escolha.
  • Logue requisições OPTIONS em produção, pelo menos temporariamente, para ver códigos de status e cabeçalhos.

Se seu app foi gerado por ferramentas como Lovable, Bolt, v0, Cursor ou Replit e a configuração de CORS está embaralhada entre código backend e settings de deploy, a FixMyMess pode executar uma auditoria de código gratuita e fazer com que os cabeçalhos do backend e a configuração da plataforma trabalhem juntos rapidamente.