Прокси-серверы

Прокси-сервер — промежуточный узел между клиентским приложением и удалённым сервером. В контексте веб-карт и приложений на базе Leaflet прокси используется для перенаправления, фильтрации, кеширования и защиты сетевых запросов.

Схема взаимодействия выглядит следующим образом:

Leaflet-приложение
        │
        ▼
   Прокси-сервер
        │
        ▼
 Картографический сервис

При использовании прокси браузер взаимодействует не напрямую с поставщиком картографических данных, а через специальный сервер-посредник.


Причины использования прокси в Leaflet-приложениях

Обход ограничений CORS

Одной из наиболее распространённых причин внедрения прокси является ограничение политики безопасности браузеров — Cross-Origin Resource Sharing (CORS).

Предположим, приложение размещено по адресу:

https://my-map.com

А данные необходимо получить с сервиса:

https://data.example.org

Если сервер не разрешает междоменные запросы, браузер заблокирует обращение:

fetch('https://data.example.org/points.json');

В этом случае запрос перенаправляется на собственный сервер:

fetch('/proxy/points');

Сервер получает данные от удалённого ресурса и возвращает их клиенту уже без нарушения политики безопасности.


Сокрытие API-ключей

Многие картографические сервисы требуют использование ключей доступа.

Нежелательный вариант:

L.tileLayer(
    'https://maps.example.com/{z}/{x}/{y}.png?key=SECRET_KEY'
);

После загрузки страницы любой пользователь сможет увидеть ключ через инструменты разработчика.

Безопасная схема предполагает хранение ключа на сервере:

Браузер
   │
   ▼
Прокси
   │ + API Key
   ▼
Картографический сервис

В этом случае ключ никогда не попадает в клиентский код.


Контроль доступа

Прокси позволяет ограничивать доступ к картографическим данным.

Например:

  • проверять авторизацию пользователя;
  • фильтровать IP-адреса;
  • ограничивать количество запросов;
  • вести журнал обращений.

Запрос проходит дополнительную проверку:

GET /proxy/tiles/12/2200/1400

Прокси может определить:

  • авторизован ли пользователь;
  • не превышен ли лимит запросов;
  • доступен ли данный слой.

Только после этого запрос будет передан внешнему сервису.


Кеширование картографических данных

Картографические сервисы часто предоставляют одинаковые тайлы тысячам пользователей.

Без кеширования каждый запрос отправляется во внешний сервис:

Пользователь 1 → Сервер карт
Пользователь 2 → Сервер карт
Пользователь 3 → Сервер карт

С прокси и кешем:

Пользователь 1 → Прокси → Сервер карт
Пользователь 2 → Прокси
Пользователь 3 → Прокси

После первого обращения тайл сохраняется локально.

Преимущества:

  • снижение нагрузки на внешний сервис;
  • уменьшение времени ответа;
  • сокращение сетевого трафика;
  • повышение стабильности приложения.

Архитектура проксирования в Leaflet

Проксирование тайлов

Наиболее распространённый сценарий.

Вместо прямого обращения:

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

Пользователь взаимодействует только с собственным доменом.


Проксирование GeoJSON

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-сервисов

Многие геоинформационные системы предоставляют данные через протокол WMS.

Стандартное подключение:

L.tileLayer.wms(
    'https://gis.example.com/wms',
    {
        layers: 'roads'
    }
);

Через прокси:

L.tileLayer.wms(
    '/proxy/wms',
    {
        layers: 'roads'
    }
);

Прокси формирует полный запрос к удалённому серверу и возвращает результат клиенту.


Создание простого прокси на Node.js

Для организации проксирования часто используется 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);
    });

Проксирование тайлов через Express

Более реалистичный пример:

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);

Параметры означают:

  • окно контроля — 60 секунд;
  • максимум 100 запросов.

При превышении лимита сервер вернёт ошибку.


Белые списки адресов

Опасной практикой считается открытый прокси.

Плохой вариант:

/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

Для крупных проектов чаще применяется 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 и прокси-серверы

Современные картографические приложения должны использовать защищённые соединения.

Нежелательная конфигурация:

https://site.com
http://proxy.site.com

Возникает проблема смешанного контента (Mixed Content).

Корректный вариант:

https://site.com
https://proxy.site.com

Все запросы выполняются по защищённому каналу.


Reverse Proxy и Forward Proxy

Forward Proxy

Работает от имени клиента.

Схема:

Клиент
  │
  ▼
Forward Proxy
  │
  ▼
Интернет

Используется для:

  • анонимизации;
  • фильтрации доступа;
  • корпоративных сетей.

Reverse Proxy

Работает от имени сервера.

Схема:

Клиент
  │
  ▼
Reverse Proxy
  │
  ▼
Приложение

Наиболее распространён в веб-разработке.

Для Leaflet-проектов обычно применяется именно Reverse Proxy.


Использование Nginx в качестве прокси

Пример конфигурации:

location /tiles/ {

    proxy_pass https://tile.server.com/;

}

После настройки запрос:

/tiles/10/500/300.png

будет автоматически перенаправлен на:

https://tile.server.com/10/500/300.png

Преимущества Nginx:

  • высокая производительность;
  • встроенное кеширование;
  • балансировка нагрузки;
  • поддержка HTTPS;
  • минимальное потребление ресурсов.

Балансировка нагрузки

При высокой посещаемости одного прокси может оказаться недостаточно.

Схема:

             Load Balancer
             /          \
            /            \
      Proxy 1        Proxy 2
            \            /
             \          /
            Tile Server

Балансировщик распределяет запросы между несколькими экземплярами сервиса.

Это обеспечивает:

  • отказоустойчивость;
  • масштабируемость;
  • более высокую производительность.

Типичные сценарии использования прокси в Leaflet

Публичные картографические порталы

Прокси используется для:

  • кеширования тайлов;
  • защиты API-ключей;
  • ограничения нагрузки.

Корпоративные ГИС

Прокси обеспечивает:

  • контроль доступа;
  • аудит действий пользователей;
  • интеграцию с внутренними сервисами.

Геоаналитические платформы

Прокси позволяет объединять данные из множества источников:

Leaflet
   │
   ▼
Прокси
 ├─ Геокодер
 ├─ WMS
 ├─ GeoJSON API
 └─ Тайловый сервер

Приложение получает единый интерфейс взаимодействия независимо от количества внешних систем.

Высоконагруженные карты

Прокси выполняет:

  • кеширование тайлов;
  • балансировку нагрузки;
  • сжатие данных;
  • мониторинг производительности.

В результате инфраструктура становится более безопасной, масштабируемой и управляемой, а клиентские приложения на базе Leaflet получают единый контролируемый механизм доступа ко всем картографическим ресурсам и геоданным.