← Назад к вопросам

Как сделать запрос на другой домен без нарушения политики браузера?

2.0 Middle🔥 161 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Запросы на другой домен без нарушения CORS

Это расширение вопроса про CORS. Разберём все способы безопасно делать cross-origin запросы.

Проблема: Same-Origin Policy

Браузер блокирует запросы на другой домен из соображений безопасности:

// На странице https://myapp.com
fetch('https://api.example.com/data'); // БЛОКИРУЕТСЯ!

// Ошибка:
// Access to XMLHttpRequest at 'https://api.example.com/data' 
// from origin 'https://myapp.com' has been blocked by CORS policy

Решение 1: CORS на сервере (ПРАВИЛЬНОЕ)

Сервер должен явно разрешить запросы с твоего домена:

На Node.js/Express:

const express = require('express');
const cors = require('cors');
const app = express();

// Опция 1: разрешить всем (только для development!)
app.use(cors()); // НЕ делай так в production!

// Опция 2: разрешить конкретным доменам (ПРАВИЛЬНО)
const allowedOrigins = [
  'https://myapp.com',
  'https://www.myapp.com',
  'http://localhost:3000' // для development
];

const corsOptions = {
  origin: (origin, callback) => {
    if (allowedOrigins.includes(origin) || !origin) {
      callback(null, true);
    } else {
      callback(new Error('Not allowed by CORS'));
    }
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  credentials: true,
  allowedHeaders: ['Content-Type', 'Authorization']
};

app.use(cors(corsOptions));

// Для preflight запросов
app.options('*', cors(corsOptions));

app.get('/api/data', (req, res) => {
  res.json({ data: 'Hello World' });
});

На Python/FastAPI:

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

allowed_origins = [
    "https://myapp.com",
    "https://www.myapp.com",
    "http://localhost:3000"
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=allowed_origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/api/data")
async def get_data():
    return {"data": "Hello World"}

Решение 2: JSONP (устаревшее)

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

// На сервере должна быть поддержка JSONP
app.get('/api/data', (req, res) => {
  const callback = req.query.callback;
  const data = { data: 'Hello World' };
  res.send(`${callback}(${JSON.stringify(data)})`);
});

// На фронте
const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleData';
document.body.appendChild(script);

function handleData(data) {
  console.log(data);
}

Проблемы с JSONP:

  • Нельзя отправить заголовки
  • Нельзя отправить POST данные
  • Небезопасно (выполняет произвольный JS код)
  • Сложнее обрабатывать ошибки

Решение 3: Proxy на своём сервере (РЕКОМЕНДУЕТСЯ)

Делаем запрос к своему серверу, сервер делает запрос на другой домен:

// На фронте запрашиваем свой сервер
fetch('/api/proxy/data')
  .then(res => res.json())
  .then(data => console.log(data));

// На бэке перенаправляем запрос
app.get('/api/proxy/data', async (req, res) => {
  try {
    // Делаем запрос на внешний API с сервера
    const response = await fetch('https://external-api.com/data');
    const data = await response.json();
    res.json(data);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Решение 4: Credentials (с аутентификацией)

Если нужно отправить cookies/токены:

// На фронте
fetch('https://api.example.com/data', {
  method: 'GET',
  credentials: 'include', // отправляем cookies
  headers: {
    'Authorization': 'Bearer token'
  }
});

// На сервере
const corsOptions = {
  origin: 'https://myapp.com',
  credentials: true, // ВАЖНО!
  allowedHeaders: ['Content-Type', 'Authorization']
};

app.use(cors(corsOptions));

Решение 5: Шифрование в URL (не безопасно)

Можно передать данные как параметры, но это не безопасно:

// ПЛОХО - данные видны в URL
fetch('https://api.example.com/data?apiKey=secret123')

// ХОРОШО - данные в заголовках
fetch('https://api.example.com/data', {
  headers: {
    'Authorization': 'Bearer secret123'
  }
})

Практический пример: интеграция с сторонним API

// Сценарий: нужно получить данные с weather API

// НЕПРАВИЛЬНО - прямой запрос
function getWeather(city) {
  // Это не будет работать из-за CORS
  return fetch(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=key`)
    .then(res => res.json());
}

// ПРАВИЛЬНО - через свой proxy
// На фронте
function getWeather(city) {
  return fetch(`/api/weather?city=${city}`)
    .then(res => res.json());
}

// На бэке (Express)
app.get('/api/weather', async (req, res) => {
  const city = req.query.city;
  
  if (!city) {
    return res.status(400).json({ error: 'City required' });
  }
  
  try {
    const response = await fetch(
      `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${process.env.WEATHER_API_KEY}`
    );
    
    if (!response.ok) {
      throw new Error(`API returned ${response.status}`);
    }
    
    const data = await response.json();
    
    // Можем обработать, кэшировать, валидировать
    res.json({
      city: data.name,
      temp: data.main.temp,
      description: data.weather[0].description
    });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Отладка CORS проблем

// Шаг 1: Открываю DevTools -> Network
// Ищу мой запрос

// Шаг 2: Проверяю Response Headers
// Должны быть:
// Access-Control-Allow-Origin: https://myapp.com
// Access-Control-Allow-Methods: GET, POST
// Access-Control-Allow-Headers: Content-Type, Authorization

// Шаг 3: Если это preflight (OPTIONS запрос)
// Проверяю что сервер отвечает на OPTIONS

// Шаг 4: Проверяю консоль
// Точное сообщение об ошибке подскажет что не так

// Ошибка: "Credentials mode of requests is 'include'"
// Решение: сервер должен иметь Access-Control-Allow-Credentials: true

// Ошибка: "Method not allowed"
// Решение: добавить метод в Access-Control-Allow-Methods

// Ошибка: "Missing header"
// Решение: добавить заголовок в Access-Control-Allow-Headers

Сравнение подходов

ПодходБезопасностьСложностьСкорость
CORS на сервереХорошаяПростоБыстро
ProxyОтличнаяСреднеМожет быть медленнее
JSONPПлохаяПростоБыстро
CredentialsЗависитСложноНормально

Мой выбор

// Если контролирую оба сервера:
// CORS на обоих - самое простое

const corsOptions = {
  origin: process.env.ALLOWED_ORIGINS.split(','),
  credentials: true
};
app.use(cors(corsOptions));

// Если интегрирую сторонний API:
// Proxy на своём сервере - безопаснее

app.get('/api/external', async (req, res) => {
  const data = await fetch('https://external.com/api', {
    headers: {
      'Authorization': `Bearer ${process.env.EXTERNAL_API_KEY}`
    }
  }).then(r => r.json());
  res.json(data);
});

// Если нужна максимальная безопасность:
// Proxy + кэширование + rate limiting

const cache = new Map();

app.get('/api/external', async (req, res) => {
  const cacheKey = 'external-data';
  
  // Проверяю кэш
  if (cache.has(cacheKey)) {
    return res.json(cache.get(cacheKey));
  }
  
  // Запрашиваю только если кэш пустой
  const data = await fetch('https://external.com/api')
    .then(r => r.json());
  
  // Сохраняю в кэш на 5 минут
  cache.set(cacheKey, data);
  setTimeout(() => cache.delete(cacheKey), 5 * 60 * 1000);
  
  res.json(data);
});

Итого

Для запросов на другой домен:

  1. Лучший способ - CORS на сервере (если контролируешь)
  2. Безопаснее - proxy на своём сервере (скрываешь API ключи)
  3. Не используй - JSONP (устаревший и небезопасный)
  4. Помни - эти ограничения работают только в браузере, не в Node.js
  5. Документируй - какие домены разрешены и почему