Визуализация карт в MapLibre GL JS опирается на загрузку внешних ресурсов: векторных тайлов, растровых изображений, шрифтовых глифов, спрайтов стилей и дополнительных данных. Все эти компоненты загружаются через HTTP(S), и именно здесь возникает фундаментальное ограничение браузеров — политика одного источника (Same-Origin Policy). Для корректной работы карты необходимо корректно настроить CORS (Cross-Origin Resource Sharing), иначе запросы к тайлам и ассетам будут блокироваться на уровне браузера.
MapLibre GL JS использует WebGL-рендеринг, при котором любые внешние ресурсы становятся частью графического контекста. Это накладывает дополнительные ограничения:
Если хотя бы один тип ресурса не проходит CORS-проверку, карта может:
MapLibre GL JS взаимодействует с несколькими типами внешних данных:
Векторные тайлы
.pbf, .mvtРастровые тайлы
.png, .jpgSprite atlas
sprite.pngsprite.jsonШрифты
.pbf глифы в формате PBFStyle JSON
Каждый из этих ресурсов загружается через fetch или XHR,
и на каждый распространяется CORS-политика.
Для корректной загрузки ресурсов сервер должен возвращать минимальный набор заголовков:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Content-Type
В более строгих сценариях:
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true
Ключевой момент: при использовании credentials: true
нельзя использовать * в Allow-Origin.
MapLibre GL JS использует внутренний механизм
transformRequest, через который проходят все сетевые
запросы. Именно здесь можно централизованно управлять
CORS-параметрами.
Стандартное поведение:
fetchcorsКлючевой механизм контроля запросов:
const map = new maplibregl.Map({
container: 'map',
style: 'https://tiles.example.com/style.json',
transformRequest: (url, resourceType) => {
return {
url: url,
mode: 'cors'
};
}
});
Расширенный вариант с авторизацией:
transformRequest: (url, type) => {
if (type === 'Tile' || type === 'Source') {
return {
url: url,
headers: {
'Authorization': 'Bearer TOKEN'
},
mode: 'cors'
};
}
return { url };
}
Отсутствие заголовков на сервере
Access-Control-Allow-OriginНесовпадение доменов
Redirect без CORS
Mixed Content
file:// протокол
location /tiles/ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "*";
if ($request_method = OPTIONS) {
return 204;
}
}
<Directory "/var/www/tiles">
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, OPTIONS"
</Directory>
import express from 'express';
import cors from 'cors';
const app = express();
app.use('/tiles', cors());
app.get('/tiles/:z/:x/:y.pbf', (req, res) => {
res.sendFile(...);
});
Векторные тайлы в MapLibre GL JS обрабатываются через WebGL. При неправильной CORS-конфигурации возникает дополнительная проблема:
map.getCanvas().toDataURL()
перестает работатьКритично, чтобы:
Спрайты и шрифты часто становятся причиной скрытых ошибок:
sprite.png + sprite.json
glyphs (шрифты)
загружаются по шаблону URL:
/fonts/{fontstack}/{range}.pbfсервер должен поддерживать CORS на динамических маршрутах
При разработке чаще всего проблемы возникают из-за локального окружения:
Открытие через file://
Решение
запуск локального HTTP сервера:
vitewebpack-dev-serverhttp-serverТипичный dev-сценарий
npx http-server .
или
npm run dev
Если сервер не контролируется, используется проксирование:
devServer: {
proxy: {
'/tiles': {
target: 'https://external-tiles.com',
changeOrigin: true,
secure: false
}
}
}
Преимущество подхода:
При использовании готовых решений:
Некорректный CORS часто проявляется неявно:
Типичная WebGL ошибка:
SecurityError: The operation is insecure
В production окружениях применяются следующие подходы:
*CORS тесно связан с кешированием:
Cache-Control: public, max-age=86400
При неправильной комбинации:
Корректная конфигурация MapLibre GL JS обычно включает: