Publish

HTTPS

You can easily publish your map service via HTTPS.

HTTPS is also required for HTTP/2, which also speeds up the map display by allowing more requests at once.

CORS

Cross-Origin Resource Sharing restricts the websites that are allowed to embed your hosted resources.

Warning

Avoid wildcards * for Access-Control-Allow-Origin in productive use.

Map resources

Even if your PMTiles archives or tile endpoints originate from your own infrastructure, other resources on a web map may originate from external providers. These include JavaScript libraries, CSS stylesheets and fonts.

Content security policy

By defining a content security policy via an HTTP header or an HTML meta tag, you can ensure that all resources on the HTML page come from the same source, for example for MapLibre:

<meta
  http-equiv="Content-Security-Policy"
  content="default-src 'self' 'nonce-n0nce' 'nonce-n0nce1'; worker-src blob: ; child-src blob: ; img-src data: blob: ;" />

Example

Below you will find a complete example of a map application in which third-party sources are avoided:

 1<html>
 2    <head>
 3        <meta charset="utf-8"/>
 4        <meta
 5          http-equiv="Content-Security-Policy"
 6          content="default-src 'self' 'nonce-n0nce' 'nonce-n0nce1'; worker-src blob: ; child-src blob: ; img-src data: blob: ;" />
 7        <link rel="stylesheet" href="maplibre-gl.css">
 8        <script src="maplibre-gl.js"></script>
 9        <script src="pmtiles.js"></script>
10        <script src="basemaps.js"></script>
11    </head>
12    <body>
13        <div id="map" style="height: 100%; width: 100%"></div>
14        <script type="text/javascript">
15            let protocol = new pmtiles.Protocol();
16            maplibregl.addProtocol("pmtiles", protocol.tile);
17            maplibregl.setRTLTextPlugin(
18              "mapbox-gl-rtl-text.min.js",
19              true,
20            );
21            const map = new maplibregl.Map({
22              container: "map",
23              zoom: 12,
24              center: [13.424233,52.534675],
25              style: {
26                glyphs: "fonts/{fontstack}/{range}.pbf",
27                sprite: "sprites/v4/light",
28                version: 8,
29                sources: {
30                  protomaps: {
31                    type: "vector",
32                    url: "pmtiles://berlin.pmtiles",
33                    attribution: '© <a href="https://openstreetmap.org">OpenStreetMap</a>'
34                  },
35                },
36                layers: basemaps.layers("protomaps", basemaps.namedFlavor("light"), {lang: "en"})
37              },
38            });
39        </script>
40    </body>
41</html>
Lines 7–10

maplibre-gl.js and maplibre-gl.css are JavaScript and CSS for the MapLibre GL rendering library.

pmtiles.js is a JavaScript file for decoding PMTiles archives in the browser.

basemaps.js is a JavaScript file for creating a MapLibre GL style for a base map tile set.

Line 18

mapbox-gl-rtl-text.min. js a MapLibre plugin for supporting languages written from right to left.

Lines 26–27

fonts/fontstack/range.pbf are font glyphs for the display of labels, available under protomaps/basemaps-assets.

sprites/version/{flavor_name are Sprites for basemap icons, available under protomaps/basemaps-assets.

Line 32

pmtiles://berlin.pmtiles is the archive file with the map data.

Cloud storage

PMTiles works on any S3-compatible cloud storage platform that supports HTTP Range Requests and `Cross-Origin Resource Sharing (CORS).

Upload

The pmtiles command line tool has the pmtiles upload command to transfer files to a cloud storage. RClone is another tool for managing large files on S3-compatible storage:

rclone copyto FILENAME CONFIGURATION:BUCKET/FOLDER/FILENAME.pmtiles --progress --s3-chunk-size=256M

Web-Server

Caddy

Caddy is highly recommended for the provision of PMTiles due to its built-in HTTPS support. Uses the file_server configuration to deploy *.pmtiles from a static directory with the following CORS configuration:

header {
  Access-Control-Allow-Methods GET,HEAD
  Access-Control-Expose-Headers ETag
  Access-Control-Allow-Headers Range,If-Match
  Access-Control-Allow-Origin https://www.cusy.io
}

Alternatively, you can use the pmtiles_proxy for Caddy.

nginx

nginx supports HTTP range requests, whereby the CORS headers and support for CORS preflight requests should be set in the configuration file:

if ($request_method = 'OPTIONS') {
  add_header 'Access-Control-Max-Age' 3600;
  add_header 'Content-Type' 'text/plain charset=UTF-8';
  add_header 'Access-Control-Allow-Origin' '*' always;
  add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range' always;
  add_header 'Content-Length' 0;
  return 204;
}