Настройка CORS

Визуализация карт в MapLibre GL JS опирается на загрузку внешних ресурсов: векторных тайлов, растровых изображений, шрифтовых глифов, спрайтов стилей и дополнительных данных. Все эти компоненты загружаются через HTTP(S), и именно здесь возникает фундаментальное ограничение браузеров — политика одного источника (Same-Origin Policy). Для корректной работы карты необходимо корректно настроить CORS (Cross-Origin Resource Sharing), иначе запросы к тайлам и ассетам будут блокироваться на уровне браузера.


Природа CORS в контексте картографического рендеринга

MapLibre GL JS использует WebGL-рендеринг, при котором любые внешние ресурсы становятся частью графического контекста. Это накладывает дополнительные ограничения:

  • изображения и спрайты становятся частью WebGL-текстур;
  • векторные тайлы декодируются в браузере;
  • шрифты используются для генерации текстовых глифов на GPU;
  • любые кросс-доменные ошибки приводят к частичной или полной неработоспособности стиля.

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

  • не отобразить слои;
  • потерять подписи;
  • отрисовать пустой холст;
  • выбросить ошибки WebGL security error.

Основные ресурсы, требующие CORS

MapLibre GL JS взаимодействует с несколькими типами внешних данных:

Векторные тайлы

  • форматы .pbf, .mvt
  • основной источник геометрии

Растровые тайлы

  • изображения .png, .jpg
  • используются как базовые слои

Sprite atlas

  • sprite.png
  • sprite.json

Шрифты

  • .pbf глифы в формате PBF

Style JSON

  • описание стиля, включающее ссылки на все перечисленные ресурсы

Каждый из этих ресурсов загружается через fetch или XHR, и на каждый распространяется CORS-политика.


Требования браузера к 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 при загрузке ресурсов

MapLibre GL JS использует внутренний механизм transformRequest, через который проходят все сетевые запросы. Именно здесь можно централизованно управлять CORS-параметрами.

Стандартное поведение:

  • все URL загружаются через fetch
  • режим запроса — cors
  • заголовки не модифицируются без явной настройки
  • кеширование управляется браузером

Настройка transformRequest

Ключевой механизм контроля запросов:

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

Типичные причины CORS-ошибок

Отсутствие заголовков на сервере

  • сервер не добавляет Access-Control-Allow-Origin
  • CDN не проксирует заголовки

Несовпадение доменов

  • стиль загружается с одного домена
  • тайлы — с другого без разрешений

Redirect без CORS

  • редирект на другой домен без CORS-заголовков

Mixed Content

  • HTTPS-страница запрашивает HTTP-ресурсы

file:// протокол

  • локальное открытие HTML файла блокирует все запросы

Настройка CORS на популярных серверах

Nginx

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

Apache

<Directory "/var/www/tiles">
    Header set Access-Control-Allow-Origin "*"
    Header set Access-Control-Allow-Methods "GET, OPTIONS"
</Directory>

Node.js (Express)

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(...);
});

CORS для vector tiles и WebGL ограничения

Векторные тайлы в MapLibre GL JS обрабатываются через WebGL. При неправильной CORS-конфигурации возникает дополнительная проблема:

  • WebGL контекст блокирует использование текстур
  • canvas становится “tainted”
  • экспорт изображения через map.getCanvas().toDataURL() перестает работать

Критично, чтобы:

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

Настройка CORS для sprite и glyphs

Спрайты и шрифты часто становятся причиной скрытых ошибок:

sprite.png + sprite.json

  • оба файла должны иметь идентичные CORS-заголовки
  • JSON и PNG должны приходить с одного origin-политики

glyphs (шрифты)

  • загружаются по шаблону URL:

    /fonts/{fontstack}/{range}.pbf
  • сервер должен поддерживать CORS на динамических маршрутах


Локальная разработка и CORS

При разработке чаще всего проблемы возникают из-за локального окружения:

Открытие через file://

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

Решение

  • запуск локального HTTP сервера:

    • vite
    • webpack-dev-server
    • http-server

Типичный dev-сценарий

npx http-server .

или

npm run dev

Прокси как способ обхода CORS

Если сервер не контролируется, используется проксирование:

devServer: {
  proxy: {
    '/tiles': {
      target: 'https://external-tiles.com',
      changeOrigin: true,
      secure: false
    }
  }
}

Преимущество подхода:

  • браузер видит один origin
  • CORS становится не нужен

CORS и MapTiler / TileServer GL

При использовании готовых решений:

  • TileServer GL по умолчанию не всегда включает CORS
  • MapTiler Cloud требует токен и корректные headers
  • self-hosted решения требуют ручной настройки nginx или node middleware

Ошибки WebGL, связанные с CORS

Некорректный CORS часто проявляется неявно:

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

Типичная WebGL ошибка:

SecurityError: The operation is insecure

Безопасные стратегии конфигурации

В production окружениях применяются следующие подходы:

  • строгий список разрешённых origin вместо *
  • разделение тайлового сервера и frontend домена
  • CDN с корректной проксирующей конфигурацией
  • единый домен через reverse proxy

Роль заголовков кеширования в CORS-сценариях

CORS тесно связан с кешированием:

Cache-Control: public, max-age=86400

При неправильной комбинации:

  • браузер кеширует ответ без CORS-заголовков
  • последующие запросы падают
  • поведение становится нестабильным

Итоговая архитектура корректной CORS-настройки

Корректная конфигурация MapLibre GL JS обычно включает:

  • единый или разрешённый cross-origin источник тайлов
  • одинаковые CORS-заголовки для всех ассетов стиля
  • transformRequest для тонкой настройки запросов
  • серверную поддержку OPTIONS preflight
  • отсутствие смешивания HTTP и HTTPS
  • корректную работу CDN или прокси-слоя