Avatar ·

Как работает заголовок Access-Control-Allow-Origin и как исправить CORS-ошибку?

Всем привет! Я новичок в JavaScript и пытаюсь сделать запрос с фронтенда на другой домен (API погоды). У меня простой код на чистом JS: ```javascript fetch('https://api.example-weather.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Ошибка:', error)); ``` Но в консоли браузера я вижу ошибку: > Access to fetch at 'https://api.example-weather.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Я читал, что нужно добавить заголовок `Access-Control-Allow-Origin`, но не понимаю, куда его добавлять: на сервер или на клиент? Я пробовал добавить его в fetch вот так: ```javascript fetch('https://api.example-weather.com/data', { headers: { 'Access-Control-Allow-Origin': '*' } }) ``` Но это не работает. Объясните, пожалуйста, как на самом деле работает этот заголовок и что мне делать, чтобы мой запрос выполнился? Я использую локальный сервер через Live Server в VS Code. Заранее спасибо! <<>>

Что такое CORS и почему возникает ошибка?

CORS (Cross-Origin Resource Sharing) — это механизм безопасности браузера, который запрещает веб-страницам делать запросы на другой домен (протокол, порт или хост), если сервер явно этого не разрешит. Ваш код работает на localhost:3000, а API находится на api.example-weather.com — это разные источники (origin). Браузер блокирует запрос, потому что сервер API не прислал разрешающий заголовок.

Главное правило: заголовок устанавливает сервер, а не клиент

Вы не можете добавить Access-Control-Allow-Origin в fetch или XMLHttpRequest на клиенте. Этот заголовок должен приходить в ответе от сервера. Браузер проверяет ответ: если в нём нет заголовка Access-Control-Allow-Origin, который разрешает ваш origin, браузер блокирует доступ к данным (даже если сервер их прислал).

Попробуйте открыть вкладку Network в инструментах разработчика (F12). Вы увидите, что запрос уходит на сервер, сервер возвращает данные (статус 200), но браузер не даёт JavaScript их прочитать — именно из-за отсутствия нужного заголовка.

Как должен выглядеть правильный ответ сервера

Сервер должен добавить в HTTP-ответ один из следующих заголовков:

  • Access-Control-Allow-Origin: * — разрешить любому сайту (небезопасно для production).
  • Access-Control-Allow-Origin: http://localhost:3000 — разрешить только конкретному источнику.
  • Access-Control-Allow-Origin: https://mysite.com — для продакшена.

Что делать, если вы не управляете сервером API?

Если API принадлежит не вам, у вас есть несколько вариантов:

  1. Использовать прокси-сервер — самый распространённый способ. Вы создаёте свой небольшой сервер (например, на Node.js или Python), который принимает запросы от вашего фронтенда, сам делает запрос к внешнему API и возвращает результат с нужным CORS-заголовком.
  2. Проверить, поддерживает ли API CORS — многие публичные API (например, GitHub API, JSONPlaceholder) уже добавляют заголовок Access-Control-Allow-Origin: *. Уточните в документации.
  3. Использовать режим no-cors (ограниченно): fetch(url, { mode: 'no-cors' }). Но тогда вы не сможете прочитать ответ — только отправить запрос. Подходит только для аналитики или логирования.

Пример простого прокси на Node.js (Express)

const express = require('express');
const fetch = require('node-fetch');
const app = express();

app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  next();
});

app.get('/weather', async (req, res) => {
  const response = await fetch('https://api.example-weather.com/data');
  const data = await response.json();
  res.json(data);
});

app.listen(3001, () => console.log('Прокси на порту 3001'));

Тогда на клиенте вы делаете запрос на http://localhost:3001/weather — и CORS-ошибка исчезнет, потому что ваш собственный сервер добавляет нужный заголовок.

Как проверить, что сервер поддерживает CORS?

Используйте curl в терминале:

curl -I https://api.example-weather.com/data

Ищите в выводе строку access-control-allow-origin. Если её нет — сервер не настроен на кросс-доменные запросы.

Запомните: CORS — это защита браузера, а не сервера. Сервер может быть доступен из любого места (через curl, Postman или серверный код), но браузер блокирует чтение ответа, если заголовок отсутствует.

<<>>

Альтернативный подход: JSONP (устарел, но иногда встречается)

До появления CORS разработчики использовали JSONP (JSON with Padding). Этот метод работает только для GET-запросов и использует тег <script>, который не подчиняется политике same-origin. Сервер оборачивает JSON-ответ в вызов функции, которую вы определяете на клиенте.

// Сервер возвращает: callbackFunction({"temp": 25})
function handleWeather(data) {
  console.log(data);
}

const script = document.createElement('script');
script.src = 'https://api.example-weather.com/data?callback=handleWeather';
document.body.appendChild(script);

Недостатки JSONP:

  • Работает только с GET-запросами.
  • Менее безопасен (можно внедрить произвольный код).
  • Современные API почти не поддерживают JSONP, переходя на CORS.

Что выбрать новичку?

Рекомендую освоить прокси-сервер — это даст вам полный контроль. Если API не ваш, а CORS не поддерживается, прокси — единственное надёжное решение для браузера. Для обучения можно использовать сервисы вроде cors-anywhere (но не для продакшена!).

Пример быстрого теста: добавьте https://cors-anywhere.herokuapp.com/ перед URL API в fetch:

fetch('https://cors-anywhere.herokuapp.com/https://api.example-weather.com/data')

Это временный прокси, который добавляет заголовок. Но не используйте такие сервисы в реальных проектах — они могут быть нестабильны или небезопасны.