CORS em PHP: Guia Completo com Exemplos Práticos (incluindo .htaccess)

CORS (Cross-Origin Resource Sharing) é um mecanismo de segurança crucial para aplicações web modernas. Vamos explorar tudo sobre CORS em PHP, com exemplos práticos incluindo configuração via .htaccess.

O que é CORS?

CORS é um mecanismo que permite que recursos restritos em uma página web sejam solicitados de outro domínio fora do domínio ao qual pertence o recurso que será solicitado.

Por que CORS é importante?

  • Segurança: Evita requisições não autorizadas entre domínios diferentes
  • Flexibilidade: Permite APIs serem consumidas por diferentes front-ends
  • Controle: Define exatamente quais origens, métodos e headers são permitidos

Configurando CORS no PHP

Método básico com headers PHP

<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Allow-Credentials: true");

// Sua lógica de aplicação continua aqui...

Versão mais segura (origem específica)

<?php
$allowedOrigins = [
    "https://seusite.com",
    "https://app.seusite.com",
    "http://localhost:3000"
];

$origin = $_SERVER['HTTP_ORIGIN'] ?? '';

if (in_array($origin, $allowedOrigins)) {
    header("Access-Control-Allow-Origin: $origin");
}
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Max-Age: 86400"); // Cache por 24 horas

// Tratamento para requisições OPTIONS (preflight)
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    header("HTTP/1.1 200 OK");
    exit();
}

Configurando CORS via .htaccess

Para quem usa Apache, o .htaccess pode ser uma forma mais limpa de gerenciar CORS:

Configuração básica no .htaccess

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "*"
    Header set Access-Control-Allow-Methods "GET, POST, OPTIONS"
    Header set Access-Control-Allow-Headers "Content-Type, Authorization"
</IfModule>

Configuração avançada no .htaccess

<IfModule mod_headers.c>
    SetEnvIf Origin "^(https?://(localhost:\d+|(.*\.)?seusite\.com)(:\d+)?)$" CRS=$0
    Header always set Access-Control-Allow-Origin "%{CRS}e" env=CRS
    Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
    Header always set Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With"
    Header always set Access-Control-Allow-Credentials "true"
    Header always set Access-Control-Max-Age "86400"

    # Resposta para requisições OPTIONS (preflight)
    RewriteEngine On
    RewriteCond %{REQUEST_METHOD} OPTIONS
    RewriteRule ^(.*)$ $1 [R=200,L]
</IfModule>

Lidando com Credenciais

Quando você precisa enviar cookies ou autenticação via CORS:

<?php
header("Access-Control-Allow-Origin: https://seusite.com");
header("Access-Control-Allow-Credentials: true");

// Para ler cookies em requisições cross-origin
session_set_cookie_params([
    'samesite' => 'None',
    'secure' => true,
    'httponly' => true
]);
session_start();

Exemplo Completo: API PHP com CORS

<?php
// config_cors.php
function handleCORS() {
    $allowedOrigins = [
        "https://seusite.com",
        "https://app.seusite.com",
        "http://localhost:3000"
    ];

    $origin = $_SERVER['HTTP_ORIGIN'] ?? '';

    if (in_array($origin, $allowedOrigins)) {
        header("Access-Control-Allow-Origin: $origin");
        header("Access-Control-Allow-Credentials: true");
    } else {
        header("Access-Control-Allow-Origin: null");
    }

    header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
    header("Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With");
    header("Access-Control-Max-Age: 86400");

    if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
        header("HTTP/1.1 200 OK");
        exit();
    }
}

// api.php
require 'config_cors.php';
handleCORS();

header("Content-Type: application/json");

$response = [
    'status' => 'success',
    'data' => [
        'message' => 'API funcionando com CORS configurado!',
        'timestamp' => time()
    ]
];

echo json_encode($response);

Problemas Comuns e Soluções

  1. Erro “No ‘Access-Control-Allow-Origin’ header”
  • Verifique se o header está sendo enviado
  • Confira se o domínio de origem está na lista de permitidos
  1. Cookies não são enviados
  • Certifique-se que Access-Control-Allow-Credentials: true
  • Configure os cookies com SameSite=None e Secure
  1. Headers personalizados bloqueados
  • Adicione todos os headers necessários em Access-Control-Allow-Headers
  1. Métodos HTTP não permitidos
  • Inclua todos os métodos necessários em Access-Control-Allow-Methods

Boas Práticas

  1. Nunca use * para Access-Control-Allow-Origin quando estiver usando credenciais
  2. Limite os métodos HTTP permitidos apenas aos necessários
  3. Considere usar um middleware para gerenciar CORS em aplicações grandes
  4. Para APIs públicas, documente claramente as políticas CORS

Testando sua Configuração CORS

Você pode testar usando curl:

curl -H "Origin: http://seusite.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: X-Requested-With" \
-X OPTIONS --verbose http://suaapi.com/endpoint

Ou via JavaScript:

fetch('http://suaapi.com/endpoint', {
  method: 'POST',
  credentials: 'include',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  body: JSON.stringify({test: 'data'})
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

Conclusão

Configurar corretamente o CORS é essencial para a segurança e funcionalidade de suas aplicações web. Com PHP, você tem flexibilidade para implementar tanto soluções simples quanto configurações complexas e granulares. O uso do .htaccess pode simplificar a manutenção, especialmente em ambientes compartilhados.

Lembre-se: segurança primeiro! Sempre restrinja as permissões ao mínimo necessário para o funcionamento da sua aplicação.

Quer um bônus secreto? Aqui vai uma pérola para debug em produção:

// debug_cors.php (remover depois!)
file_put_contents('cors_log.txt', 
    date('Y-m-d H:i:s') . " - " . 
    ($_SERVER['HTTP_ORIGIN'] ?? 'NO_ORIGIN') . "\n",
    FILE_APPEND
);

Isso cria um log simplão pra ver quais origens estão batendo na sua API (útil quando o cliente diz “não funciona” mas esquece te dizer qual domínio está usando 😅).

Rolar para cima