Почему в JavaScript ошибка CORS, а в Postman запрос работает? Разбираемся
Access to fetch at 'https://api.example.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.
Вот мой код:
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ key: 'value' })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Ошибка:', error));
Но самое странное: когда я копирую этот же URL, метод и тело запроса в Postman — всё работает идеально! Postman возвращает ожидаемый ответ. Я уже перепробовал кучу советов с форумов: добавлял заголовки, менял режимы fetch, но ничего не помогает.
Почему так происходит? Ведь запросы вроде бы одинаковые. В чём разница между запросом из браузера (JavaScript) и запросом из Postman? Как мне заставить мой код работать? Заранее спасибо за помощь!
<<Ключевое отличие: браузер vs. Postman
Вы столкнулись с классической ситуацией, которая путает многих новичков. Проблема не в вашем коде, а в том, как браузер и Postman обрабатывают запросы к другому домену (кросс-доменные запросы).
Postman — это инструмент для разработчиков. Он отправляет "сырой" HTTP-запрос на сервер и просто показывает ответ. Postman не соблюдает политику Same-Origin Policy (SOP) и механизм CORS. Ему всё равно, какие заголовки приходят в ответе.
Браузер — это среда с повышенными требованиями безопасности. Когда ваш JavaScript (работающий на http://localhost:3000) пытается сделать запрос на https://api.example.com, браузер сначала отправляет предварительный запрос OPTIONS (preflight request). И только если сервер в ответе на этот OPTIONS пришлёт правильные CORS-заголовки, браузер разрешит выполнить ваш основной POST.
Вы получаете ошибку, потому что сервер api.example.com не настроен на приём запросов из внешних источников (вашего localhost). Он не возвращает заголовок Access-Control-Allow-Origin.
Как это исправить? Три пути решения
1. Серверная настройка CORS (правильный способ)
Если у вас есть доступ к серверу, который вы запрашиваете, нужно настроить его на отправку правильных CORS-заголовков. Для Node.js (Express) это выглядит так:
// Серверный код (Node.js + Express)
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); // Или '*' для всех
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// Обрабатываем preflight запрос
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
app.post('/data', (req, res) => {
res.json({ message: 'Успех!' });
});
2. Использование CORS-прокси (для разработки)
Если сервер вам не принадлежит (например, публичное API), можно использовать прокси-сервер, который добавит нужные заголовки. Например, сервис cors-anywhere:
fetch('https://cors-anywhere.herokuapp.com/https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' })
});
Важно: Это подходит только для разработки и тестирования. В продакшене так делать нельзя.
3. JSONP (устаревший метод)
Работает только для GET-запросов. Сервер оборачивает ответ в функцию JavaScript. Используется редко.
Почему Postman работает, а браузер — нет?
- Postman: не проверяет CORS. Он просто показывает ответ сервера.
- Браузер: проверяет CORS и блокирует ответ, если сервер не разрешил доступ. Ошибка возникает на стороне браузера, а не сервера. Сервер скорее всего обработал ваш запрос, но браузер "выбросил" ответ.
Проверить это можно в инструментах разработчика (F12) -> вкладка Network. Вы увидите два запроса: OPTIONS (заблокирован) и POST (часто тоже с ошибкой).
Вывод: Если сервер не ваш — без изменения его настроек CORS, вы не сможете сделать прямой запрос из браузера. Используйте бэкенд-прокси (свой сервер) или попросите владельца API добавить ваш домен в разрешённые.
<<Альтернативный взгляд: режим no-cors и работа с ошибками
Если вам не нужно читать ответ от сервера (например, вы просто отправляете аналитику или логи), вы можете использовать режим no-cors в fetch. Это не решит проблему получения данных, но позволит избежать ошибки в консоли:
fetch('https://api.example.com/data', {
method: 'POST',
mode: 'no-cors', // Важно!
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ key: 'value' })
})
.then(response => {
// response.type будет 'opaque'
// Вы НЕ сможете прочитать тело ответа
console.log('Запрос отправлен, но ответ недоступен');
});
Минусы:
- Вы не получите данные от сервера.
- Вы не увидите HTTP-статус ответа (всегда 0).
- Некоторые заголовки (например, Content-Type) могут быть проигнорированы браузером.
Этот подход часто используется для трекеров или пинг-запросов, где важен только факт отправки. Для полноценной работы с API он не подходит.
Лучшая практика: Если API стороннее и не поддерживает CORS, создайте простой бэкенд-прокси на Node.js или PHP. Ваш фронтенд будет обращаться к своему бэкенду (CORS не нужен, так как домен один), а бэкенд уже будет делать запрос к стороннему API. Это безопасно и надёжно.
Ответов пока нет.
Войдите чтобы оставить ответ