PureTools

CORS Explicado: Por Que Sua Chamada de API Está Bloqueada

PureTools Team· 7 min de leitura
CORS Explicado: Por Que Sua Chamada de API Está Bloqueada

CORS: O Erro Que Todo Desenvolvedor Já Viu

Você está construindo um frontend que chama sua API. Funciona no Postman. Funciona com curl. Mas o navegador diz:

Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Isso não é um erro do servidor. É o navegador protegendo o usuário.

O Que CORS Realmente É

CORS (Cross-Origin Resource Sharing) é um mecanismo de segurança do navegador. Por padrão, JavaScript rodando na origem A não pode fazer requisições para a origem B. Uma origem é a combinação de protocolo + domínio + porta:

URLOrigem
http://localhost:3000http://localhost:3000
https://meuapp.comhttps://meuapp.com
https://api.meuapp.comhttps://api.meuapp.com (subdomínio diferente = origem diferente)
https://meuapp.com:8080https://meuapp.com:8080 (porta diferente = origem diferente)

Como Funciona

Para requisições simples (GET, POST com content types padrão), o navegador envia a requisição normalmente mas verifica os headers da resposta. Se a resposta não incluir Access-Control-Allow-Origin, o navegador bloqueia o JavaScript de ler a resposta.

Para requisições complexas (PUT, DELETE, headers customizados, content type JSON), o navegador envia uma requisição preflight primeiro:

OPTIONS /api/data HTTP/1.1
Origin: http://localhost:3000
Access-Control-Request-Method: POST
Access-Control-Request-Headers: Content-Type, Authorization

O servidor deve responder com:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

Resolvendo CORS

Express.js:

import cors from 'cors';

// Permitir todas as origens (apenas desenvolvimento!)
app.use(cors());

// Produção: especificar origens permitidas
app.use(cors({
  origin: ['https://meuapp.com', 'https://staging.meuapp.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true, // se usar cookies
}));

API routes Next.js:

export async function GET(request: Request) {
  const data = await getData();
  return Response.json(data, {
    headers: {
      'Access-Control-Allow-Origin': 'https://meuapp.com',
      'Access-Control-Allow-Methods': 'GET, POST',
    },
  });
}

Nginx:

location /api/ {
    add_header 'Access-Control-Allow-Origin' 'https://meuapp.com';
    add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
    add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';

    if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Max-Age' 86400;
        return 204;
    }
}

Erros Comuns

  • Usar * com credentials. Access-Control-Allow-Origin: * não funciona com credentials: true. Você deve especificar a origem exata.
  • Esquecer o handler de OPTIONS. Seu servidor deve responder a requisições preflight OPTIONS com headers CORS e status 204.
  • Resolver no frontend. CORS é configuração server-side. Nenhum código frontend pode contornar (esse é o objetivo).
  • Proxy no dev, esquecer em prod. Rewrites do next.config.js funcionam em desenvolvimento mas não em produção. Você precisa de headers CORS reais no servidor da API.

Alternativas ao CORS

  • Proxy same-origin: Roteie chamadas de API pelo seu próprio servidor (/api/proxy) para o navegador ver requisições same-origin.
  • Server-side rendering: Busque dados no servidor (Next.js Server Components), não no navegador. Sem necessidade de CORS.

Debug seus headers: Referência HTTP — verifique status codes e headers das suas respostas de API.