Современные браузеры применяют модель Cross-Origin Resource Sharing (CORS) для контроля доступа к ресурсам между различными доменами. В контексте веб-картографии это критически важный механизм, поскольку тайлы, GeoJSON-данные, стили и растровые изображения часто подгружаются с разных серверов.
При отсутствии корректных CORS-заголовков браузер блокирует такие запросы, даже если сервер возвращает корректные данные. Это проявляется как пустые слои карты, ошибки загрузки тайлов или отказ в отрисовке изображений на canvas.
Основные ограничения формируются политикой Same-Origin Policy, где протокол, домен и порт должны совпадать для беспрепятственного доступа к ресурсам.
Библиотека Leaflet сама по себе не выполняет серверные запросы
напрямую — она делегирует их браузеру через XMLHttpRequest,
fetch или загрузку ресурсов через <img>
и <script>. Это означает, что все ограничения CORS
применяются на уровне браузера, а не Leaflet.
Важные источники кросс-доменных запросов в Leaflet:
TileLayer)fetch или
XMLHttpRequestImageOverlay)L.tileLayer.wmsНаиболее частый источник 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">
Это позволяет:
Без этого атрибута браузер блокирует доступ к пикселям изображения после отрисовки.
Сервер тайлов обязан возвращать заголовки:
Access-Control-Allow-Origin: *
или более строго:
Access-Control-Allow-Origin: https://example.com
Без этого заголовка crossOrigin: true не даст эффекта,
так как браузер отклонит ответ.
При загрузке 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.
L.imageOverlay загружает изображения как
<img>, и при включённом canvas-рендеринге (например,
при экспорте карты) действует ограничение tainted canvas.
L.imageOverlay('https://server.com/image.png', bounds, {
crossOrigin: true
}).addTo(map);
Если crossOrigin не задан, попытка:
map.getContainer().toDataURL();
приведёт к ошибке безопасности.
Иконки маркеров также загружаются как изображения:
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-слои (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"
Canvas становится «загрязнённым», если в него попадает изображение без CORS-разрешения. После этого запрещены операции:
toDataURL()getImageData()Leaflet сам не очищает это состояние. Единственный способ —
корректная загрузка ресурсов с crossOrigin.
Иногда требуется передача 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-плагинов Leaflet (например, рендеринг больших датасетов) CORS влияет на загрузку текстур. Любое изображение без корректных заголовков блокирует контекст WebGL аналогично canvas.
CORS-заголовки не должны конфликтовать с кэшированием:
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=86400
Неправильная комбинация может привести к:
При нарушении CORS появляются ошибки:
No 'Access-Control-Allow-Origin' header is presentCORS policy blocked the requestCanvas has been tainted by cross-origin dataВ Leaflet это выражается как:
Использование CDN для тайлов и изображений требует единообразной политики CORS на всех edge-узлах. Даже один сервер без заголовков ломает весь слой.
Особенно критично при:
crossOriginСовременные версии 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 не генерирует исключений самостоятельно. Вместо этого:
tileerror могут срабатывать