Baidu Map GL White Screen: CSP Blocks Worker data: Fetch & eval
The article details debugging a Baidu Map GL white screen caused by CSP blocking worker fetch of data: URIs for image atlases and eval usage in SDK scripts; fixing required adding data: to connect-src and unsafe-eval to script-src in the nginx CSP header.
Background: What a Modern Page Actually Loads
Backend developers often think a page is just HTML plus images. In reality, a modern page loads resources via five distinct mechanisms, each governed by separate CSP directives: script – JavaScript files ( <script src> or dynamic insertion), analogous to loading a code package. img – Images via <img> or CSS backgrounds, analogous to fetching static assets. fetch / XHR – HTTP requests initiated by page JavaScript, controlled by CSP connect-src, analogous to in-process RPC calls. worker – Web Workers, background threads running in isolated environments, similar to forked worker processes. WASM – Browser bytecode for CPU-intensive tasks (GL rendering, media decoding), akin to native extensions.
A special case is the data: URI, which embeds resources as base64 directly in the URL (e.g., fetch('data:image/png;base64,AAA...')). The resource loads from memory without network or disk I/O.
Key insight: Each loading mechanism is independent; allowing img does not allow fetch. This independence is the core of the issue.
How Baidu Map GL Runs in the Browser
Baidu Map GL (JS API GL) is a vector rendering engine, not a static image. Its normal load sequence:
Page inserts a bootstrap script:
api.map.baidu.com/api?type=webgl&v=1.0&ak=merchant_key. This script only assembles parameters and downloads the main script ( getscript, ~800 KB). It first sets window.BMapGL to an empty placeholder object.
After the main script loads, BMapGL namespace becomes usable; new BMapGL.Map(container) creates the map instance.
GL rendering uses a <canvas> (WebGL) and several Web Workers . Workers also run WASM for vector tile decoding.
Workers use fetch to retrieve vector tiles ( pvd?qt=vtile...) and image atlases . Baidu bundles small icons and textures as base64 PNGs inside the worker script; at runtime they are fetched via fetch('data:image/png;base64,...') from memory.
The canvas draws frame by frame: roads, water, POIs, custom markers.
A white screen means any step failed: script not loaded, canvas not created, worker not started, tiles not fetched, atlases blocked.
CSP: Nginx Only Sends the Header; the Browser Enforces It
Content Security Policy is a browser feature, not an nginx feature. Nginx merely sends a Content-Security-Policy response header. The browser then checks every resource load against that policy. Violations are blocked and logged as blocked:csp in the console.
CSP primarily mitigates XSS: even if malicious script is injected, it won't execute if its source isn't allowed. The policy scope is the browser user and page; it does not directly affect backend server security. A misconfigured CSP breaks page functionality without crashing the backend.
The site's actual CSP (from /nginx/conf.d/stall.conf) is:
add_header Content-Security-Policy "default-src 'self';
script-src 'self' 'wasm-unsafe-eval' https://api.map.baidu.com ...;
style-src 'self' 'unsafe-inline' https://api.map.baidu.com https://*.bdimg.com;
img-src 'self' data: blob: https://*.bdimg.com https://*.baidu.com https://*.bdstatic.com;
connect-src 'self' https://api.map.baidu.com https://map.baidu.com https://*.bdimg.com https://*.baidu.com;
font-src 'self' data: https://*.bdimg.com https://*.baidu.com;
worker-src 'self' blob: https://api.map.baidu.com https://*.bdimg.com https://*.baidu.com;
object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;Directive breakdown for backend developers: default-src 'self' – fallback: any unlisted load type only allowed from same origin. script-src – allowed script sources. 'wasm-unsafe-eval' permits WASM compilation; adding 'unsafe-eval' would allow eval(). img-src – only governs <img> tags and CSS background images. connect-src – governs page JavaScript fetch/XHR requests . Independent from img-src; this distinction is the root cause. worker-src – whitelist for worker creation. blob: allows workers created from in-memory code (common pattern: fetch worker script text, wrap as Blob, create worker).
Source tokens: 'self' (same origin), data: (inline data), blob: (in-memory objects).
The difficulty is exhaustive enumeration : missing a single protocol or domain silently breaks the corresponding feature – no exception thrown, no error in business code, only a console blocked:csp line.
Debugging Process (Including Dead Ends)
Round 1: Loader Timing (Fixed but Not the Main Cause)
The bootstrap script sets window.BMapGL = {} before asynchronously downloading the main script. If application code only checks if (window.BMapGL), it proceeds while the main script is still loading, causing silent failure at new BMapGL.Point().
Fix: adopt the official loader pattern – append &callback= to the bootstrap URL, verify BMapGL.Map is a function in the callback, and add a 20-second timeout fallback. This fix is correct and retained, but the map remained white.
Round 2: Systematic Elimination
AK invalid? Server-side curl to GET /qt=verify returned {"error":0}. Ruled out.
CSP missing Baidu domains? Checked script-src, connect-src, worker-src domain whitelists – all present. Mistake: only checked domains, not protocols. data: is a protocol, not a domain, and was absent from relevant directives.
Component code bug? Map instance created successfully; rendering try/catch not triggered. Ruled out.
Embedded browser reproduction? In a controlled browser, the map container lacked even a <canvas>. However, the same embedded browser couldn't run the official Baidu bootstrap script at all. Lesson: an environment that can't run the official demo invalidates all observations made in it. Verify the reproduction environment works with the vendor's example first.
Decisive Evidence: User's Console Errors
In incognito mode (excluding extensions), the console showed:
worker_wasm_g0l1oe.js:65
Connecting to 'data:image/png;base64,iVBORw0KGgo...' violates
the following Content Security Policy directive: "connect-src 'self' https://api.map.baidu.com ...".
The action has been blocked.
Content Security Policy of your site blocks the use of `eval` in JavaScript
Source: getscript?type=webgl… script-src BlockedRoot Cause Analysis
① WASM worker's fetch('data:image/png;base64,...') blocked by connect-src .
Workers inherit the page's CSP. A worker created from a blob receives the creator page's CSP.
Inside the worker, fetch loads the embedded atlases. fetch is governed by connect-src.
The site's connect-src listed only https:// Baidu domains, missing the data: protocol token.
Intuition suggests atlases are images, so one checks img-src – which does contain data:. But img-src only covers <img> tags; it does not cover fetch.
Result: all atlas loads failed → WASM decoding lacked rendering assets → vector features not drawn → only canvas background color visible.
② getscript and risk-control scripts use eval , blocked by script-src . Without 'unsafe-eval', internal SDK mechanisms break.
Both failures are silent : no popup, no exception caught by application code (worker-internal blocks don't propagate to page business logic). Features simply "don't respond".
Solution
Patch the nginx CSP in two places. Domain whitelists unchanged; only add the missing protocol and eval capability:
- script-src 'self' 'wasm-unsafe-eval' https://api.map.baidu.com ...;
- connect-src 'self' https://api.map.baidu.com ...;
+ script-src 'self' 'unsafe-eval' 'wasm-unsafe-eval' https://api.map.baidu.com ...;
+ connect-src 'self' data: https://api.map.baidu.com ...;Trade-off on 'unsafe-eval': it relaxes script injection protection, but allowed sources remain restricted by the domain whitelist, and Baidu Map cannot function without it. "Functionality works" takes precedence over "theoretical hardening"; each allowance is documented in the nginx config. After deployment, the map worked immediately.
Three Takeaways for Backend Developers
When integrating a heavy third-party frontend SDK (maps, video, charts, captcha), start with Content-Security-Policy-Report-Only . Append -Report-Only to the header; the browser reports violations without blocking. Run the full business flow, collect console and network reports, whitelist each violation, then switch to enforcement. Ten minutes saves an evening of source diving.
Distinguish "domain not allowed" from "protocol not allowed". Every CSP whitelist entry is a combination of origin and protocol. Tokens like data:, blob:, 'unsafe-eval' are enumerated alongside https://xxx domains. Protocol tokens are the easiest to miss when adding domains one by one.
Verify your reproduction environment runs the official demo first. If the controlled browser can't run the vendor's example, any "looks normal/abnormal" observations are invalid. All prior checks were voided; the issue was finally pinned by a user's real-browser console screenshot.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
