Как работает заголовок Access-Control-Allow-Origin и как исправить CORS-ошибку?
Что такое 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 принадлежит не вам, у вас есть несколько вариантов:
- Использовать прокси-сервер — самый распространённый способ. Вы создаёте свой небольшой сервер (например, на Node.js или Python), который принимает запросы от вашего фронтенда, сам делает запрос к внешнему API и возвращает результат с нужным CORS-заголовком.
- Проверить, поддерживает ли API CORS — многие публичные API (например, GitHub API, JSONPlaceholder) уже добавляют заголовок
Access-Control-Allow-Origin: *. Уточните в документации. - Использовать режим 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')
Это временный прокси, который добавляет заголовок. Но не используйте такие сервисы в реальных проектах — они могут быть нестабильны или небезопасны.