Почему JavaScript выдает ошибку CORS, а Postman работает нормально?
В чём разница между браузером и Postman?
Ключевое отличие в том, что Postman — это инструмент для разработчиков, который не реализует политику Same-Origin Policy (SOP). Он просто отправляет HTTP-запрос и показывает ответ, игнорируя заголовки CORS. Браузер же, наоборот, строго соблюдает политику безопасности: если веб-страница (с домена A) пытается сделать запрос к другому домену (B), браузер сначала отправляет предварительный запрос (preflight) с методом OPTIONS и проверяет, разрешает ли сервер B такой кросс-доменный запрос.
Как работает CORS?
CORS (Cross-Origin Resource Sharing) — это механизм безопасности браузера. Он работает так:
- Сервер B должен явно разрешить доступ с других доменов, отправив заголовок
Access-Control-Allow-Origin. - Если этого заголовка нет или он не соответствует домену A, браузер блокирует запрос и показывает ошибку.
- Postman не проверяет этот заголовок, поэтому видит успешный ответ.
Почему ваш код не работает?
Вы открываете HTML-файл через file://. В этом случае происхождение (origin) вашей страницы — null. Большинство серверов не разрешают доступ с null-происхождения по соображениям безопасности. Даже если бы вы запустили локальный сервер (например, через http://localhost:3000), сервер API должен был бы явно разрешить этот домен.
Как это исправить?
Есть несколько решений:
1. Используйте серверный прокси (рекомендуется для production)
Создайте простой бэкенд на Node.js, который будет посредником между вашим фронтендом и API:
// server.js
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('/proxy', async (req, res) => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
res.json(data);
});
app.listen(3000);
Теперь ваш фронтенд обращается к http://localhost:3000/proxy — это один origin, поэтому CORS не нужен.
2. Используйте режим no-cors (только для чтения, без доступа к данным)
fetch('https://api.example.com/data', { mode: 'no-cors' })
.then(response => console.log('Ответ получен, но данные скрыты'));
Этот вариант бесполезен для получения данных — вы не сможете прочитать тело ответа.
3. Используйте JSONP (только для GET-запросов, если API поддерживает)
function handleData(data) {
console.log(data);
}
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleData';
document.body.appendChild(script);
4. Запустите локальный сервер и откройте HTML через него
Установите http-server через npm и запустите в папке с вашим HTML:
npx http-server . -p 8080
Откройте http://localhost:8080. Это не решит проблему CORS, но хотя бы origin не будет null.
Почему Postman работает?
Postman — это клиент для API, а не браузер. Он не загружает веб-страницы, не выполняет JavaScript в контексте страницы и, самое главное, не применяет политику Same-Origin Policy. Postman просто отправляет HTTP-запросы и показывает ответы сервера. Он как curl в командной строке — никаких ограничений безопасности, связанных с веб-страницами.
Если вы хотите протестировать CORS-ограничения в Postman, то не получится — он их не эмулирует. Для тестирования CORS используйте браузер или специальные инструменты вроде curl с флагом -H "Origin: http://example.com".
Вывод: Ошибка CORS — это не баг, а функция безопасности браузера. Postman её игнорирует, поэтому и показывает успешный ответ. Чтобы решить проблему, либо настройте сервер API на отправку нужных заголовков, либо используйте прокси-сервер.
<<Дополнительный подход: используйте браузерные расширения
Если вам нужно быстро протестировать запросы без настройки сервера, можно временно отключить CORS в браузере. Это не подходит для production, но полезно для разработки.
Для Chrome:
Установите расширение "CORS Unblock" или "Allow CORS: Access-Control-Allow-Origin". Они добавляют заголовок Access-Control-Allow-Origin: * к ответам сервера на лету.
Запуск Chrome без CORS (только для разработки):
# Windows
chrome.exe --disable-web-security --user-data-dir="C:/temp/chrome_dev"
# macOS
open -a Google\ Chrome --args --disable-web-security --user-data-dir="/tmp/chrome_dev"
# Linux
google-chrome --disable-web-security --user-data-dir="/tmp/chrome_dev"
Важно: Это отключает все проверки безопасности браузера, включая защиту от XSS. Используйте только на своих проектах и никогда на реальных сайтах.
Ещё один вариант: используйте CORS-прокси сервисы
Существуют бесплатные сервисы, которые добавляют CORS-заголовки к запросам. Например:
fetch('https://cors-anywhere.herokuapp.com/https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
Но такие сервисы нестабильны, имеют ограничения по запросам и не подходят для production. Лучше написать свой прокси-сервер.
Совет: Если вы разрабатываете API самостоятельно, обязательно добавьте поддержку CORS на серверной стороне. Для Express.js это делается через middleware cors:
const cors = require('cors');
app.use(cors()); // разрешает все запросы
Для production настройте более строгие правила, указывая конкретные домены, которым разрешён доступ.