Прокси-сервер — промежуточный узел между клиентским приложением и удалённым сервером. В контексте веб-карт и приложений на базе Leaflet прокси используется для перенаправления, фильтрации, кеширования и защиты сетевых запросов.
Схема взаимодействия выглядит следующим образом:
Leaflet-приложение
│
▼
Прокси-сервер
│
▼
Картографический сервис
При использовании прокси браузер взаимодействует не напрямую с поставщиком картографических данных, а через специальный сервер-посредник.
Одной из наиболее распространённых причин внедрения прокси является ограничение политики безопасности браузеров — Cross-Origin Resource Sharing (CORS).
Предположим, приложение размещено по адресу:
https://my-map.com
А данные необходимо получить с сервиса:
https://data.example.org
Если сервер не разрешает междоменные запросы, браузер заблокирует обращение:
fetch('https://data.example.org/points.json');
В этом случае запрос перенаправляется на собственный сервер:
fetch('/proxy/points');
Сервер получает данные от удалённого ресурса и возвращает их клиенту уже без нарушения политики безопасности.
Многие картографические сервисы требуют использование ключей доступа.
Нежелательный вариант:
L.tileLayer(
'https://maps.example.com/{z}/{x}/{y}.png?key=SECRET_KEY'
);
После загрузки страницы любой пользователь сможет увидеть ключ через инструменты разработчика.
Безопасная схема предполагает хранение ключа на сервере:
Браузер
│
▼
Прокси
│ + API Key
▼
Картографический сервис
В этом случае ключ никогда не попадает в клиентский код.
Прокси позволяет ограничивать доступ к картографическим данным.
Например:
Запрос проходит дополнительную проверку:
GET /proxy/tiles/12/2200/1400
Прокси может определить:
Только после этого запрос будет передан внешнему сервису.
Картографические сервисы часто предоставляют одинаковые тайлы тысячам пользователей.
Без кеширования каждый запрос отправляется во внешний сервис:
Пользователь 1 → Сервер карт
Пользователь 2 → Сервер карт
Пользователь 3 → Сервер карт
С прокси и кешем:
Пользователь 1 → Прокси → Сервер карт
Пользователь 2 → Прокси
Пользователь 3 → Прокси
После первого обращения тайл сохраняется локально.
Преимущества:
Наиболее распространённый сценарий.
Вместо прямого обращения:
L.tileLayer(
'https://tile.server.com/{z}/{x}/{y}.png'
);
Используется собственный сервер:
L.tileLayer(
'/tiles/{z}/{x}/{y}.png'
);
Далее сервер выполняет:
/tiles/10/500/300.png
│
▼
https://tile.server.com/10/500/300.png
Пользователь взаимодействует только с собственным доменом.
Leaflet активно использует формат GeoJSON.
Получение данных через прокси:
fetch('/api/geojson/cities')
.then(response => response.json())
.then(data => {
L.geoJSON(data).addTo(map);
});
На стороне сервера:
/api/geojson/cities
│
▼
https://external-api.com/cities.geojson
Многие геоинформационные системы предоставляют данные через протокол WMS.
Стандартное подключение:
L.tileLayer.wms(
'https://gis.example.com/wms',
{
layers: 'roads'
}
);
Через прокси:
L.tileLayer.wms(
'/proxy/wms',
{
layers: 'roads'
}
);
Прокси формирует полный запрос к удалённому серверу и возвращает результат клиенту.
Для организации проксирования часто используется Express.
Установка зависимостей:
npm install express axios
Минимальный сервер:
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/proxy', async (req, res) => {
const url = req.query.url;
try {
const response = await axios.get(url);
res.json(response.data);
} catch (error) {
res.status(500).send('Proxy error');
}
});
app.listen(3000);
Использование в Leaflet:
fetch('/proxy?url=https://api.example.com/data')
.then(response => response.json())
.then(data => {
L.geoJSON(data).addTo(map);
});
Более реалистичный пример:
app.get('/tiles/:z/:x/:y', async (req, res) => {
const { z, x, y } = req.params;
const url =
`https://tile.openstreetmap.org/${z}/${x}/${y}.png`;
const response = await axios.get(url, {
responseType: 'stream'
});
response.data.pipe(res);
});
Подключение в Leaflet:
L.tileLayer(
'/tiles/{z}/{x}/{y}',
{
maxZoom: 19
}
).addTo(map);
Без ограничений злоумышленник способен создать значительную нагрузку.
Для защиты используется Rate Limiting.
Пример:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 60000,
max: 100
});
app.use('/proxy', limiter);
Параметры означают:
При превышении лимита сервер вернёт ошибку.
Опасной практикой считается открытый прокси.
Плохой вариант:
/proxy?url=https://any-site.com
Злоумышленник сможет использовать сервер для произвольных запросов.
Правильный подход — ограничить список разрешённых ресурсов:
const allowedHosts = [
'api.example.com',
'tile.server.com'
];
Проверка:
const host = new URL(url).hostname;
if (!allowedHosts.includes(host)) {
return res.status(403).send('Forbidden');
}
Простейший вариант:
const cache = {};
Сохранение:
cache[key] = response.data;
Получение:
if (cache[key]) {
return res.send(cache[key]);
}
Подход подходит для небольших приложений.
Для крупных проектов чаще применяется Redis.
Схема работы:
Leaflet
│
▼
Прокси
│
├── Redis
│
▼
Удалённый сервис
Преимущества:
Прокси должен корректно реагировать на недоступность удалённых сервисов.
Пример:
try {
const response = await axios.get(url);
res.send(response.data);
}
catch(error) {
res.status(502).json({
error: 'Bad Gateway'
});
}
Клиентская часть:
fetch('/proxy/data')
.then(response => {
if (!response.ok) {
throw new Error();
}
return response.json();
})
.catch(() => {
alert('Источник данных недоступен');
});
В крупных геоинформационных системах важно отслеживать обращения к сервисам.
Пример регистрации:
app.use((req, res, next) => {
console.log(
new Date(),
req.ip,
req.url
);
next();
});
Логи помогают:
Современные картографические приложения должны использовать защищённые соединения.
Нежелательная конфигурация:
https://site.com
http://proxy.site.com
Возникает проблема смешанного контента (Mixed Content).
Корректный вариант:
https://site.com
https://proxy.site.com
Все запросы выполняются по защищённому каналу.
Работает от имени клиента.
Схема:
Клиент
│
▼
Forward Proxy
│
▼
Интернет
Используется для:
Работает от имени сервера.
Схема:
Клиент
│
▼
Reverse Proxy
│
▼
Приложение
Наиболее распространён в веб-разработке.
Для Leaflet-проектов обычно применяется именно Reverse Proxy.
Пример конфигурации:
location /tiles/ {
proxy_pass https://tile.server.com/;
}
После настройки запрос:
/tiles/10/500/300.png
будет автоматически перенаправлен на:
https://tile.server.com/10/500/300.png
Преимущества Nginx:
При высокой посещаемости одного прокси может оказаться недостаточно.
Схема:
Load Balancer
/ \
/ \
Proxy 1 Proxy 2
\ /
\ /
Tile Server
Балансировщик распределяет запросы между несколькими экземплярами сервиса.
Это обеспечивает:
Прокси используется для:
Прокси обеспечивает:
Прокси позволяет объединять данные из множества источников:
Leaflet
│
▼
Прокси
├─ Геокодер
├─ WMS
├─ GeoJSON API
└─ Тайловый сервер
Приложение получает единый интерфейс взаимодействия независимо от количества внешних систем.
Прокси выполняет:
В результате инфраструктура становится более безопасной, масштабируемой и управляемой, а клиентские приложения на базе Leaflet получают единый контролируемый механизм доступа ко всем картографическим ресурсам и геоданным.