JavaScript Fetch: Requisições Cross-Origin (CORS)
Aprenda a fazer requisições cross-origin com a Fetch API do JavaScript: como o CORS funciona, as opções mode e credentials, requisições preflight e boas práticas para tratar erros.
Requisições cross-origin permitem que uma página web carregue dados de um servidor em uma origem diferente da própria página. Elas sustentam praticamente todo aplicativo moderno: chamar uma API pública, comunicar-se com seu próprio backend em outro subdomínio ou incorporar um serviço de terceiros. Esta página explica como as regras CORS do navegador funcionam, como fazer chamadas cross-origin com a Fetch API e como tratar credenciais, requisições preflight e erros corretamente.
O que é considerado "cross-origin"
Uma origem é a combinação de protocolo + host + porta. Duas URLs compartilham a mesma origem somente quando as três coincidem. Se qualquer uma diferir, uma requisição entre elas é cross-origin.
Requisição de https://app.example.com para… | Mesma origem? | Motivo |
|---|---|---|
https://app.example.com/api/users | Sim | protocolo, host e porta idênticos |
http://app.example.com/api | Não | protocolo diferente (http vs https) |
https://api.example.com/users | Não | host diferente (subdomínio conta) |
https://app.example.com:8443/api | Não | porta diferente |
Requisições de mesma origem são irrestritas. Requisições cross-origin são governadas pelo CORS.
O que é o CORS (e o que não é)
Cross-Origin Resource Sharing (CORS) é um mecanismo de segurança do navegador que decide se o JavaScript de uma origem tem permissão para ler uma resposta de outra origem. A decisão é tomada pelo servidor, que concede permissão enviando cabeçalhos de resposta HTTP específicos. O navegador então os aplica.
Dois pontos são fáceis de confundir:
- CORS não é algo que você corrige apenas no código do front-end. Se o servidor não enviar os cabeçalhos corretos, nenhuma opção do
fetchfará a leitura ter sucesso. - A requisição frequentemente ainda chega ao servidor e é processada lá; o CORS apenas impede que seu script leia a resposta. Por isso o CORS não substitui a autenticação.
Fazendo uma requisição cross-origin com Fetch
A Fetch API é a maneira moderna e baseada em promises de fazer requisições HTTP. Uma chamada simples ao fetch para outra origem já é cross-origin — o navegador lida com o CORS automaticamente.
Isso funciona porque jsonplaceholder.typicode.com retorna Access-Control-Allow-Origin: *. Se não retornasse, o navegador bloquearia a leitura e o fetch rejeitaria. Consulte o capítulo da Fetch API para o modelo completo de requisição/resposta.
Os cabeçalhos do servidor que fazem isso funcionar
Quando você faz uma requisição cross-origin, o servidor deve incluir cabeçalhos CORS para permitir a operação. Os mais comuns:
Access-Control-Allow-Origin— qual(is) origem(ns) podem ler a resposta (uma origem exata ou*para qualquer uma).Access-Control-Allow-Methods— quais métodos HTTP são permitidos (usado em respostas preflight).Access-Control-Allow-Headers— quais cabeçalhos de requisição o cliente pode enviar (usado em respostas preflight).Access-Control-Allow-Credentials— se cookies/autenticação podem ser enviados (deve sertruepara permitir credenciais).
Você configura isso no servidor, não no fetch. O front-end controla apenas a requisição.
A opção mode
A opção mode do Fetch declara como o tratamento cross-origin deve se comportar:
'cors'— o padrão. A requisição só é permitida se o servidor retornar cabeçalhos CORS compatíveis; caso contrário, a leitura é bloqueada.'same-origin'— falha imediatamente para qualquer URL cross-origin.'no-cors'— envia a requisição, mas retorna uma resposta opaca: você não pode ler seu status, cabeçalhos ou corpo. Útil apenas para casos do tipo fire-and-forget, como armazenar em cache uma imagem em um service worker.
Como 'cors' já é o padrão, raramente é necessário defini-lo, mas ser explícito documenta a intenção:
No servidor, evite Access-Control-Allow-Origin: * em produção. Permita apenas as origens específicas em que você confia.
Enviando credenciais
Por padrão, requisições fetch cross-origin não enviam cookies ou cabeçalhos de autenticação HTTP. Para incluí-los, defina a opção credentials como 'include'. O servidor também deve responder com Access-Control-Allow-Credentials: true e informar sua origem exata em Access-Control-Allow-Origin — * é rejeitado quando credenciais estão envolvidas. Consulte Cookies e document.cookie para entender como os cookies se comportam entre sites.
async function fetchWithCredentials(url) {
try {
const response = await fetch(url, {
mode: 'cors',
credentials: 'include'
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Fetch error:', error);
}
}
fetchWithCredentials('https://api.crossorigin.com/secure-data');Quando uma política CORS bloqueia uma leitura cross-origin, o navegador não entrega a resposta ao seu script. Em vez disso, o fetch rejeita com um TypeError ("Failed to fetch"), e o erro cai no seu bloco catch — não na verificação if (!response.ok).
Requisições preflight
Para requisições que o navegador considera potencialmente inseguras, ele primeiro envia automaticamente uma requisição OPTIONS — um preflight — para perguntar ao servidor se a requisição real é permitida. Seu código nunca emite essa chamada OPTIONS; o navegador faz isso por você.
Uma requisição dispara um preflight quando não é uma "requisição simples", ou seja, quando ela:
- usa um método diferente de
GET,HEADouPOST; - inclui cabeçalhos de requisição personalizados (por exemplo,
AuthorizationouX-Api-Key); - usa um
Content-Typediferente deapplication/x-www-form-urlencoded,multipart/form-dataoutext/plain(enviar JSON comapplication/jsoné o gatilho mais comum).
Um POST JSON típico, portanto, custa duas viagens de ida e volta — o preflight e depois a requisição real:
async function createPost(url, payload) {
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' }, // triggers a preflight
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Fetch error:', error);
}
}
createPost('https://api.example.com/posts', { title: 'Hello' });Para que o preflight seja aprovado, o servidor deve responder à requisição OPTIONS com Access-Control-Allow-Methods e Access-Control-Allow-Headers que cubram o que a requisição real utilizará. Se falhar, o navegador gera um erro de rede e a requisição real nunca é enviada.
Distinguindo os modos de falha
Erros de CORS são notoriamente confusos porque o navegador oculta detalhes por segurança. Use esta lista de verificação:
fetchrejeita ("Failed to fetch") — geralmente um bloqueio CORS, uma falha de rede ou um preflight mal-sucedido. Verifique o console do navegador para a mensagem CORS específica; ela não é exposta ao JavaScript.response.okéfalse— a requisição foi bem-sucedida e o CORS passou, mas o servidor retornou um status 4xx/5xx. Trata-se de um erro da aplicação, não de CORS.- Cookies não enviados — você esqueceu
credentials: 'include', ou o servidor não está retornandoAccess-Control-Allow-Credentials: truecom uma origem específica.
Envolva as chamadas em try/catch e inspecione response.ok separadamente, como nos exemplos acima.
Boas práticas
- Restrinja o
Access-Control-Allow-Origin. Liste as origens exatas em que você confia em vez de usar*, especialmente quando credenciais estão envolvidas (*é rejeitado com credenciais). - Use sempre HTTPS. Protege os dados em trânsito e é exigido por muitas APIs de contexto seguro.
- Reduza os preflights. Evite cabeçalhos personalizados desnecessários e permita que o servidor envie
Access-Control-Max-Agepara que o navegador armazene em cache os resultados do preflight. - Trate os erros com cuidado. Separe falhas de transporte/CORS (
catch) de erros de status HTTP (!response.ok) e apresente mensagens úteis ao usuário. - Não dependa do CORS para segurança. Ele controla o que os navegadores permitem que scripts leiam; não autentica o chamador. Valide e autorize cada requisição no servidor.
Capítulos relacionados
- Fetch API — a base para cada requisição nesta página.
- Abortar um fetch — cancele requisições lentas ou indesejadas.
- Trabalhando com JSON — analise e serialize corpos de requisição e resposta.
- Cookies: document.cookie — como os cookies interagem com credenciais cross-origin.
Resumo
Uma requisição é cross-origin quando seu protocolo, host ou porta difere do da página. O CORS permite que o servidor decida quais origens podem ler a resposta, e o navegador aplica essa decisão. Com o Fetch, o modo 'cors' é o padrão; use a opção credentials para enviar cookies, espere uma requisição preflight OPTIONS para chamadas não simples e trate as falhas de CORS no catch enquanto verifica response.ok para erros no nível HTTP.