Guia completo para resolver o problema de não envio de cookies em pedidos Cross-Origin (CORS)

Se já se deparou com um cenário em que o servidor define o cookie corretamente (visível na resposta do navegador), mas nos pedidos seguintes o navegador não devolve esse cookie ao servidor, este guia é para si. Este problema ocorre quase sempre quando o frontend e o backend do seu projeto estão em dois domínios ou portas diferentes — por exemplo, frontend em app.example.com e API em api.example.com.

Porque é que isto acontece?

Os navegadores modernos impõem duas políticas de segurança rigorosas para prevenir ataques CSRF e sequestro de sessão:

  1. Política CORS: Por defeito, o navegador não permite que pedidos entre domínios incluam credenciais (como cookies), a menos que o servidor o permita explicitamente.
  2. Política SameSite: A partir do Chrome 80, os cookies são tratados por defeito com o comportamento SameSite=Lax, o que significa que não serão enviados em pedidos entre domínios, a menos que sejam explicitamente definidos com SameSite=None.

O ponto importante é que, para resolver este problema, tanto o lado do cliente como o do servidor devem ser configurados corretamente. Modificar apenas um lado não é suficiente.

Passo 1: Configuração correta do servidor

Três regras de ouro no lado do servidor

Regra 1 — Ativar credenciais: O cabeçalho de resposta do servidor deve incluir o seguinte valor:

Access-Control-Allow-Credentials: true

Regra 2 — Sem curinga: Quando as credenciais estão ativadas, não pode usar o caractere curinga * no cabeçalho Access-Control-Allow-Origin. Deve especificar exatamente o endereço de domínio completo do frontend:

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

Regra 3 — Atributos do cookie: O cookie deve ser definido com dois atributos: SameSite=None e Secure. Sem estes dois atributos, o navegador armazenará o cookie, mas nunca o enviará em pedidos entre domínios.

Código de exemplo em Node.js (Express)

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

const app = express();

app.use(cors({
  origin: 'https://app.example.com', // Domínio exato do frontend, sem /
  credentials: true
}));

app.post('/login', (req, res) => {
  res.cookie('session_token', 'abc123', {
    httpOnly: true,   // Impede o acesso JavaScript ao cookie
    secure: true,     // Enviado apenas via HTTPS
    sameSite: 'none', // Permite envio em pedidos Cross-Origin
    maxAge: 24 * 60 * 60 * 1000
  });
  res.json({ message: 'Login bem-sucedido' });
});

Código de exemplo em PHP

<?php
// Especifique o domínio exato do frontend, não *
header("Access-Control-Allow-Origin: https://app.example.com");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");

// Responder ao pedido Preflight
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

// Definir o cookie com os atributos necessários
setcookie('session_token', 'abc123', [
    'expires'  => time() + 86400,
    'path'     => '/',
    'secure'   => true,      // Obrigatório
    'httponly' => true,
    'samesite' => 'None'     // Obrigatório para Cross-Origin
]);

Nota: Se estiver a usar Laravel, defina supports_credentials como true no ficheiro config/cors.php e adicione o domínio do frontend ao array allowed_origins.

Passo 2: Configuração correta do cliente (frontend)

Mesmo com a configuração completa do servidor, o navegador ainda não enviará cookies, a menos que declare explicitamente no lado do cliente que este pedido é "com credenciais".

Usando Fetch API

fetch('https://api.example.com/user/profile', {
  method: 'GET',
  credentials: 'include'  // Esta linha é a chave para resolver o problema
})
  .then(res => res.json())
  .then(data => console.log(data));

Usando Axios

// Para um pedido específico
axios.get('https://api.example.com/user/profile', {
  withCredentials: true
});

// Ou globalmente para todo o projeto
axios.defaults.withCredentials = true;

Lista de verificação para resolução de problemas

Se o problema persistir após aplicar o acima, verifique estes itens por ordem:

  • Ambos os lados estão em HTTPS? O atributo Secure significa que o cookie só funciona em ligações encriptadas. Se o seu site estiver em HTTP, o navegador não aceitará o cookie. (Exceção: ambiente de desenvolvimento em localhost)
  • O endereço Origin é exato? O endereço Access-Control-Allow-Origin deve corresponder exatamente ao URL que vê no navegador. Mesmo uma diferença em www ou uma barra final / causará falha.
  • O pedido OPTIONS é bem-sucedido? No separador Network das ferramentas de desenvolvimento do navegador, verifique o pedido Preflight (com o método OPTIONS). Este pedido deve ser respondido com código 200 ou 204 e cabeçalhos CORS corretos.
  • O cookie está armazenado? No navegador, vá para DevTools ← Application ← Cookies e certifique-se de que o cookie está armazenado com os atributos Secure e SameSite=None.
  • Tem um proxy ou firewall intermédio? Por vezes, Cloudflare ou servidores web como Nginx reescrevem cabeçalhos. Verifique também a configuração deles.

Resumo

O envio de cookies em pedidos entre domínios requer a coordenação de três fatores: o servidor com Access-Control-Allow-Credentials: true e Origin exato, o cookie com atributos SameSite=None; Secure, e o cliente com ativação de credentials: 'include' ou withCredentials: true. Seguindo estes três pontos, o problema será completamente resolvido.


Infraestrutura confiável, tranquilidade para programadores

A implementação correta de CORS e a gestão de cookies é apenas parte da história; a infraestrutura em que o seu projeto é executado também desempenha um papel decisivo na estabilidade e segurança do serviço:

Radib Hosting — Alojamento web de alta velocidade com suporte completo para SSL gratuito e configuração padrão de cabeçalhos; uma escolha ideal para alojar o frontend e backend dos seus projetos.

Radib Virtual Server — Se o seu projeto requer acesso completo, recursos dedicados e liberdade na configuração do servidor web (Nginx/Apache), os servidores virtuais Radib com hardware poderoso e rede estável são a melhor escolha para APIs profissionais.

Radib Debugging and Security Services — Se está a lidar com erros complexos de CORS, problemas de autenticação ou desafios de segurança, a equipa técnica da Radib, com profunda experiência em resolução de problemas e segurança de aplicações web, está aqui para ajudá-lo a resolver o problema pela raiz.

 

Esta resposta foi útil? 112 Utilizadores acharam útil (112 Votos)