---
slug: "emdash-is-slow-as-of-v0-9-0"
title: "EmDash is slow (as of v0.9.0)"
description: "Tried Cloudflare's new CMS EmDash on Cloudflare Workers and saw TTFB of 500ms–3000ms. Even a text-only about page lands above 400ms. With comparisons, a breakdown of the bottleneck, and links to third-party benchmarks."
url: "https://www.ytyng.com/en/blog/emdash-is-slow-as-of-v0-9-0"
publish_date: "2026-05-06T07:48:35.191Z"
created: "2026-05-06T07:48:35.193Z"
updated: "2026-05-08T09:49:10.726Z"
categories: []
keywords: ""
featured_image_url: "https://media.ytyng.com/resize/20260506/71ed668ce04c4779841abefd6d78103c.png.webp?width=768"
has_video: true
has_music: true
video_urls: ["https://media.ytyng.net/ytyng-blog/346/featured-video-1.mp4", "https://media.ytyng.net/ytyng-blog/346/featured-video-2.mp4", "https://media.ytyng.net/ytyng-blog/346/featured-video-3.mp4"]
music_urls: ["https://media.ytyng.net/ytyng-blog/346/featured-music-346-1.mp3", "https://media.ytyng.net/ytyng-blog/346/featured-music-346-2.mp3"]
lang: "en"
---

# EmDash is slow (as of v0.9.0)

[Cloudflare](https://www.cloudflare.com/) launched a CMS called [EmDash](https://docs.emdashcms.com/) in April 2026. It's a full-stack CMS that runs on Astro + Cloudflare Workers + D1 + R2 and bills itself as the [spiritual successor to WordPress](https://blog.cloudflare.com/emdash-wordpress/). It's also AI-agent-first by design, so authoring a custom plugin with an LLM agent is unusually smooth.

I had high hopes when I tried it. **It's noticeably slow in practice.**

![The default EmDash blog template — typical post-list landing page](https://media.ytyng.com/20260506/c1a496103aa44d9a84ee2e0c93e343a9.png)

▲ The site after applying EmDash's optional bundled demo data during setup. This is what the measurements below are taken against.

## How slow

I deployed the default demo project (a typical blog with a post-list landing page) generated by `create-emdash` to Cloudflare Workers and measured response times. Plan: [Cloudflare Workers Paid ($5/month)](https://developers.cloudflare.com/workers/platform/pricing/).

```
=== Top page (with post list) ===
0.925s  TTFB=0.916s
0.505s  TTFB=0.501s
0.582s  TTFB=0.580s
0.486s  TTFB=0.484s
0.481s  TTFB=0.380s

=== /about page (text only, no images, minimal data fetching) ===
0.420s  TTFB=0.408s
0.464s  TTFB=0.458s
0.960s  TTFB=0.943s
0.511s  TTFB=0.507s
0.410s  TTFB=0.392s
```

**The top page lands at 480–925 ms TTFB, and even a text-only `/about` page is 410–960 ms.** Hit it more often and you'll catch a cold start that pushes it to 2–3 seconds.

![Firefox DevTools network tab — a turtle warning icon next to the document request, with the tooltip "Slow server response time (880 ms). The recommended limit is 500 ms."](https://media.ytyng.com/20260506/1e3c2f65c636423ea8ac975b0ca5e30f.png)

The browser is picking this up as well. Firefox's DevTools flags the document request with a **turtle icon**: "**Slow server response time (880 ms). The recommended limit is 500 ms.**" The browser's own objective threshold rules it slow.

## Comparison

For reference, here are some other sites I run.

| Site | Stack | Measured TTFB |
|---|---|---|
| [www.cyberneura.com](https://www.cyberneura.com/) | Astro (SSG) + AWS Amplify Hosting / no DB | **30 ms** (160 ms cold) |
| [www.ytyng.com](https://www.ytyng.com/) | SvelteKit (SSR) + Django Ninja API / PostgreSQL | **100 ms** |
| [chat-attendant.cyberneura.net](https://chat-attendant.cyberneura.net/) | SvelteKit (SSG) + Cloudflare Pages / no DB | **65 ms** |
| [emoji.ytyng.com](https://emoji.ytyng.com/) | SvelteKit (SSG) + Vercel / no DB | **32 ms** |
| **emdash-test-01 (this post)** | **EmDash 0.9.0 + Cloudflare Workers + D1 + R2** | **480–960 ms** |

The most directly comparable use case for EmDash is a blog site — that's exactly what www.ytyng.com (this site) is. Against it, EmDash is roughly 5× slower.

Against the static / SSG sites it's 20–30×.

## "Is it just my setup?"

I checked. **No, it isn't. It's structural to EmDash on Cloudflare Workers + D1.**

### A detailed third-party benchmark

[shift64 — "I Bought the Domain Before I Ran the Test. EmDash Still Lost to WordPress."](https://shift64.com/blog/emdash-cms-vs-wordpress-honest-benchmark)

| | EmDash | WordPress | Ratio |
|---|---|---|---|
| Server processing | 543 ms | 84 ms | WordPress is **6.4× faster** |
| Total TTFB | 596 ms | 136 ms | |

The author — who started out hoping EmDash would win — concludes (quoted):

> An SEO professional reviewing crawl-stats charts blind classified the EmDash performance as needing to be **"scrapped and rebuilt"** (~1,300 ms zone), while the WordPress site landed in "normal, expected" territory.
>
> After **aggressive optimization**, the gap narrowed only to **4.1× slower**, still insufficient for recommendation.
>
> The fundamental issue stems from architecture — **D1 database round-trips at the edge create an unavoidable latency floor that optimization cannot overcome** without platform-level changes to Cloudflare's infrastructure.

My measurements (596 ms vs his 596 ms) line up almost exactly, so this looks like a reproducible reality.

### Other independent voices

- [Maciek Palmowski: EmDash, a fresh take on CMS](https://maciekpalmowski.dev/blog/emdash-a-fresh-take-on-cms/) — argues that EmDash doesn't actually target what WordPress users actually struggle with: **performance, hosting cost, plugin bloat**. EmDash's real focus is plugin security and AI-agent integration.
- [Hacker News thread #47602832](https://news.ycombinator.com/item?id=47602832) — not many direct performance complaints, but the overall mood is "interesting architecture, but we've heard this before."

### Beware the contrary numbers

Some posts will tell you EmDash is blazing fast. Read carefully — they're measuring different things.

- [Lushbinary: "P50 latency 1–3 ms"](https://lushbinary.com/blog/emdash-aws-vs-cloudflare-hosting-costs-performance-2026/) — that's just the Worker hop, not the EmDash render path.
- [dev.to: "sub-50 ms TTFB"](https://dev.to/muyaedward/emdash-vs-wordpress-which-cms-delivers-the-best-speed-for-african-websites-4d3k) — talks about edge-node distance, not actual measured render time. Reads like marketing.

Trust the actual numbers from shift64 and your own probe — not the abstract latency claims.

## What's slow

EmDash emits a `Server-Timing` header on every response. Here's the breakdown for one of my requests:

```
setup;dur=61          probe
rt;dur=89             runtime init total
  rt.db;dur=23        D1 migration check
  rt.plugins;dur=10   _plugin_state load
  rt.site;dur=12      site info options
  rt.market;dur=15    marketplace plugins
  rt.hooks;dur=29     exclusive hook resolution
render;dur=346        ← page render (D1 fetch for posts/media/menus)
mw;dur=496            ← total middleware (sum of the above)
```

The crucial bit: **`rt.*` runs on every request**. The migration check, plugin-state load, site-info read, and hook resolution are independent SELECTs against D1, and EmDash doesn't parallelize many of them. They stack up.

On top of that, `render` (346 ms) is Astro's page render plus EmDash's content fetch plus Portable Text rendering, with several more D1 queries (post list, menus, media references) inside.

[Cloudflare D1](https://developers.cloudflare.com/d1/) looks like SQLite from your code, but it's actually **SQLite over the network** at the edge. Each query is a 5–15 ms round-trip, and a few dozen of those is your 500 ms.

The shift64 benchmark concludes:

> The fundamental issue stems from architecture — D1 database round-trips at the edge create an unavoidable latency floor that optimization cannot overcome without platform-level changes to Cloudflare's infrastructure.

**Without platform-level changes (i.e. Cloudflare reworking D1's locality model), application-level optimization can't get past this floor.**

## Mitigations that exist

Not full fixes, but partial relief:

1. **Set `Cache-Control: public, max-age=N` on public pages** → Cloudflare's CDN can cache and serve in single-digit milliseconds. Doesn't apply to authenticated admin / preview / dynamic APIs.
2. **D1 Sessions API (read replicas)** — set `session: "auto"` in `astro.config.mjs`. Helps if a replica is geographically near the request, otherwise marginal.
3. **Call `Astro.cache.set(cacheHint)` on pages** — listed as a project rule in EmDash's template `CLAUDE.md`.
4. **Deploy on Node.js + local SQLite (containers)** — drops to ~85 ms TTFB but you give up the Workers value proposition.

## Does the $5 plan fix it?

**No.** Workers Paid ($5/month) only changes CPU time limits (Free 10 ms → Paid 30 / 50 ms) and the billing model. It does not change the per-request count of D1 round-trips or the edge latency.

It helps for CPU-heavy workloads (image transforms etc.), but EmDash's request flow is I/O-dominated, so the user-facing TTFB barely moves.

## What about the EmDash project itself?

Searching [emdash-cms/emdash issues](https://github.com/emdash-cms/emdash/issues) for performance keywords turns up nothing dedicated. The team's stated focus is [plugin security and AI-agent-first](https://blog.cloudflare.com/emdash-wordpress/), not raw speed.

In other words, **there's no visible push for the kind of architectural rework that would actually fix this**.

## Verdict

| Expectation | Reality |
|---|---|
| It's Cloudflare's flagship CMS — must be fast | **Structurally 6× slower than WordPress** |
| Workers cold start is <5 ms — instant | Worker boots fast, but **D1 RPCs stack up** |
| Cache-Control will solve it | Only for the static slice; falls apart on admin / preview / dynamic |
| Upgrading to Workers Paid ($5) will help | **Unrelated**; only loosens CPU caps |

For now, **adopting EmDash for production is a meaningful risk**.

For hands-on, hobby, or experiments around AI-agent–driven CMS authoring, it's fine — that's the genuine strength here, and speed is secondary in those contexts.

For a traffic-bearing production site, holding off is the safer call. As you add features, lifecycle hooks, and plugins, **the trajectory is monotonically slower** — barring framework-level rework.

The realistic path forward is waiting for the EmDash team or Cloudflare to revisit D1's locality model (e.g. embedding a SQLite-shaped layer inside the application Worker itself).
