Avatar ·

Почему JavaScript выдает ошибку CORS, а Postman работает нормально?

Привет всем! Я начинающий фронтенд-разработчик и уже несколько дней бьюсь с одной проблемой. У меня есть простой сайт на HTML/JavaScript, и я пытаюсь отправить GET-запрос к внешнему API (например, https://api.example.com/data). В браузере я получаю ошибку: "No 'Access-Control-Allow-Origin' header is present on the requested resource". При этом, если я делаю точно такой же запрос через Postman, всё работает отлично — данные приходят без проблем. Вот мой код: ```javascript fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) .catch(error => console.error('Ошибка:', error)); ``` Я использую обычный HTML-файл, открытый через file://, без сервера. Сначала я думал, что проблема в моём коде, но Postman же работает! Почему так происходит? Это баг браузера или я что-то неправильно делаю? Нужно ли как-то настраивать сервер? Очень прошу объяснить простыми словами, в чём разница между запросом из браузера и из 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 настройте более строгие правила, указывая конкретные домены, которым разрешён доступ.