Content Security Policy
If your site sends a Content-Security-Policy header, the browser blocks
everything the policy does not allow, including Kaleidr's scripts, frames and
requests. This page lists what to allow for each product. If your site sends
no such header, you do not need this page.
How much you must allow depends on where the product runs:
| Product | Runs | What your policy must allow |
|---|---|---|
| Viewer | In an iframe that Kaleidr hosts | The loader script and the frame. |
| Tile | In an iframe that Kaleidr hosts | The loader script, the session request and the frame. |
| Chat | In your page | The loader script, requests to the API, injected styles, embedded fonts and images. |
| Editor | In your page, with its own map | All of the above, plus the basemap host, map workers and map images. |
For the two iframe products, the map and everything it loads are governed by Kaleidr's own policy inside the frame, not by yours. That is why their lists are short.
The hosts
| Host | What it serves | Used by |
|---|---|---|
https://cdn.kaleidr.com | kaleidr.js and the product bundles it loads (chat.js, viewer.js, editor.js, tile.js). | Every product, when you use the SDK. |
https://api.kaleidr.com | The session exchange, and the requests the chat makes while it answers. | Chat, Editor, Tile. |
https://map-embed.kaleidr.com | The page that renders inside the Viewer and Tile iframe. | Viewer, Tile. |
https://tile.kaleidr.com | The basemap that the Editor's in-page map draws: style, tiles, sprites and fonts. | Editor. |
You do not need to allow Mapbox, MapTiler or OpenStreetMap hosts for any Kaleidr product. Earlier versions of this page listed them, and they can be removed. If your page runs its own map, that map's hosts are still yours to allow.
Viewer
With the SDK element or Kaleidr.mount:
script-src https://cdn.kaleidr.com;
frame-src https://map-embed.kaleidr.com;
With the plain iframe snippet from Studio, there is no script:
frame-src https://map-embed.kaleidr.com;
A viewer needs no connect-src entry, because a viewer mounted without a key
makes no request from your page.
Tile
script-src https://cdn.kaleidr.com;
connect-src https://api.kaleidr.com;
frame-src https://map-embed.kaleidr.com;
connect-src is for the session exchange that the SDK performs with your
publishable key before it creates the frame.
Chat
script-src https://cdn.kaleidr.com;
connect-src https://api.kaleidr.com;
style-src 'unsafe-inline';
font-src data:;
img-src data: https:;
| Directive | Why |
|---|---|
script-src https://cdn.kaleidr.com | The loader and chat.js. |
connect-src https://api.kaleidr.com | The session exchange and every question and answer. |
style-src 'unsafe-inline' | The chat adds one <style> element to your document's head. It carries no nonce. |
font-src data: | The chat's fonts are embedded in its bundle as data: URLs. |
img-src data: | The chat's icons are embedded as data: URLs. |
img-src https: | Place cards show photos. They load from image hosts that vary from place to place, so they cannot be listed here. With a narrower img-src, photos from hosts you have not allowed do not load. |
img-src https: (site icons) | Links in an answer, and a place's website on a place card, show the site's icon. It loads from Google's favicon service at https://www.google.com, which redirects to https://t0.gstatic.com through https://t3.gstatic.com. A narrower img-src needs https://www.google.com and https://*.gstatic.com, or the icons are missing and the links still work. |
The chat bundle contains no map library and does not need
'wasm-unsafe-eval'. It draws on the map your page already has, so keep
whatever your own map library requires: its script host, its tile and style
hosts in connect-src and img-src, and worker-src blob: for Mapbox GL JS
and MapLibre GL JS.
When the chat drives a Kaleidr viewer or tile element instead of your own
map, add that product's lines as well, which means
frame-src https://map-embed.kaleidr.com.
Editor
script-src 'self' https://cdn.kaleidr.com;
style-src 'self' 'unsafe-inline';
connect-src 'self' https://api.kaleidr.com https://tile.kaleidr.com;
img-src 'self' data: blob: https://tile.kaleidr.com;
worker-src blob:;
font-src 'self' data:;
| Directive | Why |
|---|---|
script-src https://cdn.kaleidr.com | The loader and editor.js. |
style-src 'unsafe-inline' | The editor adds its stylesheets to the document from JavaScript, without a nonce. |
connect-src https://api.kaleidr.com | The session exchange. |
connect-src https://tile.kaleidr.com | The basemap's style, tiles, sprites and label fonts, which the editor's map fetches from your page. |
img-src data: blob: https://tile.kaleidr.com | Map imagery and icons. The map creates images from blob: and data: URLs. |
worker-src blob: | The map runs its workers from blob: URLs. |
font-src data: | The editor's interface fonts are embedded as data: URLs. |
'wasm-unsafe-eval' is not part of the list. Add it to script-src only if
the console reports that your policy blocked a WebAssembly compile from
editor.js, which is the editor's 3D model decoder.
If you pass a full style URL as styleId, also allow that style's own hosts
in connect-src and img-src.
Several products on one page
Add up the lists. A page with a tile map and a chat panel that drives it needs:
script-src https://cdn.kaleidr.com;
connect-src https://api.kaleidr.com;
frame-src https://map-embed.kaleidr.com;
style-src 'unsafe-inline';
font-src data:;
img-src data: https:;
A complete policy
The blocks above show what Kaleidr adds. A real policy also allows your own resources. This is a complete header for a site that serves everything else from its own origin and embeds a viewer and a tile map:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.kaleidr.com; connect-src 'self' https://api.kaleidr.com; frame-src https://map-embed.kaleidr.com; style-src 'self' 'unsafe-inline'
style-src 'unsafe-inline' is in this example for a reason that has nothing
to do with Kaleidr's code: the snippets in these docs size the element with
an inline style="display:block; height:480px" attribute, and a policy
without 'unsafe-inline' ignores inline style attributes. If you want to
keep inline styles blocked, size the element from your stylesheet:
kaleidr-map { display: block; height: 480px; }
The size the SDK gives its iframe is not affected. It is set from script,
which a style policy does not block. The element's own display:block
default is not affected either: it is not delivered as a <style> tag or a
style attribute, so it needs no 'unsafe-inline'.
Nonces, hashes and strict-dynamic
- Scripts. The loader adds each product bundle as a
<script src>element. A host-basedscript-src(as above) allows it. If your policy uses a nonce with'strict-dynamic', put your nonce on the loader tag. The scripts it adds are then trusted through'strict-dynamic'and the host entry is not needed. - Styles. The SDK cannot be given a nonce for the styles that Chat and
Editor inject, and their content changes with releases, so a hash cannot be
pinned either. Those two products need
'unsafe-inline'instyle-src. Viewer and Tile inject no styles into your page. - Integrity. Do not add an
integrityattribute to the loader tag. The file is updated in place. See CDN versioning.
What a blocked resource looks like
Every block is reported in the browser console with the directive that caused it. Match it against this table.
| Symptom | Console names | Add |
|---|---|---|
Nothing mounts. Kaleidr is not defined. | script-src | https://cdn.kaleidr.com to script-src. |
The element stays empty. Console: [kaleidr:<product>] [kaleidr] failed to load …/embed/v1/<product>.js | script-src | The same. The loader was allowed (for example by a hash) but the bundle it loads was not. |
| Viewer or Tile shows an empty box. | frame-src (or child-src, or default-src) | https://map-embed.kaleidr.com to frame-src. |
Tile, Chat or Editor does not start. The sessions request is blocked. | connect-src | https://api.kaleidr.com to connect-src. |
| The chat panel appears unstyled. | style-src | 'unsafe-inline' to style-src. |
| The chat's icons or fonts are missing. | img-src, font-src | data: to both. |
| Place photos, or the icons beside links, are missing in the chat. | img-src | https: to img-src, or the hosts the console names. |
| The editor's panels appear but its map is blank. | connect-src, worker-src or img-src | https://tile.kaleidr.com to connect-src and img-src, blob: to worker-src and img-src. |
| The editor appears unstyled. | style-src | 'unsafe-inline' to style-src. |
The element ignores the height in its style attribute. | style-src | Nothing. Size it from a stylesheet. |
Not every failure is a policy problem. If the sessions request is sent and
comes back with status 401, 403 or 429, the policy allowed it and the API
refused it. See Errors and
CORS and allowed origins.
Roll out with report-only
To add Kaleidr to a strict policy without breaking a production page:
- Send the candidate policy as
Content-Security-Policy-Report-Only, alongside your current one, with a reporting endpoint you own. - Exercise the embed: load the page, move the map, and for chat ask a few questions that return places with photos.
- Add the sources that appear in the reports to the directive each report names. Place photos are the usual source of new hosts.
- When the reports are quiet, move the policy to
Content-Security-Policy.
What you do not need
- Mapbox, MapTiler or OpenStreetMap hosts, for any Kaleidr product.
'unsafe-inline'inscript-src. The loader adds product bundles as<script src>elements, never as inline scripts.frame-ancestors. That directive controls who may frame your page, not what your page may frame.- Any entry for what happens inside the Viewer or Tile iframe.