Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

CORS em APIs Node.js

Atualizado em: 18 de agosto de 2026

Rack de servidores processando fluxos de dados no Node.js

CORS em APIs Node.js é um tema importante para quem desenvolve serviços modernos com JavaScript no servidor. Não basta fazer uma demonstração funcionar no computador local: uma solução profissional precisa ter contratos claros, tratamento de falhas, segurança, testes, observabilidade e um processo de implantação previsível.

Neste guia, você aprenderá a aplicar CORS em APIs Node.js de forma prática, entendendo a arquitetura, os principais componentes, os riscos mais comuns e as decisões que tornam o projeto mais fácil de manter. O objetivo é sair de um exemplo isolado e chegar a uma base que possa evoluir sem depender de improvisos.

O que é CORS em APIs Node.js?

CORS, ou Cross-Origin Resource Sharing, é o mecanismo de headers HTTP usado pelo navegador para decidir se um site pode acessar recursos de outra origem. Ele não é um sistema de autenticação e não protege chamadas feitas por servidores, scripts fora do navegador ou ferramentas de linha de comando.

Em aplicações Node.js, essa abordagem se encaixa bem em sistemas orientados a eventos e operações de entrada e saída. Para revisar os fundamentos da plataforma, leia o que é Node.js. Se você ainda está montando seu primeiro backend, o tutorial sobre como criar uma API com Node.js oferece uma visão complementar.

Quando vale a pena usar?

Configure CORS quando um frontend hospedado em uma origem precisa consumir uma API em outra. A política deve ser tão restrita quanto o produto permite. Aplicações servidas pela mesma origem podem evitar CORS; APIs públicas podem aceitar várias origens, mas ainda precisam de autenticação, rate limiting e validação.

A decisão deve considerar o problema real, a experiência da equipe, a infraestrutura disponível e o custo de operação. Uma tecnologia pode ser excelente e ainda assim ser inadequada quando aumenta a complexidade sem gerar benefício mensurável. Comece pequeno, defina critérios de sucesso e valide o comportamento sob carga e falhas.

Preparando o projeto

Crie uma pasta dedicada, inicialize o package.json e fixe uma versão suportada do Node.js no arquivo de configuração do projeto. Use variáveis de ambiente para endereços, credenciais e opções de execução, mas valide tudo no início do processo para evitar falhas tardias.

npm init -y
npm install express cors
npm install --save-dev supertest

Separe dependências de produção das ferramentas de desenvolvimento. Ative lint, formatação e testes no pipeline de integração contínua. O artigo sobre deploy com GitHub Actions mostra como automatizar verificações antes que uma alteração chegue ao servidor.

Exemplo inicial

O exemplo permite uma lista explícita de origens e rejeita as demais. A função também aceita requisições sem Origin, comuns em clientes servidor a servidor.

import express from 'express';
import cors from 'cors';

const allowedOrigins = new Set([
  'https://app.example.com',
  'https://admin.example.com'
]);

const app = express();

app.use(cors({
  origin(origin, callback) {
    if (!origin || allowedOrigins.has(origin)) {
      return callback(null, true);
    }

    callback(new Error('Origin not allowed'));
  },
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  maxAge: 600
}));

Quando credentials é true, a resposta não pode usar Access-Control-Allow-Origin com asterisco. A origem precisa ser refletida apenas depois de passar pela lista permitida. O maxAge reduz preflights repetidos, mas mudanças de política podem levar tempo para alcançar clientes.

Estrutura recomendada

Centralize a política de CORS em um módulo de configuração, porém permita exceções explícitas por rota. Separe origens de desenvolvimento, homologação e produção. Não derive permissões diretamente de qualquer header ou parâmetro enviado pelo cliente.

  • config: leitura e validação de ambiente;
  • domain: regras de negócio independentes do transporte;
  • application: casos de uso e orquestração;
  • infrastructure: banco, filas, rede e integrações;
  • interfaces: HTTP, comandos, eventos ou tarefas agendadas;
  • tests: unidades, integração e cenários de contrato.

Essa separação reduz acoplamento e permite substituir bibliotecas sem reescrever regras de negócio. Evite criar camadas vazias apenas para seguir um padrão: cada módulo deve proteger uma responsabilidade concreta.

Contratos e validação

Dados externos são sempre não confiáveis. Valide corpo, parâmetros, headers, eventos e respostas de serviços terceiros. Defina tamanho máximo, formatos permitidos e mensagens de erro consistentes. Schemas executáveis aproximam documentação e comportamento real; veja também o guia de validação com Zod no TypeScript.

Normalize origens como URLs completas com esquema e porta. Compare valores exatos, não use includes ou endsWith ingênuos. Uma regra como domínio terminando em example.com pode aceitar attackerexample.com. Para subdomínios dinâmicos, faça parse da URL e valide o hostname por limites de rótulo.

Tratamento de erros

Classifique falhas em categorias: entrada inválida, autenticação, autorização, recurso ausente, conflito, indisponibilidade temporária e erro interno. A resposta pública deve ser estável e não pode expor stack trace, consulta SQL, segredo ou detalhes da infraestrutura.

Responda preflight rejeitado sem expor detalhes desnecessários. Em muitos casos, o navegador exibirá um erro genérico ao frontend. Registre a origem, rota e motivo internamente. Não transforme toda falha de autenticação em problema de CORS, porque isso dificulta diagnóstico.

function isAllowedOrigin(origin) {
  try {
    const url = new URL(origin);

    return url.protocol === 'https:' &&
      (
        url.hostname === 'app.example.com' ||
        url.hostname.endsWith('.trusted.example')
      );
  } catch {
    return false;
  }
}

Segurança

A segurança deve fazer parte da arquitetura desde o início. Aplique privilégio mínimo, valide todas as fronteiras e trate credenciais como dados sensíveis. Para CORS em APIs Node.js, priorize:

  • mantenha uma lista explícita de origens confiáveis;
  • não use asterisco quando envia cookies ou credenciais;
  • valide scheme, hostname e porta;
  • reduza métodos e headers permitidos ao necessário;
  • proteja a API com autenticação independente de CORS;
  • revise regras para ambientes de preview e subdomínios temporários.

Registre eventos de segurança sem armazenar tokens completos, senhas ou dados pessoais desnecessários. Revise dependências e mantenha um processo claro para corrigir vulnerabilidades.

Desempenho e capacidade

Otimização começa com medição. Defina indicadores como latência, taxa de erro, uso de memória, CPU, tamanho de filas e tempo de dependências. O guia sobre como otimizar APIs RESTful em Node.js detalha princípios que também se aplicam aqui.

  • configure maxAge com valor moderado para preflight;
  • evite políticas diferentes sem necessidade por usuário;
  • não consulte banco de dados em cada verificação de origem;
  • faça cache seguro de configurações de tenants;
  • meça quantidade e latência de requisições OPTIONS;
  • mantenha respostas preflight pequenas.

Teste com carga representativa e inclua cenários de falha. Uma solução que funciona apenas com dependências saudáveis não está pronta para produção.

Observabilidade

Registre contagem de origens aceitas e bloqueadas, preflights por rota e falhas de configuração. Evite colocar a origem como label ilimitado de métrica; use logs para valores completos e métricas agregadas por resultado.

Use logs estruturados em JSON e inclua um identificador de correlação. Métricas devem mostrar volume, sucesso, falhas e duração. Traces distribuídos ajudam a encontrar gargalos quando uma operação atravessa vários serviços, mas precisam de amostragem para controlar custo.

Testes automatizados

Teste requisições simples e preflight. Confirme origem permitida, origem bloqueada, credenciais, métodos, headers e ausência do header Origin. Também valide que uma origem parecida, porém maliciosa, não passa pela regra.

import test from 'node:test';
import assert from 'node:assert/strict';
import request from 'supertest';
import { app } from './app.js';

test('aceita preflight confiável', async () => {
  const response = await request(app)
    .options('/orders')
    .set('Origin', 'https://app.example.com')
    .set('Access-Control-Request-Method', 'POST');

  assert.equal(response.status, 204);
  assert.equal(
    response.headers['access-control-allow-origin'],
    'https://app.example.com'
  );
});

test('não reflete origem desconhecida', async () => {
  const response = await request(app)
    .get('/orders')
    .set('Origin', 'https://evil.example');

  assert.equal(
    response.headers['access-control-allow-origin'],
    undefined
  );
});

Evite testes dependentes de ordem, relógio real ou serviços externos instáveis. Injete relógio, geradores de identificadores e clientes de infraestrutura. Para fundamentos, consulte testes unitários com Jest.

Implantação e operação

Garanta que proxy, CDN e cache incluam Origin na chave quando a resposta varia por origem. O header Vary: Origin evita servir uma permissão destinada a outro site. Revise cabeçalhos após passar por todos os componentes da borda.

Implemente graceful shutdown: ao receber um sinal de encerramento, pare de aceitar trabalho novo, conclua o que estiver em andamento dentro de um prazo e feche conexões. Configure health checks que diferenciem processo vivo de serviço pronto para receber tráfego.

Erros comuns

  • Tratar CORS como autenticação: clientes fora do navegador ignoram essa política.
  • Refletir qualquer Origin: isso equivale a aceitar todos os sites.
  • Usar curinga com cookies: a combinação é inválida e insegura.
  • Comparar domínio com includes: origens maliciosas podem contornar a regra.
  • Esquecer o cache: respostas com origem errada podem ser reutilizadas.

Checklist antes de publicar

  • origens de produção explícitas;
  • preflight testado;
  • credentials usado apenas quando necessário;
  • Vary: Origin confirmado;
  • métodos e headers mínimos;
  • subdomínios validados por hostname;
  • autenticação independente;
  • métricas de bloqueio configuradas.

Referências oficiais

Conteúdos relacionados

Conclusão

CORS é uma política do navegador baseada em headers, não uma barreira completa de segurança. Uma configuração correta usa origens explícitas, credenciais com cuidado, preflight controlado e cache coerente. O backend continua responsável por autenticar, autorizar e validar cada operação.

O caminho mais seguro é implementar uma versão pequena, observável e testável, medir seu comportamento e evoluir com base em dados. Quando contratos, limites e falhas são tratados explicitamente, CORS em APIs Node.js deixa de ser apenas uma funcionalidade e se torna uma parte confiável da plataforma.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita