We migrated our platform from traditional hosting to the edge: the real numbers
TTFB from 1,068 ms to 259 ms, US$0/month infrastructure and three features that had been broken for months. What went well, what broke silently and what we would do differently.
We migrated our own platform from traditional shared hosting to edge computing. We are publishing the numbers because almost nobody does: there is plenty of talk about architecture in the abstract and very little about what actually changes when you measure before and after.
What we measured before touching anything
The first rule was to move nothing without a baseline. With the site still on the previous host, we measured TTFB for an HTML page, a hashed asset and an API endpoint, three times each:
- HTML: 1,068 ms median, with
cf-cache-status: DYNAMICon all three requests. - Hashed asset: 249 ms, with
HIT. - API: 1,026 ms cold, 326 ms warm.
The first figure shaped the whole project. There was a CDN in front, but HTML was never cached: every navigation travelled to the origin data centre. The CDN was installed, not configured. It is a more common failure than it seems, and it shows up in no dashboard — only in the response header.
What changed at the edge
- TTFB from 1,068 ms to 259 ms: 4.1 times faster.
- Variance nearly eliminated. The origin ranged between 728 and 1,075 ms depending on load; the edge stays between 251 and 280 ms. For a landing page, predictability matters as much as the average.
- Infrastructure cost: US$0/month. Static asset requests do not invoke code and do not consume quota.
None of those gains came from optimising code. They came from removing the trip to the origin.
Three features the previous host made impossible
- Dead outbound mail. The provider blocked ports 25, 465 and 587. Booking confirmations had not been sent for months. It did not fail with an error — they simply never arrived.
- No scheduled tasks. The hosting management API did not expose the cron module, so a data sync that should run on its own depended on someone pressing a button.
- Deployment without rollback. The mechanism was a file dump that never deleted, so production accumulated orphans nobody audited.
What broke silently
Rewriting an API in another language has a specific failure mode: the new code works, returns 200, and the screen is still empty. It happened to us four times.
The clearest case: the booking availability endpoint returned a list of slots, while the calendar component expected an object indexed by date. On a list, that access returns undefined, so the calendar painted every day as fully booked. With no error anywhere. Smoke tests missed it too, because they checked the HTTP status and the status was correct.
The operational lesson: when porting an API, response shape is part of the contract, not an implementation detail.
Migrating data without stopping operations
The requirement was no write freeze. The strategy was initial load plus incremental re-sync by timestamp, with reconciliation at the end.
The detail we nearly missed: timestamp detection is blind to deletions. Deleting a row updates no date field anywhere. Comparing counts does not catch it either: one deletion plus one insertion give the same total. Verification had to compare identifier sets, not counts.
The limits that shape free-tier design
- 10 ms CPU per invocation. That is CPU, not wall time: waiting 20 seconds on an AI model costs nothing. It breaks where you process an unbounded collection.
- 50 database queries per invocation (D1 limits), forcing aggregation in the database rather than in memory.
- 3 MiB of compressed code. Enough for a ported API; not enough for server-side rendering with a full framework.
- Sending mail to arbitrary recipients requires a paid plan; sending to verified account addresses is free.
That last point had an exit not in the plan: sending over SMTP with the corporate mailbox already being paid for. From the edge only port 465 with implicit TLS works — 25 is blocked and 587 with STARTTLS is unsupported — which is exactly what the mail provider offers.
What we would do differently
- Write contract tests before porting, not after the first failure.
- Measure the baseline more rigorously. Three requests from one location confirm the order of magnitude, not a distribution.
- Treat rollback as part of the design. While the previous host stayed contracted, reverting meant removing a routing rule. Once cancelled, that safety net disappears.
Does this apply to you?
If your site is mostly static and your API does bounded work, the change is straightforward and the savings are real. If you depend on server-side rendering with a heavy framework, persistent connections to your own database, or long compute-heavy processes, free-tier limits will squeeze you.
What applies in every case: measure before migrating. Our most profitable finding was not picking a better platform — it was discovering the CDN we already had was caching nothing.
Sources
- Cloudflare Workers — Limitshttps://developers.cloudflare.com/workers/platform/limits/
- Cloudflare D1 — Limitshttps://developers.cloudflare.com/d1/platform/limits/
- Cloudflare Workers — Static assetshttps://developers.cloudflare.com/workers/static-assets/
- Cloudflare Email Service — Pricinghttps://developers.cloudflare.com/email-service/platform/pricing/
- web.dev — Time to First Byte (TTFB)https://web.dev/articles/ttfb
Does this resonate? Let's talk.
If this describes a problem you have, schedule 30 minutes with us. No commitment. We tell you if we fit.
Request free diagnostic