The 6:40 Bell and the 40,000 Parents: A Closure Page That Holds
Every UK school office knows the ritual. The forecast mentions snow, the group chat gets twitchy, and by 6:40 the closure page is taking more traffic than the rest of the trust combined. Forty thousand hits before breakfast is not a normal load problem. It is a short, sharp stampede from parents on phones, often on a weak signal, all asking one question: is it shut?
The page that answers that question cannot be a normal CMS page with a chat widget and a virtual tour bolted on. It needs to be boring, small and already sitting at the edge. The pattern below is a two-tier build: a pre-rendered snapshot for the crowd, and a single control switch for the staff who have to make the call at 6:15.
Design for the bad morning, not the average one
A closure page has an odd life. It sits idle for eleven months, then gets hammered for two hours, then goes quiet again. Autoscaling from cold is the wrong tool. So is a page that depends on the CMS rendering under load. The sensible approach is to separate the thing parents read from the thing staff edit.
Tier one is a static HTML snapshot served straight from the edge. Tier two is a tiny panic-mode flag that decides whether the nice-to-have scripts are allowed anywhere near the page. When the flag is on, the page is just the closure notice. When it is off, the extras can load after the important stuff, or not at all.
At MonkeyLoader we would keep that split deliberately unglamorous. No clever personalisation, no client-side router, no service worker on this route. A school closure page is a status board. Treat it like one.
Tier one: a snapshot at the edge
The snapshot is built when staff publish a closure, or when the panic switch changes state. It contains the school name, the closure status, the date and time it was last updated, the affected year groups or sites, transport information if buses are cancelled, and a phone number for anyone who needs a human. That is the critical bundle.
Keep the budget tight. Roughly: HTML under 30KB, inline CSS under 10KB, a small inline SVG logo, and one tiny loader script under 5KB. Use a system font stack rather than a web font. No tag manager, no chat widget, no analytics, no font loader, no virtual tour embed. Those can wait, and on the bad morning they should not be there at all.
Serve the HTML with a browser cache of zero and an edge cache of around sixty seconds. That sounds aggressive for a static page, but it is the right trade for a status board. Forty thousand parents hit the edge, not the origin. The browser revalidates, the edge answers. Add stale-if-error so a wobbly origin does not turn into a blank page while the gritters are still out.
Do not put a service worker in front of this route. A service worker is brilliant for offline apps and terrible for a page whose whole job is to tell you the current truth. If you already have one, exclude the closure path explicitly.
Tier two: one panic switch, no deploy
The loader script fetches a tiny JSON file, something like /panic-mode.json, cached at the edge for thirty seconds. If panic mode is on, the loader does nothing. If it is off, the loader can bring in analytics, the chat widget, the font loader or the virtual tour after the page has rendered, preferably on idle.
The important part is that staff flip the switch in the CMS. They do not ring a developer. They do not wait for a deploy. The CMS updates the flag, purges the closure page by cache tag, and the next request gets the new snapshot. If the flag file is stale for half a minute, the page is still correct because the closure status itself is in the HTML, not in the flag.
Name the switch in plain language. 'Snow closure mode' is clearer than 'degraded experience toggle'. The person using it at 6am is not thinking about bundle budgets.
The stale-closure disaster
The worst failure mode is not a slow page. It is a fast page that says the school is open when it is shut. That usually happens because yesterday's HTML is still cached somewhere, or because a service worker is serving a stale shell, or because someone purged the homepage but not the closure URL.
So make cache-busting part of the publish routine. Every snapshot gets a build ID in an HTML comment and in any versioned asset URLs. The page is purged by surrogate key when the switch flips. The response carries a visible 'last updated' timestamp so parents can see whether they are looking at this morning's notice or last January's. And the browser cache stays at zero, so a parent who refreshes gets the edge's current copy rather than a local memory of the old one.
After a purge, check the page from at least two networks and one mobile handset. Do not trust the CMS preview alone. The preview is not the edge.
The week before the forecast turns
Rehearse the switch, not just the page. A short checklist is worth more than a long runbook nobody reads.
- Flip snow closure mode in staging and confirm the extras disappear.
- Publish a test closure and purge it. Check the timestamp and the status on a phone using mobile data.
- Load the page with JavaScript disabled. The closure notice must still be readable and correct.
- Open an old bookmarked URL and confirm it resolves to the current snapshot, not a cached copy.
- Confirm no service worker is intercepting the route.
- Check that the person on duty can flip the switch without calling anyone.
- Look at the edge hit ratio during a small burst. If the origin is taking the traffic, fix it before the real snow.
The first flake is not the time to discover that the chat widget is blocking the closure notice. Build the quiet page, keep the loud stuff behind one switch, and let the edge take the stampede. That is the whole trick.