When the image service is down, what your recipients see
By Danish Mohammed. , updated 8 min read
When part of the EmailTimer.App image service fails, a recipient sees a picture, not a broken image icon. Every request for an image address answers HTTP 200 with a valid image: the live countdown when it can, and otherwise one of three stand-ins. Every request reaches a Worker running on Cloudflare’s network before it reaches our own server, and that Worker is what a mail client actually talks to.
This is the reliability chapter of choosing a dynamic image and countdown timer vendor for an enterprise email programme. It is written for the person who has to answer “what does our campaign look like if you go down”, and it describes the code as it stood on 23 September 2026, with the edge Worker’s cutover of 26 September 2026 folded in.
The rule behind it is short. An email has already been delivered when its image is fetched, and nothing can recall it, so an error status is a broken image for good. The service is built so that nothing it controls ever sends one.
The four pictures an image address can return
These are real renders from the product’s own engine, the same sale design in each state, plus the card the image service keeps on disk for failures.
| Picture | Served when | Shared caches keep it | Counts as an open |
|---|---|---|---|
| Live countdown | Everything worked | 30 seconds | Yes |
| Expired frame | The deadline has passed | 7 days | Yes |
| Template fallback | This recipient’s image cannot be drawn, such as a missing per-recipient deadline | 30 seconds | Yes |
| Service card | A verified request the image service still can’t draw: an unknown template, a rate or concurrency ceiling, a render past 5 seconds, or any exception | 30 seconds | No |
Where each one is set, and how to give a template a fallback image of your own, is on what a timer shows when it is not counting down. This page is about what happens around them during an incident.
The card has two layers under it. If its file were missing from a deployment, the service draws a plain grey card of the same size without fonts or native code, and if even that fails, it sends a one-pixel grey PNG written out byte by byte in the source.
What an outage looks like
Every image request reaches a Worker running on Cloudflare’s network first. Most of the time it answers from its own cache, without ever asking our origin server. When it does need to ask, that server runs the image service beside Postgres, Redis and the rest of the product, on one machine.
Most origin failures stay inside the rule, because the image service is still running and answering:
- A template that will not load, or an exception. The service card, and a real render on the next fetch once it is fixed.
- A spike past the service’s ceilings. The card, immediately, rather than a place in a queue. What breaks at five million sends has the ceilings.
- A lost signing key cache in Redis. A signed address that already passed the Worker’s own check still needs the origin’s copy of the signing key to render. Every image in that workspace becomes the card until a sweep rewrites the keys from Postgres, within five minutes.
- An image service worker process that dies. The service runs several, one fewer than the machine’s cores by default, and a dead one is replaced. A deploy drains requests in flight first.
- Postgres down. Nothing changes for recipients. The image service holds no database credentials and does not read Postgres on the render path.
What used to be the case outside the rule was the origin server itself being unreachable, with nothing of ours left answering. The Worker in front of it changes that: it can serve a fresh copy from its own cache, or the last real render it holds for that address, without the origin server answering at all. Only past both does it reach for the pixel, which is still HTTP 200 rather than an error. That is what the rest of this page walks through.
What the Worker in front of the image service does on every request
The Worker runs on Cloudflare’s network, in front of the origin server, live since the edge cutover on 26 September 2026. Every open reaches it first.
Three things follow from that.
Most opens stop reaching the server at all. The Worker keeps each render in Cloudflare’s cache under the exact signed query, so a repeat of the same image is answered at the edge.
An outage serves the last real image instead of an error. Every successful render is also kept as a last-known-good copy: a day by default, up to 30 days where the service allows it, as it does for static personalized images. When the service errors or takes more than ten seconds, the Worker serves that copy. A stale countdown is wrong by however long the outage lasts, and still better than a hole in the email.
Many expired timers stop depending on the server. Where the signed address carries its own deadline, as a per-recipient deadline or a send time does, the Worker can see that it has passed and rewrites every such request for the template to one canonical address. That one render is served from the edge cache for everybody, outage or not. A deadline set inside the template is not in the address, so those requests still go to the service.
With neither copy, the Worker answers with a transparent one-pixel PNG at HTTP 200, a different stand-in from the service card: the mail client sizes it from the width and height on your <img> tag and draws nothing, so the space holds. The same pixel answers a forged or unsigned address, which never reaches the server.
What a mail client’s own cache does with any of this
The service tells every cache how long it may keep each picture. The code asks private caches to keep a live countdown, the service card and a template fallback for zero seconds, so a recipient who opened during an incident gets a real render on the next open. An expired frame is the exception, at a day, because it will not change again.
That did not match what we measured on the live hostname before 26 September 2026: the service card came back from img.emailtimer.app with max-age=14400, four hours, where the service sends zero. A Cloudflare zone setting, Browser Cache TTL, was raising a lifetime the origin set lower than its own. It was fixed in code that day (3da2637): the Worker now restates the Cache-Control header the image service intended on every response it serves, from one shared table, rather than passing through what the zone setting adds to its own subrequest to the origin. Measured live after that deploy, a cache hit and a miss both reach recipients with max-age=0, s-maxage=30, the header the service actually sends.
Beyond the headers, each client keeps copies its own way, and nobody can purge them. Gmail fetches through Google’s image proxy, which publishes no cache lifetime (the Gmail page). Apple Mail with Mail Privacy Protection fetches at delivery rather than at open, so what Apple fetched during an outage is what a recipient may see later (the Apple Mail page). Outlook for Windows draws the first frame, and a stand-in is a still picture already.
What to put in the email so an outage costs less
Put the deadline in the body text as well as in the image, because text needs nothing from us. Set width and height on the <img> so any stand-in, including the edge pixel, keeps the layout. Write alt text that says the offer, such as “Sale ends Friday at midnight”, rather than “countdown timer”. Countdown timer not showing covers the reasons an image fails that have nothing to do with the service.
Nothing alerts us automatically yet: no uptime monitor, and no alert on the rate of service cards. An uptime check would stay green anyway, because every failure is a 200, which is why the service counts each card by its reason. The status page says what is watched and how an incident reaches you.
A security reviewer will ask the same question from the other side, and what a security review asks an email image service has the documents behind these answers. If you are weighing this against the vendor you use today, the matching question is what their addresses return once the contract ends, which moving off an incumbent without breaking mail already sent answers for each of them. The plan limits behind the countdown are on pricing.
Questions and answers
Does a recipient ever see a broken image icon?
Not from anything the image service or the Worker in front of it sends. Between the Worker’s own cache, its last-known-good copies and the pixel, an origin outage never has to reach a mail client as an error. That would need Cloudflare’s own network to be unreachable, which none of the incidents this page describes have been.
Is a stand-in counted against our plan?
The service card is not counted. The expired frame and a template fallback are, because each is a rendered response for a real template. How opens are counted has the full rule.
Sources
Related
Put a countdown in the campaign you are writing now
The free plan covers 5,000 opens a month with no card. Starter is $19 a month for 250,000.