← Назад к вопросам
Как сделать запрос на другой домен без нарушения политики браузера?
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);
});
Итого
Для запросов на другой домен:
- Лучший способ - CORS на сервере (если контролируешь)
- Безопаснее - proxy на своём сервере (скрываешь API ключи)
- Не используй - JSONP (устаревший и небезопасный)
- Помни - эти ограничения работают только в браузере, не в Node.js
- Документируй - какие домены разрешены и почему