W3docs

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/usersSimprotocolo, host e porta idênticos
http://app.example.com/apiNãoprotocolo diferente (http vs https)
https://api.example.com/usersNãohost diferente (subdomínio conta)
https://app.example.com:8443/apiNãoporta 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 fetch fará 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.

javascript— editable

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 ser true para 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:

Aviso

No servidor, evite Access-Control-Allow-Origin: * em produção. Permita apenas as origens específicas em que você confia.

javascript— editable

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');
Nota

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, HEAD ou POST;
  • inclui cabeçalhos de requisição personalizados (por exemplo, Authorization ou X-Api-Key);
  • usa um Content-Type diferente de application/x-www-form-urlencoded, multipart/form-data ou text/plain (enviar JSON com application/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:

  • fetch rejeita ("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á retornando Access-Control-Allow-Credentials: true com 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-Age para 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

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.

Was this page helpful?