From b4035c02e331ed737a6cbf3bc43dbea003beed29 Mon Sep 17 00:00:00 2001 From: Kresna Date: Sat, 8 Aug 2026 15:27:48 +0700 Subject: [PATCH] fix(maps): bypass CF edge cache for OFM proxy + add tile probe diagnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Worker was fetching OFM tile URLs with cf:{cacheEverything:true}, which caused it to serve corrupted responses cached by Cloudflare's Singapore edge node (HTTP 200 but invalid PBF body). Removing cacheEverything forces the Worker to fetch direct from OFM origin via CF backbone, skipping the stale edge cache entries. Also adds a one-shot tile probe in MapExplorer that logs the status, content- type, size, and first 8 bytes of a sample tile — so the next test immediately reveals whether the response is valid PBF or corrupted data. Co-Authored-By: Claude Sonnet 4.6 --- src/islands/maps/MapExplorer.tsx | 16 ++++++++++++++++ worker/index.js | 8 +++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/islands/maps/MapExplorer.tsx b/src/islands/maps/MapExplorer.tsx index cbab136..556489e 100644 --- a/src/islands/maps/MapExplorer.tsx +++ b/src/islands/maps/MapExplorer.tsx @@ -192,6 +192,22 @@ export default function MapExplorer({ lang = 'en' }: { lang?: Lang }) { // never needs its own TileJSON fetch (avoids stale CDN edge responses). const styleInput = await fetchResolvedStyle(initialUrl); if (cancelled || !containerRef.current) return; + + // Diagnostic: probe one vector tile to verify the proxy returns valid PBF. + // Valid gzip-PBF starts with bytes 1f 8b; raw PBF starts with a protobuf tag. + // HTML/JSON/text responses (corrupted edge cache) start with ASCII bytes. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const omtTiles = (styleInput as any)?.sources?.openmaptiles?.tiles; + if (Array.isArray(omtTiles) && omtTiles[0]) { + const probeUrl = (omtTiles[0] as string).replace('{z}', '5').replace('{x}', '26').replace('{y}', '16'); + fetch(probeUrl).then(async r => { + const buf = await r.arrayBuffer(); + const b = new Uint8Array(buf.slice(0, 8)); + const hex = Array.from(b).map(n => n.toString(16).padStart(2, '0')).join(' '); + console.log('[map] tile probe', probeUrl, '→ status:', r.status, 'ct:', r.headers.get('content-type'), 'size:', buf.byteLength, 'bytes:', hex); + }).catch(e => console.error('[map] tile probe error:', e)); + } + // Rewrite every OpenFreeMap URL that MapLibre would fetch on its own // (TileJSON, vector/raster tiles, glyphs, sprites) through our /ofm/ proxy. // This is the definitive safety net: even if fetchResolvedStyle leaves a diff --git a/worker/index.js b/worker/index.js index 38a5df5..5b93e6c 100644 --- a/worker/index.js +++ b/worker/index.js @@ -64,9 +64,15 @@ export default { return new Response('Method not allowed', { status: 405 }); } const upstream = 'https://tiles.openfreemap.org/' + url.pathname.slice('/ofm/'.length) + url.search; + // Fetch WITHOUT cf.cacheEverything so we bypass Cloudflare's edge cache. + // The Singapore edge had corrupted cached responses for OFM PBF tile URLs + // (HTTP 200 with wrong body), which were being served by the edge cache + // when cacheEverything:true was set. Going directly to OFM's origin via + // CF backbone avoids those stale entries. The browser's own HTTP cache + // (driven by the cache-control header we set below) handles client-side + // caching without touching the broken edge entries. const res = await fetch(upstream, { headers: { 'user-agent': 'goodwebtools-ofm-proxy' }, - cf: { cacheEverything: true, cacheTtl: 86400 }, }); if (!res.ok) { return new Response('Upstream OFM fetch failed', { status: res.status || 502 });