Switch to ESRI World_Street_Map via local CORS proxy

Carto Voyager was returning 'API Key required' content for the
user (likely rate-limited or IP-blocked by Carto). Switched to
ESRI World_Street_Map which has CORS-friendly tiles with plenty
of place names and street labels.

URL format is different from Carto: {z}/{y}/{x}.jpg (y before x,
jpg not png). Updated the build to match. The local /esri/
proxy in default.conf handles the upstream CORS-incompatibility
by adding permissive CORS headers and a proper User-Agent.
This commit is contained in:
Richard Zink committed 2026-10-08 09:17:15 +02:00
1 parent e0c6b685ad
commit 4500fe3214
2 files changed
+22 -7

No files matched your search

+18 -4
View File
@@ -8,10 +8,8 @@ server {
try_files $uri $uri/ =404; try_files $uri $uri/ =404;
} }
# CORS proxy for map tiles. Forwards to the upstream tile server # CORS proxy for Carto basemaps (place names, OSM-derived).
# but injects permissive CORS headers so the browser can load # URL format: /carto/voyager/{z}/{x}/{y}.png
# tiles via cross-origin img.src requests. The 'resolver' is
# required by the proxy module to resolve the upstream hostname.
location ^~ /carto/ { location ^~ /carto/ {
resolver 1.1.1.1 8.8.8.8 ipv6=off valid=300s; resolver 1.1.1.1 8.8.8.8 ipv6=off valid=300s;
proxy_pass https://a.basemaps.cartocdn.com/; proxy_pass https://a.basemaps.cartocdn.com/;
@@ -23,6 +21,22 @@ server {
add_header Access-Control-Max-Age 86400 always; add_header Access-Control-Max-Age 86400 always;
} }
# CORS proxy for ESRI World_Street_Map. URL format:
# /esri/wsm/{z}/{y}/{x}.jpg -> ESRI /ArcGIS/.../World_Street_Map/.../tile/{z}/{y}/{x}
location ^~ /esri/ {
resolver 1.1.1.1 8.8.8.8 ipv6=off valid=300s;
rewrite ^/esri/streets/(.*)$ /ArcGIS/rest/services/World_Street_Map/MapServer/tile/$1 break;
rewrite ^/esri/topo/(.*)$ /ArcGIS/rest/services/World_Topo_Map/MapServer/tile/$1 break;
rewrite ^/esri/imagery/(.*)$ /ArcGIS/rest/services/World_Imagery/MapServer/tile/$1 break;
proxy_pass https://server.arcgisonline.com;
proxy_ssl_server_name on;
proxy_set_header Host server.arcgisonline.com;
proxy_set_header User-Agent "Mozilla/5.0 (compatible; tile-proxy)";
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
add_header Access-Control-Max-Age 86400 always;
}
# CORS preflight handler # CORS preflight handler
location ~* \.(png|jpg)$ { location ~* \.(png|jpg)$ {
if ($request_method = 'OPTIONS') { if ($request_method = 'OPTIONS') {
+4 -3
View File
@@ -284,7 +284,7 @@ function init(canvas) {
// mosaic centre always corresponds to UV (0.5, 0.5). // mosaic centre always corresponds to UV (0.5, 0.5).
// Tile source: Carto basemaps Voyager style (with place names). // Tile source: Carto basemaps Voyager style (with place names).
// Free, CORS-enabled, OSM-derived. URL format {z}/{x}/{y}.png. // Free, CORS-enabled, OSM-derived. URL format {z}/{x}/{y}.png.
var GROUND_TILE_URL = '/carto/voyager/'; var GROUND_TILE_URL = '/esri/streets/';
var GROUND_TILE_Z = 11; var GROUND_TILE_Z = 11;
var GROUND_TILE_SIZE = 256; var GROUND_TILE_SIZE = 256;
// Fetch direction: which way the tile corridor is oriented. // Fetch direction: which way the tile corridor is oriented.
@@ -318,8 +318,9 @@ function fetchTileImage(tileX, tileY) {
img.crossOrigin = 'anonymous'; img.crossOrigin = 'anonymous';
img.onload = function() { resolve(img); }; img.onload = function() { resolve(img); };
img.onerror = function() { reject(new Error('tile fetch failed')); }; img.onerror = function() { reject(new Error('tile fetch failed')); };
// Carto/OSM-style format: {z}/{x}/{y}.png via local proxy // ESRI World_Street_Map format: {z}/{y}/{x}.jpg via local
img.src = GROUND_TILE_URL + GROUND_TILE_Z + '/' + tileX + '/' + tileY + '.png'; // proxy. (y before x, jpg not png)
img.src = GROUND_TILE_URL + GROUND_TILE_Z + '/' + tileY + '/' + tileX + '.jpg';
}); });
} }