Carto base maps: send the CARTO API key
Since about 28 August 2026 CARTO stamps an "API KEY REQUIRED" watermark into every basemaps.cartocdn.com tile requested without a key. The request still returns HTTP 200, so nothing fails — Carto Light and Carto Dark simply started drawing the watermark over the map. Both layers now build their tile URL through cartoTileUrl(), which appends ?key=. The parameter name matters: api_key= is silently ignored and returns the same watermarked tile as no key at all, so a wrong guess looks right until the map is looked at. Verified by checksum that the URLs the map generates return the clean tiles for both styles. The key comes from VITE_CARTO_API_KEY in .env.local, which is git-ignored, so it is not in the source and rotating it is a configuration change; .env.example documents the variable. If it is missing, a console warning says so rather than leaving only a watermark to explain it. The key is inlined into the built bundle in dist/ and sent with every tile request, so it is visible to anyone using the application. That is expected for a tile key; the exposure is quota, not access. Cached watermarked tiles are not a concern: the Service Worker keys tiles by full URL including the query string, so keyed URLs are fetched fresh and the old entries age out. No Service Worker bump — HTML is network-first, so the new bundle is picked up without one, and a bump would delete every offline tile cache (flagged separately). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
88f55ade46
commit
486480eda6
8
.env.example
Normal file
8
.env.example
Normal file
@ -0,0 +1,8 @@
|
||||
# Copy to .env.local (git-ignored) and fill in. Vite reads VITE_-prefixed
|
||||
# variables at build time and inlines them into the bundle.
|
||||
|
||||
# CARTO basemaps (Carto Light, Carto Dark). Since late August 2026 CARTO stamps
|
||||
# an "API KEY REQUIRED" watermark into every tile requested without a key — the
|
||||
# request still succeeds, so a missing key shows up only as the watermark.
|
||||
# Free key: https://carto.com/basemaps/apikey
|
||||
VITE_CARTO_API_KEY=
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
1
dist/assets/index-BzlrAJOS.js.map
vendored
Normal file
1
dist/assets/index-BzlrAJOS.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/assets/index-D5My3hAw.js.map
vendored
1
dist/assets/index-D5My3hAw.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/index.html
vendored
2
dist/index.html
vendored
@ -1997,7 +1997,7 @@
|
||||
}
|
||||
|
||||
</style>
|
||||
<script type="module" crossorigin src="/assets/index-D5My3hAw.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-BzlrAJOS.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/assets/openlayers-D5-VSKzq.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/pako-Xa-UToif.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/geotiff-B9aROX49.js">
|
||||
|
||||
@ -105,6 +105,29 @@ import '../styles/layerswitcher.css';
|
||||
* and every button is given an icon from here instead, which makes this the single
|
||||
* place the toolbar's iconography is defined.
|
||||
*/
|
||||
/**
|
||||
* CARTO basemap key, used by the Carto Light and Carto Dark base maps.
|
||||
*
|
||||
* Since late August 2026 CARTO stamps an "API KEY REQUIRED" watermark into
|
||||
* every tile requested without a key. The request still returns HTTP 200, so a
|
||||
* missing or mistyped key never shows up as an error — only as the watermark on
|
||||
* the map. The parameter is `key`; `api_key` is silently ignored and produces
|
||||
* the same watermarked tiles.
|
||||
*
|
||||
* Supplied through VITE_CARTO_API_KEY in .env.local (git-ignored, see
|
||||
* .env.example), so rotating it is a configuration change. It is inlined into
|
||||
* the built bundle and sent in every tile URL, so it is visible to anyone using
|
||||
* the application — normal for a tile key, which is why it is kept separate from
|
||||
* credentials that must stay private.
|
||||
*/
|
||||
const CARTO_API_KEY = import.meta.env.VITE_CARTO_API_KEY || '';
|
||||
|
||||
/** Tile URL template for a CARTO raster style, with the key when there is one. */
|
||||
function cartoTileUrl(style) {
|
||||
const url = `https://{a-c}.basemaps.cartocdn.com/${style}/{z}/{x}/{y}.png`;
|
||||
return CARTO_API_KEY ? `${url}?key=${encodeURIComponent(CARTO_API_KEY)}` : url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resting offset of the info / analysis popup above its anchor. Dragging the
|
||||
* popup by its title bar moves it from here; reopening returns it here.
|
||||
@ -3080,13 +3103,20 @@ export class MapView {
|
||||
});
|
||||
topoLayer.set('basemapKey', 'topo');
|
||||
|
||||
if (!CARTO_API_KEY) {
|
||||
console.warn(
|
||||
'[MapView] VITE_CARTO_API_KEY is not set — Carto Light and Carto Dark tiles '
|
||||
+ 'will carry an "API KEY REQUIRED" watermark. Add it to .env.local and rebuild.'
|
||||
);
|
||||
}
|
||||
|
||||
const cartoLightLayer = new TileLayer({
|
||||
title: 'Carto Light',
|
||||
type: 'base',
|
||||
zIndex: -100,
|
||||
visible: defaultBasemap === 'carto-light',
|
||||
source: new XYZ({
|
||||
url: 'https://{a-c}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
|
||||
url: cartoTileUrl('light_all'),
|
||||
attributions: '© CARTO',
|
||||
maxZoom: 19,
|
||||
crossOrigin: 'anonymous',
|
||||
@ -3100,7 +3130,7 @@ export class MapView {
|
||||
zIndex: -100,
|
||||
visible: defaultBasemap === 'carto-dark',
|
||||
source: new XYZ({
|
||||
url: 'https://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
|
||||
url: cartoTileUrl('dark_all'),
|
||||
attributions: '© CARTO',
|
||||
maxZoom: 19,
|
||||
crossOrigin: 'anonymous',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user