White screen after releases (for some people)

I’ve had this nagging issue on my site for many years. I just upgraded to the latest release with rspack, and after releasing it happened to me. A handful of people will get a white screen when they go to the site. I cleared the Cloudflare cache and deleted my browser data, and the site came back. It’s impossible to duplicate consistently, so I’ve never been able to track the issue down. Has anyone else seen or known about this issue?

We are running our site in Docker containers in AWS Fargate.

1 Like

@robertschlackman

I currently use Meteor 3.5-rc.1 at WeKan https://wekan.fi

I use CloudFlare Developer mode that bypasses all cache. I also have cache related headers etc. I currently use Oplog sockjs, at bare metal Ubuntu 26.04 server at Hetzner, 64 GB RAM, 2x1GB NVME RAID. My Meteor 3 WeKan Multitenancy config is fully documented here:

I use rebuild-wekan.sh to build WeKan and run all tests:

I use release-all.sh to change version number, then GitHub Actions builds for all platforms:

For those GitHub Actions, I have created GitHub Secrets with this script:

Do your customers share any kind of log? maybe in the console tab

Nothing shows in the console and I also trap errors on the client and save them in a collection in mongo. Nothing shows up in the error log. It doesn’t work again until the user clears out the client side mini mongo.

@robertschlackman I’m fairly confident I can explain this (Well, Claude can) it’s a CDN caching interaction rather than a Meteor bug, and the rspack upgrade is what made a long-dormant misconfiguration start biting.

What’s happening

Meteor serves the root HTML document without a Cache-Control header. That HTML is the entry point that references your JS/CSS bundles by their content hash (e.g. /abc123.js), and those hashes change on every deploy. The hashed assets themselves are served immutable, max-age=1y — which is correct, because their URL is unique per build.

The problem is the entry document. A text/html response with no cache header invites a CDN to apply its own heuristics, and if Cloudflare is configured to cache it (a “Cache Everything” page rule, or a Cache Rule that catches HTML), the edge keeps serving an old HTML pointing at old bundle hashes. After you deploy, the new origin no longer has those files, so:

  • old HTML (from the edge) → requests /<oldhash>.js404 → the main script never loads → white screen.

This lines up with everything you described:

  • “Nothing in the console / error tracker” — a <script> that 404s doesn’t throw a catchable JS exception; it fires an error event on the element that window.onerror and most error trackers don’t capture. So a silent console is expected here, not evidence against it.
  • “Purging Cloudflare cache fixes it” — that evicts the stale HTML at the edge, so everyone gets the new entry document.
  • “Clearing browser data fixes it for that user” — same fix one layer down: it drops the browser’s own heuristic copy of the header-less HTML (plus the autoupdate version Meteor persists in localStorage), forcing a fresh fetch. (Side note on “clearing minimongo”: vanilla minimongo is in-memory and can’t persist across a reload, so the artifact actually being cleared is the cached HTML/bundle + that localStorage version — same root cause, the wording just points elsewhere.)

And critically: when the entry bundle 404s, the app never boots far enough to open a DDP connection, so Meteor’s own hot-code-push/autoupdate can’t kick in to self-heal. The user is stuck until the cache expires or is cleared.

Why rspack made it worse

Pre-rspack your app shipped largely as one bundle, so a stale entry failed in exactly one way. rspack splits every import() boundary into its own content-hashed chunk, deletes the previous build’s chunks on each deploy (its clean step — no grace window where old and new coexist), and ships no retry/reload on a failed chunk load. So now there are many more independently-versioned files that can 404, and zero tolerance for a stale reference. A client running an old bundle that lazy-loads a route then hits a ChunkLoadError (the chunk hash it wants was deleted), and the route renders blank. The upgrade itself was the trigger because it was a single deploy that rotated every hash at once.

How to confirm (2 minutes)

Next time it happens, or just on a normal load:

  1. DevTools → Network. Reload. Look for any .js/.css returning 404. If you see one, that’s it.
  2. Click the document request for / → Response Headers. Look for cf-cache-status: HIT and whether there’s a Cache-Control. If Cloudflare is serving the HTML from cache, that’s the smoking gun.

The fix

It’s on the Cloudflare side:

  • Never edge-cache the HTML document. Find the “Cache Everything” page rule (or Cache Rule) and exclude text/html / your app routes from it. Cloudflare’s default already bypasses HTML — so if you’re hit by this, something is explicitly caching it.
  • Keep caching the hashed assets aggressively — they’re already immutable and safe.

Optional belt-and-suspenders for the rspack chunk case: add a global handler that catches ChunkLoadError / a failed dynamic import() and does a single window.location.reload().

There’s also a reasonable argument that Meteor should send Cache-Control: no-cache on the boilerplate HTML by default instead of trusting every CDN to be smart — happy to raise that with the core team if others run into this.

Let me know what the Network tab shows after a repro — if it’s a 404 on the entry bundle, the Cloudflare rule is the whole story. Cc @italojs

Thanks so much. This makes a lot of sense. I’ll check back when I can provide more information about this fix.