Security and Privacy
Applications
Protomaps is designed for simple, secure and privacy-enabled map publishing, and is especially suited for:
Maps for humanitarian operations, adversarial environments or emergency services. Protomaps enables maps to work 100% offline, meaning there's less risk of data leaks or compromise.
Public sector: Protomaps can power mapping applications in city government, using storage and servers already provisioned. It ensures user data is not transmitted to third party map APIs, and can enable compliance in the European Union.
Checklist
Storage buckets
Verify the access level of your storage buckets.
Hosting PMTiles from a public storage bucket and decoding via pmtiles.js is the simplest publishing method, but allows anyone to download your entire tileset. To limit access, use one of the deployment options for decoding on the server or in a serverless function.
HTTPS
Ensure that you access your maps over HTTPS instead of plain HTTP.
Using HTTPS is also required for HTTP/2 and 3, which will make map viewing faster by enabling more requests at a time compared to HTTP 1.1.
CORS
Cross-Origin Resource Sharing limits the sites that are allowed to embed your hosted resources, such as PMTiles archives, ZXY tile endpoints and TileJSON.
Check the Cloud Storage docs for your platform for how to configure CORS.
Avoid the *
wildcard value for Access-Control-Allow-Origin
for production traffic.
Map Resources
Even if your PMTiles archives or tile endpoints come from your own infrastructure, other resources on a web map may come from an external origin. These include:
Map rendering library JavaScript.
Map rendering library CSS Stylesheets.
For MapLibre GL: Map style JSON, spritesheets, fontstacks, and RTL (right-to-left) text plugins. See Example Application below.
Use Subresource Integrity to ensure that libraries from third parties are not compromised. Example:
<script
src="https://unpkg.com/pmtiles@3.0.7/dist/pmtiles.js"
integrity="sha384-MjejsnWXHmuz93aE35YWLh5AbS/6ceRB3Vb+ukOwqFzJRTpQ8vvbkLbNV7I0QK4f"
crossorigin="anonymous"
/>
<script
src="https://unpkg.com/pmtiles@3.0.7/dist/pmtiles.js"
integrity="sha384-MjejsnWXHmuz93aE35YWLh5AbS/6ceRB3Vb+ukOwqFzJRTpQ8vvbkLbNV7I0QK4f"
crossorigin="anonymous"
/>
Content Security Policy
Setting a Content Security Policy via HTTP header or HTML meta
tag can enforce all page resources, like tiles, come from the same origin.
An example HTML CSP policy that includes MapLibre's required CSP directives:
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self' 'nonce-n0nce' 'nonce-n0nce1'; worker-src blob: ; child-src blob: ; img-src data: blob: ;" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self' 'nonce-n0nce' 'nonce-n0nce1'; worker-src blob: ; child-src blob: ; img-src data: blob: ;" />
GDPR
INFO
This is not a substitute for legal advice.
The European Union's General Data Protection Regulation (GDPR) regulates how companies store and transmit personal data.
Hosting Protomaps for your web map can eliminate third party data controllers and processors, making it easier for sites to comply with GDPR.
Hosting PMTiles via your existing cloud storage or server is a first step - a typical map application has many other components.
Example Application
Below is a complete example of a map application that avoids third-party data processors. As long as all linked assets are on your own GDPR-compliant static storage, no third party data processors or controllers are required.
<html>
<head>
<meta charset="utf-8"/>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self' 'nonce-n0nce' 'nonce-n0nce1'; worker-src blob: ; child-src blob: ; img-src data: blob: ;" />
<link rel="stylesheet" href="maplibre-gl.css">
<script src="maplibre-gl.js"></script>
<script src="pmtiles.js"></script>
<script src="protomaps-themes-base.js"></script>
</head>
<body>
<div id="map" style="height: 100%; width: 100%"></div>
<script type="text/javascript">
let protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);
maplibregl.setRTLTextPlugin(
"mapbox-gl-rtl-text.min.js",
true,
);
const map = new maplibregl.Map({
container: "map",
zoom: 12,
center: [11.24962,43.77078],
style: {
glyphs: "fonts/{fontstack}/{range}.pbf",
sprite: "sprites/v4/light",
version: 8,
sources: {
protomaps: {
type: "vector",
url: "pmtiles://firenze.pmtiles",
attribution: '© <a href="https://openstreetmap.org">OpenStreetMap</a>'
},
},
layers: protomaps_themes_base.default("protomaps", "light")
},
});
</script>
</body>
</html>
<html>
<head>
<meta charset="utf-8"/>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self' 'nonce-n0nce' 'nonce-n0nce1'; worker-src blob: ; child-src blob: ; img-src data: blob: ;" />
<link rel="stylesheet" href="maplibre-gl.css">
<script src="maplibre-gl.js"></script>
<script src="pmtiles.js"></script>
<script src="protomaps-themes-base.js"></script>
</head>
<body>
<div id="map" style="height: 100%; width: 100%"></div>
<script type="text/javascript">
let protocol = new pmtiles.Protocol();
maplibregl.addProtocol("pmtiles", protocol.tile);
maplibregl.setRTLTextPlugin(
"mapbox-gl-rtl-text.min.js",
true,
);
const map = new maplibregl.Map({
container: "map",
zoom: 12,
center: [11.24962,43.77078],
style: {
glyphs: "fonts/{fontstack}/{range}.pbf",
sprite: "sprites/v4/light",
version: 8,
sources: {
protomaps: {
type: "vector",
url: "pmtiles://firenze.pmtiles",
attribution: '© <a href="https://openstreetmap.org">OpenStreetMap</a>'
},
},
layers: protomaps_themes_base.default("protomaps", "light")
},
});
</script>
</body>
</html>
maplibre-gl.js
,maplibre-gl.css
- JavaScript and CSS for the MapLibre GL rendering library.pmtiles.js
- JavaScript for decoding PMTiles archives in the browser.protomaps-themes-base.js
- JavaScript for creating a MapLibre GL style for a basemap tileset.mapbox-gl-rtl-text.min.js
- MapLibre plugin for supporting right-to-left languages.fonts/{fontstack}/{range}.pbf
- Font glyphs for rendering labels, available at protomaps/basemaps-assets.sprites/{version/{theme}
- Sprites for basemap icons, available at protomaps/basemaps-assets.