CSP headers

Современные браузерные карты, построенные на Mapbox GL JS, активно используют динамическую загрузку ресурсов: тайлы, шрифты, спрайты, стили, Web Workers и WebGL-шейдеры. Эти механизмы вступают в прямое взаимодействие с политиками безопасности браузера, прежде всего с Content Security Policy (CSP). При строгой конфигурации CSP большинство стандартных настроек Mapbox GL JS перестают работать без явной адаптации.


Базовые причины конфликтов CSP и Mapbox GL JS

Mapbox GL JS опирается на несколько типов ресурсов, каждый из которых должен быть разрешён отдельными директивами CSP:

  • загрузка JavaScript и Web Workers
  • WebGL шейдеры и динамический код
  • CSS стили (включая inline-injected styles)
  • изображения (тайлы, спрайты, иконки)
  • сетевые запросы к API
  • загрузка шрифтов (glyphs)

Без точной настройки CSP карта либо не отображается, либо частично ломается (пустой canvas, отсутствие подписей, невозможность загрузки тайлов).


Ключевые CSP директивы для Mapbox GL JS

script-src

Mapbox GL JS создаёт Web Workers и может использовать blob URL:

  • script-src должен разрешать:

    • 'self'
    • blob:
    • при необходимости — домен CDN Mapbox

Особое значение имеет blob: — без него Web Workers не запускаются.


worker-src

Mapbox GL JS активно использует Web Workers для рендеринга векторных тайлов:

  • worker-src 'self' blob:

В строгих CSP конфигурациях отсутствие worker-src полностью блокирует инициализацию карты.


style-src

Mapbox динамически добавляет стили (например, для контрольных элементов и canvas overlay):

  • style-src 'self' 'unsafe-inline'

Хотя использование 'unsafe-inline' нежелательно, в Mapbox GL JS оно часто необходимо, если не используется nonce-модель.


img-src

Тайлы, спрайты и иконки загружаются как изображения:

  • img-src 'self' dat a: https:

Особенно важно разрешить https: при использовании сервисов Mapbox или собственных tile-серверов.


connect-src

Все сетевые запросы к API, стилям и тайлам проходят через fetch/XHR:

  • connect-src 'self' https://api.mapbox.com https://events.mapbox.com

При кастомных источниках добавляются собственные домены tile server’ов.


font-src

Шрифты (glyphs) загружаются отдельно:

  • font-src 'self' https://api.mapbox.com

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


Минимальная CSP конфигурация для Mapbox GL JS

Пример базовой рабочей политики:

Content-Security-Policy:
  default-src 'self';
  script-src 'self' blob: https://api.mapbox.com;
  worker-src 'self' blob:;
  connect-src 'self' https://api.mapbox.com https://events.mapbox.com;
  img-src 'self' dat a: https:;
  style-src 'self' 'unsafe-inline';
  font-src 'self' https://api.mapbox.com;

Web Workers и blob URL в Mapbox GL JS

Внутренний рендеринг Mapbox GL JS вынесен в отдельные потоки через:

  • new Worker(blobURL)
  • динамическую генерацию скриптов

Это приводит к обязательному требованию:

  • worker-src blob:
  • script-src blob:

Без этих разрешений карта часто остаётся в состоянии Initializing или показывает пустой canvas.


Inline стили и проблема безопасного режима

Mapbox GL JS добавляет DOM-элементы управления (zoom, attribution, compass). Эти элементы часто создаются с inline-стилями.

При строгом CSP:

  • без 'unsafe-inline' элементы управления могут не отображаться
  • либо требуется использование nonce:
style-src 'self' 'nonce-abc123';

И соответствующая разметка:

<style nonce="abc123">

Использование nonce вместо unsafe-inline

Более безопасная модель CSP:

  • style-src 'self' 'nonce-XYZ'
  • script-src 'self' 'nonce-XYZ'

Проблема: Mapbox GL JS не полностью совместим с nonce-моделью для всех внутренних стилей, поэтому требуется тестирование.


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

Mapbox GL JS загружает векторные тайлы по схеме:

  • https://api.mapbox.com/v4/...
  • или кастомные endpoints

Для корректной работы необходимо:

  • connect-src для API
  • img-src для растровых тайлов
  • поддержка https: для CDN

transformRequest как обход CSP ограничений

Mapbox GL JS предоставляет механизм перехвата запросов:

const map = new mapboxgl.Map({
  container: 'map',
  style: 'mapbox://styles/mapbox/streets-v12',
  transformRequest: (url, resourceType) => {
    return {
      url,
      headers: {
        'X-Custom-Header': 'value'
      }
    };
  }
});

Используется для:

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

CSP и локальные прокси Mapbox API

При жёстких корпоративных политиках доступ к доменам Mapbox блокируется. Тогда применяется проксирование:

  • все запросы идут на /mapbox/*
  • backend перенаправляет на API

CSP упрощается до:

connect-src 'self';
img-src 'self' dat a:;

Частые ошибки при неправильном CSP

Карта не отображается (пустой canvas)

Причины:

  • отсутствует worker-src blob:
  • заблокирован WebGL worker

Нет тайлов

Причины:

  • отсутствует connect-src api.mapbox.com
  • блокировка CDN

Нет подписей

Причины:

  • заблокирован font-src
  • не загружаются glyphs

Интерфейс отображается, но без стилей

Причины:

  • отсутствует style-src 'unsafe-inline'

CSP в development-сборках (Webpack/Vite)

В dev-режиме часто появляются дополнительные источники:

  • localhost:*
  • ws://localhost:* (HMR)

Пример расширенной политики:

connect-src 'self' https://api.mapbox.com ws://localhost:3000;
script-src 'self' blob: http://localhost:3000;

WebGL и CSP косвенные ограничения

Хотя WebGL напрямую не контролируется CSP, он зависит от:

  • загрузки шейдеров через JS
  • inline script execution
  • worker execution

Поэтому строгий CSP без script-src 'unsafe-eval' может ломать рендеринг в редких конфигурациях сборки.


Итоговая структура безопасной конфигурации

Для продакшн-сценариев с Mapbox GL JS обычно требуется баланс между безопасностью и функциональностью:

  • разрешение blob: для workers
  • явный connect-src к API и CDN
  • ограниченный img-src с HTTPS
  • контролируемый style-src
  • минимизация unsafe-inline через nonce (по возможности)
  • использование прокси для корпоративных окружений

Архитектурные последствия строгого CSP

Жёсткие политики CSP фактически влияют на архитектуру приложения:

  • вынесение API-запросов в backend proxy
  • отключение inline-стилей Mapbox UI
  • отказ от CDN в пользу self-hosted tiles
  • контроль Web Worker окружения
  • ограничение динамической генерации кода

Такая конфигурация требует точного понимания потоков данных внутри Mapbox GL JS и всех типов ресурсов, которые браузер блокирует на уровне политики безопасности.