Deploying a 50MB WASM on Cloudflare Workers: A Troubleshooting Guide
A complete walkthrough of deploying a 50MB BaZi (八字) fortune-calculating WASM to Cloudflare Workers, and how Brotli compression solved the file size limit.
Cloudflare Workers has a hidden limit: individual static assets cannot exceed 25MB.
My BaZi (八字, Chinese Eight Characters) fortune-calculating WASM file is 50MB.
This is a problem that needs solving.
Analyzing the Problem
The WASM file is large for a simple reason: BaZi calculation requires a large amount of embedded calendar data (precise solar term timestamps, true solar time correction coefficients, etc.), and this data is compiled directly into the binary.
There are three possible solutions:
- Split the data: Strip the calendar data out of the WASM and load it on demand as separate JSON files.
- Compress on the wire: Keep the WASM unchanged, compress it with Brotli or Gzip for transfer, and let the browser decompress automatically.
- Switch platforms: Host the WASM on a storage service that supports larger files (such as R2).
Option 1 requires the most changes — it means modifying the Rust source code. Option 3 introduces additional complexity. I chose option 2.
The Impact of Brotli Compression
# Original size
ls -lh ganzhi.wasm
# -rwxr-xr-x 50M ganzhi.wasm
# Brotli compression
brotli -q 11 ganzhi.wasm -o ganzhi.wasm.br
ls -lh ganzhi.wasm.br
# -rwxr-xr-x 12M ganzhi.wasm.br
The compression ratio reaches 76% — 12MB is comfortably within Cloudflare’s 25MB limit.
Worker Configuration
The key is that the Worker needs to set the correct response headers for .wasm.br files, telling the browser this is a Brotli-compressed WASM file:
if (pathname.endsWith('.wasm.br')) {
const response = await env.ASSETS.fetch(request);
const headers = new Headers(response.headers);
headers.set('Content-Type', 'application/wasm');
headers.set('Content-Encoding', 'br');
headers.set('Cache-Control', 'public, max-age=86400');
return new Response(response.body, { status: response.status, headers });
}
When the browser receives Content-Encoding: br, it automatically decompresses the payload and hands the result to WebAssembly.instantiate() as a WASM module. The entire process is completely transparent to the frontend code.
Results
The deployment succeeded, and the BaZi calculation tool is running normally. Users download a 12MB compressed file, the browser decompresses it into a 50MB WASM on the receiving end, and the whole process takes roughly 5–15 seconds (depending on network speed).
Sometimes the most elegant solution isn’t a refactor — it’s adding the right response header in the right place.
The related tool is now live; you can try the full calculation experience at the “八字命盤” (BaZi Chart) entry point above.