image

Accede a bootcamps ilimitados y a más de 650 cursos para siempre

70
%OFF
Article image
Carlos Pinheiro
Carlos Pinheiro26/05/2026 10:10
Compartir

Entendendo CORS: como permitir comunicação segura entre Frontend e Backend

    Quando começamos a desenvolver aplicações web modernas, é muito comum separar o frontend do backend. O frontend pode estar rodando em um endereço como https://meusite.com, enquanto a API pode estar em https://api.meusite.com ou até em outro domínio completamente diferente. Essa separação é poderosa, mas traz uma pergunta importante: como o navegador sabe se uma página pode ou não acessar dados de outro servidor?

    É exatamente nesse ponto que entra o CORS, sigla para Cross-Origin Resource Sharing, ou Compartilhamento de Recursos entre Origens Diferentes. De forma simples, CORS é um mecanismo baseado em cabeçalhos HTTP que permite ao servidor informar ao navegador quais origens têm permissão para acessar seus recursos. A MDN define CORS como um mecanismo que usa cabeçalhos HTTP para permitir que um servidor indique origens diferentes da sua própria que podem carregar recursos no navegador. (MDN Web Docs)

    O que é uma origem?

    Antes de entender CORS, precisamos entender o conceito de origem. Para o navegador, uma origem é formada por três partes: protocolo, domínio e porta.

    Por exemplo:

    https://meusite.com
    

    Essa origem possui:

    Protocolo: https
    Domínio:    meusite.com
    Porta:      443, implícita no HTTPS
    

    Agora observe estes exemplos:

    https://meusite.com
    https://api.meusite.com
    http://meusite.com
    https://meusite.com:3000
    

    Mesmo parecendo parecidos, para o navegador eles são origens diferentes. O subdomínio mudou, o protocolo mudou ou a porta mudou. Portanto, quando uma aplicação frontend tenta acessar uma API em outra origem, o navegador aplica regras de segurança conhecidas como Same-Origin Policy, ou Política de Mesma Origem. O CORS é uma forma controlada de relaxar essa regra.

    O que o CORS não é

    Um erro comum é pensar que CORS é uma proteção completa da API. Não é. CORS é uma política aplicada principalmente pelo navegador. Isso significa que ferramentas como curl, Postman, Insomnia ou outro servidor backend podem fazer requisições diretamente para sua API sem serem bloqueados por CORS.

    Portanto, CORS não substitui autenticação, autorização, validação de token, controle de sessão, rate limit ou firewall. Ele apenas diz ao navegador: “essa origem pode ler a resposta desta API”.

    Essa distinção é fundamental. Quando configuramos CORS, não estamos tornando a API magicamente segura. Estamos apenas controlando quais aplicações web, executadas no navegador do usuário, podem consumir aquela resposta.

    Como o CORS funciona

    Quando uma aplicação frontend faz uma requisição para uma API em outra origem, o navegador adiciona um cabeçalho chamado Origin. Esse cabeçalho informa ao servidor de onde a requisição partiu.

    Exemplo:

    Origin: https://meusite.com
    

    O servidor então pode responder com o cabeçalho:

    Access-Control-Allow-Origin: https://meusite.com
    

    Esse cabeçalho diz ao navegador que a origem https://meusite.com está autorizada a acessar a resposta. O cabeçalho Access-Control-Allow-Origin indica se a resposta pode ser compartilhada com o código da origem solicitante. (MDN Web Docs)

    Quando a requisição é simples, como um GET comum sem cabeçalhos especiais, o navegador pode fazer a requisição diretamente. Porém, quando a requisição envolve métodos como PUT, PATCH, DELETE, cabeçalhos personalizados ou determinados tipos de conteúdo, o navegador faz antes uma requisição de verificação chamada preflight.

    O que é uma requisição preflight?

    A requisição preflight é uma chamada HTTP feita com o método OPTIONS. Ela serve para perguntar ao servidor se a requisição real pode ser enviada.

    Imagine que o frontend queira enviar um DELETE para uma API:

    DELETE /usuarios/10
    Origin: https://painel.meusite.com
    Authorization: Bearer token
    

    Antes de permitir isso, o navegador pode enviar:

    OPTIONS /usuarios/10
    Origin: https://painel.meusite.com
    Access-Control-Request-Method: DELETE
    Access-Control-Request-Headers: Authorization
    

    O servidor precisa responder algo como:

    Access-Control-Allow-Origin: https://painel.meusite.com
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    Access-Control-Allow-Headers: Authorization, Content-Type
    

    Se a resposta for compatível, o navegador libera a requisição real. Caso contrário, ele bloqueia a chamada antes que o JavaScript consiga acessar a resposta. Segundo a MDN, o CORS usa esse mecanismo de preflight para verificar se o servidor permite a requisição cross-origin antes da requisição real. (MDN Web Docs)

    Principais cabeçalhos CORS

    O cabeçalho mais conhecido é:

    Access-Control-Allow-Origin
    

    Ele define quais origens podem acessar a resposta. Pode receber uma origem específica:

    Access-Control-Allow-Origin: https://meusite.com
    

    Ou pode receber o curinga:

    Access-Control-Allow-Origin: *
    

    O uso de * significa que qualquer origem pode acessar aquela resposta. Isso pode fazer sentido para APIs públicas, como dados abertos, catálogos públicos ou endpoints sem dados sensíveis. Mas em APIs privadas, administrativas ou autenticadas, isso geralmente é uma má prática.

    Outro cabeçalho importante é:

    Access-Control-Allow-Methods
    

    Ele informa quais métodos HTTP são aceitos:

    Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    

    Também temos:

    Access-Control-Allow-Headers
    

    Esse cabeçalho informa quais cabeçalhos personalizados podem ser usados na requisição:

    Access-Control-Allow-Headers: Authorization, Content-Type
    

    Quando há cookies, autenticação baseada em sessão ou credenciais, entra o cabeçalho:

    Access-Control-Allow-Credentials: true
    

    Nesse caso, é importante observar que não se deve combinar credenciais com Access-Control-Allow-Origin: *. Para credenciais, o servidor deve retornar uma origem específica.

    CORS em ambientes distribuídos

    Em um ambiente distribuído, é comum termos várias partes separadas da aplicação. Por exemplo:

    Frontend Web:     https://app.empresa.com
    API principal:    https://api.empresa.com
    Serviço auth:     https://auth.empresa.com
    Gateway:          https://gateway.empresa.com
    Painel admin:     https://admin.empresa.com
    

    Nesse cenário, liberar tudo com * pode parecer prático, mas não é uma boa estratégia. O ideal é definir uma política clara de origens permitidas. Cada serviço deve saber quais aplicações podem acessá-lo diretamente pelo navegador.

    Em arquiteturas mais organizadas, o CORS costuma ser configurado no API Gateway, no reverse proxy ou no próprio backend. Quando há muitos microsserviços, centralizar essa regra no gateway evita duplicidade, inconsistência e erros difíceis de rastrear.

    Uma prática importante em ambientes distribuídos é manter uma lista explícita de origens permitidas:

    https://app.empresa.com
    https://admin.empresa.com
    https://painel.empresa.com
    

    Assim, se uma requisição vier de https://site-desconhecido.com, o servidor simplesmente não retorna permissão CORS válida, e o navegador bloqueia o acesso à resposta.

    Boas práticas no uso de CORS

    A primeira boa prática é evitar Access-Control-Allow-Origin: * em APIs autenticadas. Esse curinga deve ser usado apenas quando a resposta realmente puder ser lida por qualquer site.

    A segunda boa prática é separar ambientes. Durante o desenvolvimento, talvez seja necessário permitir:

    http://localhost:3000
    http://localhost:5173
    

    Mas em produção, essas origens devem ser removidas ou controladas com variáveis de ambiente.

    A terceira boa prática é não tratar CORS como segurança principal. Mesmo que o navegador bloqueie uma origem indevida, a API ainda precisa validar tokens, permissões, escopos e regras de negócio no servidor.

    A quarta boa prática é usar Vary: Origin quando a resposta CORS muda de acordo com a origem. A especificação Fetch recomenda o uso de Vary quando a política é mais complexa do que apenas usar * ou uma origem fixa, porque caches HTTP precisam saber que a resposta varia conforme o cabeçalho Origin. (Fetch Standard)

    Exemplo:

    Vary: Origin
    

    Isso evita que um cache entregue uma resposta autorizada para uma origem errada.

    Exemplo 1: API Node.js com Express e CORS

    Neste primeiro exemplo, vamos criar uma API simples usando Node.js e Express. O pacote cors é um middleware para Express/Connect que configura os cabeçalhos CORS de resposta. (Express.js)

    Instalação:

    npm init -y
    npm install express cors
    

    Arquivo server.js:

    const express = require("express");
    const cors = require("cors");
    
    const app = express();
    
    app.use(express.json());
    
    const allowedOrigins = [
    "http://localhost:3000",
    "http://localhost:5173",
    "https://app.minhaempresa.com"
    ];
    
    const corsOptions = {
    origin: function (origin, callback) {
      // Permite requisições sem Origin, como chamadas feitas por curl ou Postman.
      if (!origin) {
        return callback(null, true);
      }
    
      if (allowedOrigins.includes(origin)) {
        return callback(null, true);
      }
    
      return callback(new Error("Origem não permitida pelo CORS"));
    },
    methods: ["GET", "POST", "PUT", "DELETE"],
    allowedHeaders: ["Content-Type", "Authorization"],
    credentials: true
    };
    
    app.use(cors(corsOptions));
    
    app.get("/api/produtos", (req, res) => {
    res.json([
      { id: 1, nome: "Sensor de Temperatura" },
      { id: 2, nome: "Módulo ESP32" }
    ]);
    });
    
    app.post("/api/produtos", (req, res) => {
    const produto = req.body;
    
    res.status(201).json({
      mensagem: "Produto criado com sucesso",
      produto
    });
    });
    
    app.listen(3001, () => {
    console.log("API rodando em http://localhost:3001");
    });
    

    Agora imagine um frontend rodando em:

    http://localhost:5173
    

    Ele poderia consumir a API assim:

    async function carregarProdutos() {
    const resposta = await fetch("http://localhost:3001/api/produtos", {
      method: "GET",
      credentials: "include"
    });
    
    const produtos = await resposta.json();
    
    console.log(produtos);
    }
    
    carregarProdutos();
    

    Nesse caso, a API permite explicitamente a origem http://localhost:5173. Se outra página tentar consumir essa API a partir de uma origem não cadastrada, o navegador bloqueará o acesso à resposta.

    Exemplo 2: CORS em ambiente distribuído com API Gateway

    Agora imagine uma arquitetura mais próxima de um sistema real:

    Frontend:
    https://app.exemplo.com
    
    Gateway:
    https://gateway.exemplo.com
    
    Serviço de usuários:
    http://usuarios-service:4001
    
    Serviço de pedidos:
    http://pedidos-service:4002
    

    Nesse modelo, o navegador não conversa diretamente com os microsserviços internos. Ele conversa com o gateway. O gateway recebe a requisição, valida a origem, aplica CORS e encaminha internamente para o serviço correto.

    Exemplo simplificado com Express:

    npm install express cors http-proxy-middleware
    

    Arquivo gateway.js:

    const express = require("express");
    const cors = require("cors");
    const { createProxyMiddleware } = require("http-proxy-middleware");
    
    const app = express();
    
    const allowedOrigins = [
    "https://app.exemplo.com",
    "https://admin.exemplo.com"
    ];
    
    const corsOptions = {
    origin: function (origin, callback) {
      if (!origin) {
        return callback(null, true);
      }
    
      if (allowedOrigins.includes(origin)) {
        return callback(null, true);
      }
    
      return callback(new Error("Origem bloqueada pelo Gateway"));
    },
    methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
    allowedHeaders: ["Content-Type", "Authorization"],
    credentials: true
    };
    
    app.use(cors(corsOptions));
    
    app.use(
    "/usuarios",
    createProxyMiddleware({
      target: "http://usuarios-service:4001",
      changeOrigin: true
    })
    );
    
    app.use(
    "/pedidos",
    createProxyMiddleware({
      target: "http://pedidos-service:4002",
      changeOrigin: true
    })
    );
    
    app.listen(8080, () => {
    console.log("Gateway rodando em http://localhost:8080");
    });
    

    Nesse exemplo, o CORS fica centralizado no gateway. Isso facilita a manutenção porque o frontend acessa apenas uma entrada principal:

    https://gateway.exemplo.com/usuarios
    https://gateway.exemplo.com/pedidos
    

    Os microsserviços internos não precisam expor suas próprias políticas CORS para o navegador, porque eles não são acessados diretamente pelo frontend. Essa abordagem é mais organizada em sistemas distribuídos, principalmente quando existem muitos serviços, múltiplos ambientes e diferentes aplicações consumidoras.

    Um erro comum: resolver CORS do lado errado

    Muitos iniciantes tentam resolver CORS no frontend. Mas o CORS é controlado pelo servidor que responde à requisição. O frontend pode enviar a chamada corretamente, mas quem precisa autorizar a leitura da resposta é a API.

    Por isso, quando aparece um erro como:

    No 'Access-Control-Allow-Origin' header is present
    

    o problema normalmente está na configuração do backend, gateway, proxy ou servidor HTTP. Não adianta apenas mudar o fetch, instalar biblioteca no frontend ou tentar “desativar” o navegador. A solução correta é configurar a origem permitida no servidor.

    Conclusão

    CORS é um dos conceitos mais importantes para quem desenvolve aplicações web modernas, principalmente quando frontend e backend estão separados. Ele permite que uma API diga ao navegador quais origens podem acessar suas respostas, usando cabeçalhos HTTP como Access-Control-Allow-Origin, Access-Control-Allow-Methods e Access-Control-Allow-Headers.

    Em projetos pequenos, configurar CORS diretamente no backend pode ser suficiente. Em ambientes distribuídos, com microsserviços, autenticação separada e múltiplos frontends, o ideal é tratar CORS como parte da arquitetura da aplicação, preferencialmente centralizando a política em um gateway ou proxy bem configurado.

    A regra prática é simples: libere apenas as origens necessárias, evite * em APIs privadas, separe desenvolvimento de produção e nunca confunda CORS com autenticação. CORS ajuda o navegador a decidir quem pode ler uma resposta, mas a segurança real da API continua dependendo de boas práticas no servidor.

    Referências

    Compartir
    Recomendado para ti
    GFT - Fundamentos de Cloud com AWS
    Bootcamp Bradesco - GenAI, Dados & Cyber
    Bootcamp Afya - Automação de Dados com IA
    Comentarios (0)