Proxy серверы

В веб-картографии основная проблема загрузки данных из внешних источников связана с политикой CORS (Cross-Origin Resource Sharing). Браузер ограничивает доступ к ресурсам, если они находятся на другом домене и не содержат корректных заголовков разрешения.

В CesiumJS это проявляется особенно часто, поскольку визуализация сцены опирается на множество удалённых сервисов:

  • тайловые изображения (imagery providers: WMTS, WMS, XYZ)
  • цифровые модели рельефа (terrain providers)
  • 3D Tiles и батчи геоданных
  • векторные слои и GeoJSON
  • ресурсы Cesium ion и сторонние API

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


Архитектурная роль прокси в CesiumJS

Прокси в контексте CesiumJS выступает как промежуточный HTTP/HTTPS шлюз между клиентским приложением и источниками геоданных.

Типовая схема:

CesiumJS (браузер)
        ↓
Proxy сервер (Node.js / Nginx / Backend)
        ↓
WMS / WMTS / Tile server / Cesium ion / API

Функции такого слоя:

  • обход CORS-ограничений
  • нормализация запросов к разным провайдерам
  • добавление токенов авторизации
  • кеширование тайлов и данных
  • унификация URL-структуры
  • защита исходных сервисов от прямого доступа

Поддержка прокси внутри CesiumJS

CesiumJS предоставляет встроенную абстракцию прокси через Proxy и связанные механизмы Resource.

Основные точки интеграции:

Proxy интерфейс

Cesium использует абстракцию:

new Cesium.DefaultProxy(proxyUrl);

или кастомную реализацию:

class MyProxy {
    getURL(resource) {
        return `/proxy?url=${encodeURIComponent(resource)}`;
    }
}

Любой объект, реализующий метод преобразования URL, может использоваться как прокси.


Свойство proxy у Resource

Cesium.Resource — базовый механизм загрузки данных.

const resource = new Cesium.Resource({
    url: "https://example.com/tiles/{z}/{x}/{y}.png",
    proxy: new Cesium.DefaultProxy("/proxy/")
});

При таком подходе все запросы будут проходить через заданный endpoint.


Влияние на ImageryProvider и TerrainProvider

Прокси особенно важен в следующих классах:

  • WebMapServiceImageryProvider
  • WebMapTileServiceImageryProvider
  • UrlTemplateImageryProvider
  • CesiumTerrainProvider

Пример:

const imagery = new Cesium.WebMapServiceImageryProvider({
    url: "https://external-server.com/wms",
    layers: "basic",
    proxy: new Cesium.DefaultProxy("/proxy/")
});

Серверная реализация прокси

Node.js + Express

Наиболее распространённый вариант — простой HTTP-прокси на Node.js.

import express from "express";
import fetch from "node-fetch";

const app = express();

app.get("/proxy", async (req, res) => {
    const targetUrl = req.query.url;

    const response = await fetch(targetUrl);
    const buffer = await response.arrayBuffer();

    res.set("Content-Type", response.headers.get("content-type"));
    res.set("Access-Control-Allow-Origin", "*");

    res.send(Buffer.from(buffer));
});

app.listen(3000);

Такой слой позволяет:

  • перехватывать все запросы CesiumJS
  • добавлять заголовки
  • управлять кешированием

Nginx как обратный прокси

Для высоконагруженных систем предпочтителен Nginx:

location /proxy/ {
    proxy_pass $arg_url;
    proxy_set_header Host $host;

    add_header Access-Control-Allow-Origin *;
}

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


Прокси и Cesium ion

Cesium ion использует токенизированный доступ к ресурсам:

  • 3D Tiles
  • terrain
  • imagery assets

При использовании прокси важно учитывать:

  • необходимость передачи Authorization header
  • ограничение на прямые ссылки
  • временные токены доступа

Пример обработки:

fetch("/proxy?url=" + encodeURIComponent(url), {
    headers: {
        Authorization: `Bearer ${token}`
    }
});

Обработка заголовков и безопасность

Прокси-сервер в CesiumJS-экосистеме часто становится точкой управления безопасностью.

Ключевые аспекты:

Фильтрация URL

Без ограничений прокси может стать инструментом SSRF-уязвимости. Поэтому важно ограничивать домены:

const allowedHosts = ["server1.com", "tiles.server2.com"];

function isAllowed(url) {
    const host = new URL(url).hostname;
    return allowedHosts.includes(host);
}

Управление заголовками

CesiumJS чувствителен к:

  • Content-Type
  • Cache-Control
  • Access-Control-Allow-Origin
  • ETag

Некорректная обработка может привести к:

  • повторным загрузкам тайлов
  • отсутствию кеширования
  • артефактам при рендеринге

HTTPS и mixed content

Если CesiumJS работает по HTTPS, все проксируемые ресурсы также должны быть доступны по HTTPS.

Иначе браузер блокирует:

  • изображения тайлов
  • terrain
  • 3D Tiles

Прокси часто используется для принудительного перехода:

HTTP → Proxy → HTTPS

Кеширование в прокси для CesiumJS

Cesium генерирует большое количество повторяющихся запросов:

  • тайлы при панорамировании
  • LOD уровни
  • пересечения камерой границ

Прокси должен учитывать это.

Пример кеширования на уровне Node.js

const cache = new Map();

app.get("/proxy", async (req, res) => {
    const url = req.query.url;

    if (cache.has(url)) {
        return res.send(cache.get(url));
    }

    const response = await fetch(url);
    const buffer = await response.buffer();

    cache.set(url, buffer);

    res.send(buffer);
});

HTTP caching через заголовки

Более масштабируемый вариант:

Cache-Control: public, max-age=86400
ETag: "tile-xyz"

CesiumJS автоматически использует кеш браузера, если сервер корректно выставляет заголовки.


Взаимодействие с Request Scheduler CesiumJS

Cesium использует внутренний планировщик запросов:

  • ограничение параллельных соединений
  • приоритизация тайлов
  • управление загрузкой ресурсов

Прокси влияет на это поведение косвенно:

  • увеличивает latency
  • может менять порядок ответов
  • добавляет дополнительный слой TCP соединений

При неправильной конфигурации возможны:

  • задержки рендеринга
  • «пустые» тайлы при быстром перемещении камеры

Прокси для разных типов данных CesiumJS

WMS / WMTS

Часто требуют проксирования из-за отсутствия CORS:

/proxy?url=https://wms.server.com?service=WMS&request=GetMap

XYZ тайлы

Используются для картографических подложек:

/proxy?url=https://tiles.server.com/{z}/{x}/{y}.png

3D Tiles

Особо чувствительны к задержкам:

  • tileset.json
  • b3dm
  • pnts
  • i3dm

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


GeoJSON и vector data

Для векторных слоёв критична:

  • целостность JSON
  • корректная кодировка UTF-8
  • отсутствие обрезки данных

Типовые ошибки при использовании прокси

Бесконтрольная генерация URL

Некорректная передача URL приводит к:

  • открытию доступа к внутренней сети
  • утечке данных через прокси

Отсутствие кеширования

Каждое движение камеры вызывает повторные запросы, что:

  • перегружает backend
  • увеличивает latency
  • ухудшает FPS сцены

Неправильные заголовки ответа

Особенно критично:

  • отсутствие Content-Type
  • отсутствие Content-Encoding
  • некорректный Access-Control-Allow-Origin

Оптимизированная схема прокси для CesiumJS

Типовая промышленная архитектура:

CesiumJS
   ↓
Edge Proxy (Nginx / CDN)
   ↓
Application Proxy (Node.js)
   ↓
Tile Server / WMS / Cesium ion

Функции уровней:

  • Edge: кеширование и TLS
  • Application: логика авторизации и маршрутизация
  • Backend: генерация данных

Интеграция прокси с кастомными провайдерами CesiumJS

При создании собственного провайдера:

class CustomProvider extends Cesium.ImageryProvider {
    constructor(options) {
        this.proxy = options.proxy;
    }

    requestImage(x, y, level) {
        const url = this.buildUrl(x, y, level);
        const proxied = this.proxy.getURL(url);

        return Cesium.Resource.fetchImage({ url: proxied });
    }
}

Прокси становится частью цепочки загрузки данных.


Масштабирование и производительность

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

Основные методы оптимизации:

  • горизонтальное масштабирование (stateless proxy)
  • CDN для тайлов
  • HTTP/2 multiplexing
  • gzip / brotli сжатие
  • агрессивное кеширование

CesiumJS при этом продолжает работать как клиентский рендерер, не изменяя внутреннюю логику загрузки, но выигрывая от ускорения сетевого слоя.