CORS

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

При отсутствии корректных CORS-заголовков браузер блокирует такие запросы, даже если сервер возвращает корректные данные. Это проявляется как пустые слои карты, ошибки загрузки тайлов или отказ в отрисовке изображений на canvas.

Основные ограничения формируются политикой Same-Origin Policy, где протокол, домен и порт должны совпадать для беспрепятственного доступа к ресурсам.


CORS в архитектуре Leaflet

Библиотека Leaflet сама по себе не выполняет серверные запросы напрямую — она делегирует их браузеру через XMLHttpRequest, fetch или загрузку ресурсов через <img> и <script>. Это означает, что все ограничения CORS применяются на уровне браузера, а не Leaflet.

Важные источники кросс-доменных запросов в Leaflet:

  • тайлы (TileLayer)
  • GeoJSON через fetch или XMLHttpRequest
  • изображения (ImageOverlay)
  • SVG/Canvas-рендеринг и иконки маркеров
  • WMS/WMTS слои через L.tileLayer.wms

Загрузка тайлов и заголовок crossOrigin

Наиболее частый источник CORS-проблем — слои тайлов. При использовании L.tileLayer браузер загружает изображения напрямую через <img>.

Для корректной работы с canvas (например, при использовании плагинов аналитики или обработки пикселей) необходимо включать режим CORS:

const osm = L.tileLayer('https://tile-server.com/{z}/{x}/{y}.png', {
    crossOrigin: true
});

Параметр crossOrigin добавляет атрибут crossorigin к тегу изображения:

<img src="..." crossorigin="anonymous">

Это позволяет:

  • использовать тайлы в canvas без tainted state
  • выполнять пиксельную обработку
  • экспортировать карту в изображение

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


Условия корректной работы CORS для тайлов

Сервер тайлов обязан возвращать заголовки:

Access-Control-Allow-Origin: *

или более строго:

Access-Control-Allow-Origin: https://example.com

Без этого заголовка crossOrigin: true не даст эффекта, так как браузер отклонит ответ.


GeoJSON и CORS

При загрузке GeoJSON через fetch или L.geoJSON с удалённого источника применяются те же правила:

fetch('https://api.example.com/data.geojson')
    .then(r => r.json())
    .then(data => {
        L.geoJSON(data).addTo(map);
    });

Если сервер не поддерживает CORS, запрос завершится ошибкой, а данные не будут доступны.

При использовании L.GeoJSON.ajax (плагины) поведение аналогичное — используется XHR.


ImageOverlay и ограничения canvas

L.imageOverlay загружает изображения как <img>, и при включённом canvas-рендеринге (например, при экспорте карты) действует ограничение tainted canvas.

L.imageOverlay('https://server.com/image.png', bounds, {
    crossOrigin: true
}).addTo(map);

Если crossOrigin не задан, попытка:

map.getContainer().toDataURL();

приведёт к ошибке безопасности.


Marker icons и CORS

Иконки маркеров также загружаются как изображения:

const icon = L.icon({
    iconUrl: 'https://cdn.example.com/marker.png',
    iconSize: [25, 41]
});

Если изображение используется в canvas-операциях (кластеризация, экспорт), требуется:

L.icon({
    iconUrl: 'https://cdn.example.com/marker.png',
    crossOrigin: true
});

Особенно важно при использовании leaflet-image, html2canvas и подобных библиотек.


WMS и серверные требования

WMS-слои (L.tileLayer.wms) используют HTTP-запросы через XHR, что делает CORS обязательным условием:

L.tileLayer.wms('https://geoserver.example.com/wms', {
    layers: 'layer_name',
    format: 'image/png',
    transparent: true
});

Сервер GeoServer или аналог должен быть настроен на выдачу заголовков CORS.

Типичная конфигурация Apache:

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, OPTIONS"

Проблема «tainted canvas»

Canvas становится «загрязнённым», если в него попадает изображение без CORS-разрешения. После этого запрещены операции:

  • toDataURL()
  • getImageData()
  • экспорт в PNG/JPEG

Leaflet сам не очищает это состояние. Единственный способ — корректная загрузка ресурсов с crossOrigin.


Credentials и авторизация

Иногда требуется передача cookies или токенов:

fetch('https://api.example.com/data', {
    credentials: 'include'
});

Однако в Leaflet стандартные слои не всегда позволяют управлять credentials. В таких случаях используется серверный прокси.

Важно учитывать:

  • Access-Control-Allow-Credentials: true
  • нельзя использовать * в Access-Control-Allow-Origin

Прокси-архитектура для обхода ограничений

Если сервер не поддерживает CORS, применяется проксирование:

Client → Proxy Server → Tile Server

Пример Node.js прокси:

app.get('/tiles/:z/:x/:y.png', async (req, res) => {
    const url = `https://tiles.example.com/${req.params.z}/${req.params.x}/${req.params.y}.png`;

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

    res.set('Content-Type', 'image/png');
    res.send(Buffer.from(buffer));
});

Leaflet обращается уже к своему домену:

L.tileLayer('/tiles/{z}/{x}/{y}.png').addTo(map);

WebGL и расширенные случаи

При использовании WebGL-плагинов Leaflet (например, рендеринг больших датасетов) CORS влияет на загрузку текстур. Любое изображение без корректных заголовков блокирует контекст WebGL аналогично canvas.


Кэширование и CORS

CORS-заголовки не должны конфликтовать с кэшированием:

Access-Control-Allow-Origin: *
Cache-Control: public, max-age=86400

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

  • повторным загрузкам тайлов
  • неконсистентному поведению слоёв
  • блокировке обновлений данных

Типичные ошибки браузера

При нарушении CORS появляются ошибки:

  • No 'Access-Control-Allow-Origin' header is present
  • CORS policy blocked the request
  • Canvas has been tainted by cross-origin data

В Leaflet это выражается как:

  • пустые тайлы
  • серые квадраты вместо изображений
  • отсутствие GeoJSON-слоёв
  • невозможность экспорта карты

Влияние CDN и распределённых источников

Использование CDN для тайлов и изображений требует единообразной политики CORS на всех edge-узлах. Даже один сервер без заголовков ломает весь слой.

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

  • динамической генерации тайлов
  • региональных CDN
  • кеширующих прокси уровня CloudFront / Cloudflare

Поведение разных типов ресурсов

  • PNG/JPEG тайлы — требуют crossOrigin
  • SVG — чаще безопасны, но зависят от inline-структуры
  • GeoJSON — всегда через XHR, требует CORS
  • WMTS/WMS — серверная настройка обязательна
  • Canvas-слои — чувствительны к любым cross-origin изображениям

Совместимость с браузерами

Современные версии Chrome, Firefox, Edge строго соблюдают CORS. Safari дополнительно усиливает ограничения при работе с canvas и приватным режимом, что может усиливать эффекты tainted canvas даже при частично корректных заголовках.


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

Минимально корректная конфигурация для картографических ресурсов:

Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, OPTIONS

Для защищённых API:

Access-Control-Allow-Origin: https://map-client.example.com
Access-Control-Allow-Credentials: true

Поведение Leaflet при отсутствии CORS

Leaflet не генерирует исключений самостоятельно. Вместо этого:

  • запросы завершаются ошибками браузера
  • события tileerror могут срабатывать
  • слои продолжают существовать без данных
  • карта остаётся интерактивной, но визуально неполной