CORS: erros comuns e configurações de servidor
1. Entendendo o CORS: Origem e Mecanismo
CORS (Cross-Origin Resource Sharing) é um mecanismo de segurança implementado pelos navegadores que permite ou restringe requisições HTTP entre diferentes origens. Uma origem é definida pela combinação de protocolo, domínio e porta. Por exemplo, https://api.exemplo.com e https://app.exemplo.com são origens diferentes.
O CORS existe para proteger os usuários contra ataques como CSRF (Cross-Site Request Forgery) e vazamento de dados. Sem ele, qualquer site malicioso poderia fazer requisições arbitrárias para outros domínios e ler as respostas.
Existem dois tipos de requisições CORS:
- Requisições simples: GET, HEAD ou POST com Content-Type limitado (text/plain, application/x-www-form-urlencoded, multipart/form-data)
- Requisições preflight: qualquer outra requisição que dispara uma requisição OPTIONS antes da requisição real
Headers fundamentais no CORS:
# Headers de requisição (enviados pelo navegador)
Origin: https://meuapp.com
# Headers de resposta (enviados pelo servidor)
Access-Control-Allow-Origin: https://meuapp.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
2. Erro Clássico: "No 'Access-Control-Allow-Origin' header is present"
Este é o erro mais comum ao implementar CORS. O navegador bloqueia a requisição porque o servidor não inclui o header Access-Control-Allow-Origin na resposta.
Causa: O servidor não está configurado para aceitar requisições de origens diferentes.
Como depurar:
1. Abra o console do navegador (F12)
2. Vá para a aba "Network" (Rede)
3. Faça a requisição e observe a resposta
4. Verifique se os headers CORS estão presentes
Solução:
# Configuração no servidor (exemplo em Express.js)
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
next();
});
# Ou usando middleware específico
const cors = require('cors');
app.use(cors({ origin: '*' }));
3. Erro com Credenciais: Cookies e Headers de Autenticação
Quando você precisa enviar cookies ou headers de autenticação (como Authorization), o cenário se torna mais restritivo.
Erro comum: "The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*' when credentials mode is 'include'"
Problema: Ao usar withCredentials: true no frontend, o servidor não pode usar * como origem.
Configuração correta:
# Frontend (JavaScript)
fetch('https://api.exemplo.com/dados', {
method: 'GET',
credentials: 'include', // ou 'same-origin'
headers: {
'Authorization': 'Bearer token123'
}
});
# Backend (Express.js)
app.use(cors({
origin: 'https://meuapp.com', // Origem específica, não usar '*'
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE']
}));
4. Falhas em Requisições Preflight (OPTIONS)
Requisições preflight são disparadas automaticamente pelo navegador para métodos que podem modificar dados (PUT, DELETE, PATCH) ou quando headers personalizados são usados.
Erro: "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource"
Solução: Configurar o servidor para responder adequadamente às requisições OPTIONS.
# Configuração para preflight
app.options('*', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', 'https://meuapp.com');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.setHeader('Access-Control-Max-Age', '86400'); // Cache por 24 horas
res.status(204).end();
});
5. Headers Personalizados e Métodos Não-Padrão
Headers personalizados (como X-Requested-With, X-Custom-Header) e métodos como PUT, DELETE e PATCH sempre disparam requisições preflight.
Erro: "Request header field X-Custom-Header is not allowed by Access-Control-Allow-Headers in preflight response"
Solução: Listar explicitamente todos os headers e métodos permitidos.
# Configuração que permite headers personalizados
app.use(cors({
origin: 'https://meuapp.com',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Custom-Header', 'X-Requested-With']
}));
6. Configurações de Servidor: Exemplos Práticos
Nginx
server {
listen 80;
server_name api.exemplo.com;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' 'https://app.exemplo.com';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
add_header 'Access-Control-Max-Age' 86400;
return 204;
}
add_header 'Access-Control-Allow-Origin' 'https://app.exemplo.com';
add_header 'Access-Control-Allow-Credentials' 'true';
proxy_pass http://localhost:3000;
}
}
Apache
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "https://app.exemplo.com"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Header set Access-Control-Allow-Headers "Content-Type, Authorization"
Header set Access-Control-Allow-Credentials "true"
</IfModule>
Django (Python)
# settings.py
INSTALLED_APPS = [
'corsheaders',
# ...
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
# ...
]
CORS_ALLOWED_ORIGINS = [
"https://app.exemplo.com",
]
CORS_ALLOW_CREDENTIALS = True
Spring Boot (Java)
// WebConfig.java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("https://app.exemplo.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
7. Estratégias Avançadas e Debugging
Lidando com múltiplas origens dinamicamente
# Permite múltiplas origens baseadas em whitelist
const allowedOrigins = ['https://app1.com', 'https://app2.com'];
app.use((req, res, next) => {
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
next();
});
Proxy reverso para desenvolvimento
# Exemplo com webpack-dev-server
// webpack.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}
}
};
Ferramentas de debugging
- Extensões de navegador: CORS Toggle, Allow CORS
- Ferramentas de rede: Chrome DevTools, Firefox Developer Tools
- Simulação de requisições: Postman, curl com headers personalizados
# Testando CORS com curl
curl -H "Origin: https://meuapp.com" \
-H "Access-Control-Request-Method: GET" \
-X OPTIONS \
-v https://api.exemplo.com/dados
Referências
- MDN Web Docs: CORS — Documentação completa sobre Cross-Origin Resource Sharing, incluindo headers e exemplos práticos
- CORS Tutorial by Auth0 — Guia abrangente sobre CORS com exemplos de configuração em diferentes frameworks
- Express.js CORS Middleware Documentation — Documentação oficial do middleware CORS para Node.js/Express.js
- Nginx CORS Configuration Guide — Guia prático para configurar CORS em servidores Nginx
- Spring Boot CORS Configuration — Tutorial oficial da Spring sobre configuração CORS em aplicações REST
- Django CORS Headers Documentation — Documentação do pacote django-cors-headers para configuração CORS em Django
- CORS Errors Debugging Guide — Guia do Chrome DevTools para depuração de erros CORS no navegador