Blog

Astro website performance: what dardo.studio measures

Field Core Web Vitals, Lighthouse runs, page weight and running cost for dardo.studio, an Astro site on Cloudflare Workers, including what failed.

By Nicolás Cerón ·

A paper house model on an architect's workbench at night, surrounded by a ruler, calipers and a brass stopwatch with a crimson hand
On this page

The short answer

dardo.studio is an Astro site served by a Cloudflare Worker, with content in a headless CMS and every page in English, Spanish and Portuguese. Over 28 days, from 24 September to 22 October 2026, the 75th percentile of real page views was 1,114 ms for Largest Contentful Paint (LCP), 80 ms for Interaction to Next Paint (INP) and 0.08 for Cumulative Layout Shift (CLS). Google's good thresholds are 2.5 seconds, 200 ms and 0.1, so the site as a whole passes.

The averages hide two problems. On large screens the home page's CLS p75 was 0.239, more than twice the limit, because a hover effect rearranged a grid; we fixed it on 22 October. On phones the home page's INP p75 was exactly 200 ms, good with no margin, and still unexplained. In the lab our pages scored 90 to 99 on mobile, and the weak spot is JavaScript: about two-thirds of it on a content page is analytics.

How the site is built, and why each piece

  • Astro 7.3.2 with static output, because a marketing page should be HTML first: Astro strips client-side JavaScript from components unless one is marked as an interactive island. One route, /preview/, renders on request so the CMS can show drafts.
  • A Cloudflare Worker, through the @astrojs/cloudflare adapter 14.3.4, so redirects, language routing, the contact form's API and our free tools' server checks live next to the files it serves.
  • EmDash, a headless CMS on Cloudflare, so editors publish without a developer and visitors never wait on a CMS query. It holds the pages, 41 service pages, case studies, articles, tools, interface text and settings; a publish triggers a rebuild through Workers Builds that goes live a few minutes later. More in headless CMS development.
  • Cloudflare D1, a SQLite database, for inquiries and our own Core Web Vitals readings (kept 35 days), so we own the field data; R2 for media.
  • Three languages for the US, Latin America and Brazil, with hreflang alternates: 418 URLs in the sitemap on 22 October 2026, 145 in English. See multilingual websites.

Everything except the draft preview changes when an editor publishes, not when a visitor arrives, so it is built once and served as files. Our Astro vs Next.js comparison covers when that trade goes the other way.

Field data: what real visitors got

These numbers come from our own measurement, not Google's Chrome UX Report. Each page view sends its Core Web Vitals, Time to First Byte (TTFB) and First Contentful Paint (FCP) to a D1 table. We report the 75th percentile, as Google's guidance does, split into phone-sized and large (tablet and desktop) viewports. Collection follows the visitor's analytics choice, so browsers that opt out or send Do Not Track or Global Privacy Control are not in the sample. The samples are small: read rows with a low n as direction, not proof.

Page type, screenLCP p75INP p75CLS p75TTFB p75
All pages, all screens1,114 ms (n=1,414)80 ms (n=883)0.08 (n=1,223)457 ms (n=1,712)
All pages, phones1,016 ms (n=291)184 ms (n=183)0.001 (n=153)489 ms (n=389)
All pages, large screens1,192 ms (n=1,123)64 ms (n=700)0.089 (n=1,070)438.9 ms (n=1,323)
Home, phones1,018 ms (n=182)200 ms (n=148)0.0 (n=75)476 ms (n=232)
Home, large screens792 ms (n=607)56 ms (n=465)0.239 (n=574)380.9 ms (n=663)
Service pages, phones640 ms (n=42)48 ms (n=14)0.067 (n=32)485 ms (n=47)
Service pages, large screens716 ms (n=96)64 ms (n=42)0.089 (n=89)475.6 ms (n=129)
Articles, phones1,076 ms (n=13)40 ms (n=2)0.001 (n=13)281.8 ms (n=13)
Articles, large screens648 ms (n=30)40 ms (n=8)0.005 (n=32)162.3 ms (n=55)

LCP is the strongest result: 90% of readings were good, and the slowest row, all pages on large screens, is 1,192 ms. TTFB p75 was 457 ms, under the 0.8 seconds web.dev calls good, and FCP p75 was 1,068 ms against a good mark of 1.8 seconds. Of the six page-type and screen rows, five pass all three Core Web Vitals; the home page on large screens fails on CLS. INP splits by device, 64 ms on large screens and 184 ms on phones, and 148 of the 183 phone INP readings came from the home page, the one page with heavy artwork.

What failed, and what we changed on 22 October

Our D1 table stores values without attribution, so on 21 and 22 October we found each cause by reproducing it in a browser with performance traces.

Home page layout shift on large screens

CLS p75 was 0.239 (n=574), with only 65% of readings good; a 21-day slice read 0.306. Hovering a card in the services mosaic expanded it by changing the grid's columns, so every card around it moved, and each hover shifted the layout by 0.13 to 0.26. A hover isn't a discrete input like a click, so the browser doesn't excuse its shifts as it does those within 500 ms of a click. Phones have no hover, which fits their 0.0.

Cards now keep one origin and move with CSS transforms, which don't change layout. A lab sweep that hovers every card went from 0.331 to 0.0008. The trade-off: the mosaic's artwork can sit up to 1 px from its resting place. The field number needs a few weeks of new data to move.

Phone INP on the home page

INP p75 on phones was 200 ms (n=148), 75% good. A 21-day slice read 232 ms (n=126); since 15 October it was 128 ms, on 32 readings. The worst interaction we can produce in the lab takes 64 ms. The one slow interaction we caught landed on a 295 ms startup frame with no long script, which points at the GPU or a shader compile for the WebGL artwork. Making five per-frame attribute writes run only on change cut DOM mutations by 23% but left the lab at 64 ms. It stays open. Adding attribution to our own measurement, as our guide to fixing INP describes, would point at the frame or script behind it.

Service page shifts and a slow work hub

Service pages on large screens had a CLS p75 of 0.089, yet four readings within three minutes on 21 October came in at 0.223. Covers used aspect-ratio: auto, so text jumped about 435 px when the image arrived, and intros sized in ch units grew 24% wider when the brand font loaded. Covers now reserve their ratio, those widths have an em floor and fallback fonts are scaled to the brand fonts. In the lab, service pages went from 0.03 to 0.21 down to 0.04 or less, and tablet layouts from as much as 0.50 to 0.004 or less.

The work hub's desktop LCP p75 was 4,740 ms in a 21-day slice (n=36; 2,868 ms over 28 days, in a page type that also holds case studies). Its largest element, a blurred backdrop, had no fetchpriority while hidden rows' images got the boost. Hero images now load at high priority, the rest at low, and a 4.4 MB film waits for its poster. Lab LCP fell from 1.80 to 1.45 seconds at 1x pixel density and from 2.20 to 1.57 at 2x, on an HTTP/2 server that honors priority.

Lab data and page weight against platform medians

We ran Lighthouse 12.8.2 locally on 22 October 2026 and report the second mobile run, because the first run after a rebuild hits a cold cache. Lighthouse's documentation notes that much run-to-run variation comes from outside the tool.

PageMobile scoreMobile LCPMobile CLSDesktop scoreDesktop LCP
Home961.6 s0.000950.6 s
Service: Astro development903.5 s0.000990.7 s
Article: Framer to Astro962.8 s0.000990.8 s
Tool: redirect map generator991.8 s0.0001000.4 s

The service page's mobile LCP of 3.5 seconds and the article's 2.8 are past the 2.5-second line, while real visits to those page types had p75 LCP between 640 and 1,076 ms. Lighthouse's mobile run simulates a slow phone on a throttled connection, slower than our field data suggests most visitors have, so we treat it as a regression alarm and the field data as the verdict. Lab CLS was 0.000 everywhere, which is why the hover shift went unnoticed: a page-load test doesn't hover.

Page weight against typical sites on each platform

The HTTP Archive measures the home pages of sites it detects on each platform. The platforms' audiences differ, so its medians describe typical sites, not what a careful build achieves. Ours are Lighthouse transfer sizes, one run per page.

Page or platformTotalJavaScriptImagesLighthouse mobile
dardo.studio home1,168 KB709 KB218 KB96
dardo.studio service page820 KB433 KB207 KB90
dardo.studio article716 KB432 KB120 KB96
dardo.studio tool page663 KB440 KB43 KB99
Astro median, 47,277 origins1,326 KB399 KB346 KB70
Framer Sites median, 11,319 origins3,011 KB797 KB934 KB67
Webflow median, 59,483 origins3,398 KB904 KB793 KB62
WordPress median, 2,658,175 origins2,656 KB661 KB1,017 KB43

Each of our pages is lighter in total than the median Astro home page and less than half the Framer, Webflow and WordPress medians, and carries fewer image bytes than any of them. JavaScript is the exception: every page ships more than the Astro median's 399 KB.

Pass rates tell a quieter story. In September 2026, 71.1% of Astro origins passed all three Core Web Vitals on mobile, close to Framer at 70.8% and Webflow at 71.3%, and well above WordPress at 48.3%. On desktop the figures were 84.6%, 87.9%, 82.6% and 53.7%. A lighter median page doesn't guarantee a pass; the build decides.

Where the JavaScript goes

Content pages carry about 432 KB of JavaScript. About 287 KB is analytics, loaded from third parties or through our proxy: Google's tag (gtag.js, 160 KB) and PostHog's recorder, surveys, autocapture and web-vitals modules (about 127 KB). Our own scripts are about 145 KB, a 96 KB shared interface bundle plus small page modules. Fonts add 126 KB. The home page carries 709 KB because of its interactive artwork, a WebGL dart flight driven by scrolling.

So two-thirds of the script on an article or service page is measurement, not the page. It costs transfer, plus parsing and running on the phone's main thread, the thread INP depends on. Content pages still answer quickly in our field data, with INP p75 of 40 to 64 ms on small samples. We keep the analytics because they show which pages and tools lead to inquiries; if the numbers slip, the cheaper moves are fewer PostHog modules, loading them later or dropping one tool. A speed optimization project should start the same way: list every script, its size and who needs it.

What it costs to run

The fixed platform cost is USD 5 a month, the Cloudflare account's Workers Paid plan, verified in the account's subscriptions on 22 October 2026. R2 and Stream are on usage-based plans with no base fee, the zone is on Cloudflare's free plan, and the domain and CMS run on the same account.

As of October 2026, Workers Paid includes 10 million requests and 30 million CPU milliseconds a month, then USD 0.30 per additional million requests and USD 0.02 per additional million CPU milliseconds. Requests to static assets are free and unlimited, which matters for a site that is almost all static files. D1 includes 25 billion rows read, 50 million rows written and 5 GB of storage a month, and egress from R2 is free.

The USD 5 excludes usage above those amounts, R2 and Stream usage, the domain registration, services outside Cloudflare such as analytics, and people's time. We don't publish the account's invoices, so read it as the floor, not our monthly bill.

How the site serves AI crawlers and agents

  • robots.txt allows search and AI crawlers, with Content-Signal preferences of search=yes and ai-input=yes.
  • /llms.txt (42 KB) and /llms-full.txt (about 877 KB), generated from the CMS.
  • A Markdown copy of every page: add index.md to the address, or send Accept: text/markdown.
  • A public, read-only MCP endpoint at /mcp and an A2A agent card at /.well-known/agent-card.json that answer questions about our published services from the same content as the pages.

What we can't claim: we know of no AI search vendor that documents an effect of llms.txt, Markdown copies or an MCP endpoint on whether a page gets cited. We publish them because they are cheap to generate from structured content and useful to agents that read them. Our llms.txt guide covers what the file is and who reads it, and the AI visibility checker shows how your robots.txt treats each AI crawler. We expect crawlable HTML, specific facts and named sources to matter more.

What this means if you are considering Astro

Astro made it easy to keep content pages light: our own code on an article is about 145 KB of JavaScript, and nothing runs unless we add it. It didn't prevent our two problems. A hover effect broke CLS and WebGL artwork is the suspect for phone INP; both were design decisions that would cause the same trouble on any platform.

Astro fits when the site is mostly content that changes on publish, you want the code, hosting and data in your own accounts, and you have developers to own the templates. It is the wrong move when your marketing team edits layouts visually every week and the current site already passes Core Web Vitals; Framer and Webflow pass rates are close to Astro's. A migration should buy something specific: code you own, a backend the builder can't provide, or control over every script.

Astro on Cloudflare Workers is our default stack for Astro development, and we run migrations from Framer to Astro, Webflow to Astro and WordPress to Astro.

How to measure your own site

  1. Start with context: our website platform speed report shows the HTTP Archive's latest month for your platform.
  2. Get field data. Google's Chrome UX Report only covers pages and origins with enough Chrome visitors; if yours falls short, send Core Web Vitals from the browser to your own store, as we do with D1.
  3. Report p75 with the sample size, by page type and screen size. Our site-wide numbers passed and hid both problems.
  4. Run Lighthouse twice, keep the second run and use it to catch regressions, not as the verdict.
  5. Test what a load test skips: hover, menus, scrolling artwork. Our worst layout shift lived there.
  6. Fix one cause at a time, then wait for new field data before calling it fixed.

Our guide to passing Core Web Vitals covers each metric's usual causes and fixes.

Common questions

Is an Astro site faster than a Framer or Webflow site?

Not automatically. In September 2026 the median Astro home page weighed 1,326 KB on mobile, against 3,011 KB for Framer and 3,398 KB for Webflow, yet the share passing Core Web Vitals on mobile was about the same: 71.1%, 70.8% and 71.3%. Astro ships no JavaScript unless you add it; whether a site passes depends on what you add.

Why does Lighthouse report a slower LCP than real visitors see?

Its mobile run simulates a slow phone on a throttled network, slower than most of our visitors appear to have. Our Astro development page had a 3.5-second LCP in the lab, while real visits to service pages had a p75 LCP of 640 ms on phones and 716 ms on large screens. Use the lab to catch regressions and field data to judge what visitors get.

How much does it cost to host an Astro site on Cloudflare Workers?

Our fixed cost is the Workers Paid plan, USD 5 a month as of October 2026, with 10 million requests and 30 million CPU milliseconds included and static asset requests free. Usage above that, storage, video, the domain and outside tools cost extra, so check Cloudflare's pricing against your own traffic.

Do llms.txt files or an MCP endpoint get a site cited in AI answers?

We know of no AI search vendor that documents such an effect. We publish /llms.txt, Markdown copies and a read-only MCP endpoint because they are cheap to generate from CMS content and useful to agents that read them. Treat them as a convenience, not a lever, and keep crawlable HTML and sourced facts right.

How many readings do you need before field data means something?

There is no single number. Report the sample size next to every p75 and read small slices as direction. Our site-wide LCP rests on 1,414 readings, while articles on phones rest on 13 and their INP on 2, so we draw no conclusion from those rows alone.

Sources & further reading

Sources behind this page, with further detail from the original publishers.