EmDash CMS od Cloudflare

Zainstalowałem EmDash CMS (wersja 0.1) od Cloudflare na Workers i sprawdziłem co dostaje

2 kwietnia 2026 Cloudflare ogłosił EmDash – open-source’owy CMS napisany w TypeScript, który ma być „duchowym następcą WordPressa”. Obietnice: sandboxowane pluginy, Portable Text zamiast HTML, wbudowany MCP server dla agentów AI, serverless na Cloudflare Workers, licencja MIT.

Zamiast czytać o tym co EmDash ma robić, zainstalowałem go na Cloudflare Workers i sprawdziłem, co faktycznie robi. Ten tekst to nie recenzja — to raport z instalacji i audyt SEO HTML-a który CMS generuje out of the box.

Co to jest EmDash i dlaczego mnie to obchodzi

EmDash to CMS zbudowany na Astro 6, napisany w TypeScript, zaprojektowany do pracy na Cloudflare Workers (D1 jako baza, R2 jako storage). Nie wykorzystuje ani linijki kodu WordPressa – stąd licencja MIT zamiast GPL.

Z perspektywy SEO trzy rzeczy przyciągnęły moją uwagę:

  • Pierwsza – architektura SSR na edge. Astro renderuje pełny HTML po stronie serwera, Cloudflare Workers serwuje go z najbliższego PoP. Żadnego client-side renderingu, żadnego hydration problemu, żadnego „chwila, muszę załadować JS, żeby pokazać content”. Googlebot dostaje gotowy HTML.
  • Druga – Portable Text. EmDash nie przechowuje contentu jako HTML w bazie (jak WordPress). Przechowuje go jako strukturalny JSON. To zmienia reguły gry jeśli chodzi o content portability i parsowanie treści przez AI agenty, ale o tym niżej.
  • Trzecia – x402. EmDash ma wbudowaną obsługę HTTP 402 Payment Required – mechanizm mikropłatności za content, projektowany z myślą o AI agentach, które pobierają treści. Żaden inny CMS tego nie ma natywnie.

Instalacja – dokumentacja vs rzeczywistość

Co naprawdę się dzieje, kiedy ktoś spoza zespołu Cloudflare próbuje postawić EmDash na Workers. Dokumentacja tej ścieżki praktycznie nie opisuje.

Node.js: wersja 18 nie wystarczy

Repo mówi „Node.js”. Nie mówi, która wersja. better-sqlite3 (zależność do lokalnego demo) wymaga kompilacji natywnej C++ i szuka Visual Studio Build Tools na Windowsie. Nie masz VS Build Tools? pnpm install pada.

Obejście: dodanie neverBuiltDependencies w package.json dla better-sqlite3. Ale to dopiero początek — sam build wymaga Node 20+, bo rolldown (bundler) używa styleText z node:util, które pojawiło się w Node 20.12. Na Node 18 dostajesz kryptyczny SyntaxError: The requested module 'node:util' does not provide an export named 'styleText'.

Nic nigdzie o tym w README.

Dynamic Workers: płatny plan wymagany

Deploy na Workers przechodzi gładko… do momentu, gdy wrangler zwraca:

In order to use Dynamic Workers, you must switch to a paid plan
Code language: JavaScript (javascript)

Dynamic Workers to mechanizm izolacji pluginów – kluczowa feature EmDash. Wymaga Workers Paid ($5/mies.). To nie jest problem finansowy tylko komunikacyjny. Cloudflare reklamuje EmDash jako free i open source, ale kluczowa funkcja wymaga płatnego konta. Na lokalnym Node.js sandbox pluginów w ogóle nie działa.

Route hardcoded na demo.emdashcms.com

Po pierwszym deployu wrangler zwraca error:

Could not find zone for 'demo.emdashcms.com'
Code language: JavaScript (javascript)

Konfiguracja wrangler.json generowana przy buildzie zawiera route do demo.emdashcms.com – domeny zespołu Cloudflare. Trzeba ręcznie usunąć sekcję routes i dodać "workers_dev": true. Ale plik generuje się przy każdym buildzie z wrangler.jsonc, więc poprawkę trzeba zrobić w źródle.

Auth: hardcoded pod wewnętrzny zespół Cloudflare

To jest najpoważniejszy problem. W astro.config.mjs demo Cloudflare:

auth: access({
    teamDomain: "cloudflare-cto.cloudflareaccess.com",
Code language: CSS (css)

Autoryzacja jest skonfigurowana pod Cloudflare Access zespołu CTO Cloudflare. Nikt spoza tej organizacji nie przejdzie. Efekt: panel admina się ładuje (to statyczny SPA), ale każdy request API zwraca 401 Unauthorized. Dashboard wygląda jakby działał, ale jest „martwy”.

Rozwiązanie: usunięcie providera access() z konfiguracji. Oczywiście nigdzie nie jest to udokumentowane.

Seed: brak ścieżki do inicjalizacji zdalnej D1

emdash seed działa lokalnie z SQLite. Na Cloudflare z D1? Brak udokumentowanej metody. Endpoint /_emdash/api/seed istnieje, ale zwraca CSRF error nawet z przeglądarki. Header, którego wymaga to X-EmDash-Request: 1 – znalazłem go dopiero analizując kod inline editing toolbara w wygenerowanym HTML.

Ostatecznie seed zadziałał po: usunięciu Cloudflare Access, resecie bazy D1, redeployu i wejściu na /admin – setup wizard odpalił się na czystej bazie.

Podsumowanie instalacji

Czas od git clone do działającego site’a: ~3 godziny z debugowaniem. WordPress: 5 minut. To v0.1 – ale różnica jest realna. Problem nie w tym, że EmDash CMS jest wczesny. Problem w tym, że demo Cloudflare jest skonfigurowane pod ich wewnętrzną infrastrukturę i nikt nie przetestował ścieżki „zewnętrzny developer (na Windowsie) chce to odpalić”.

Rzut oka na SEO: co generuje EmDash out of the box

Mam działającą instancję na emdash-demo.qbeczek.workers.dev. Sprawdziłem HTML kilku postów – zarówno nowo utworzonego (puste pola SEO) jak i zaseedowanego (wypełnione pola).

„EmDash generuje lepszy HTML out of the box niż WordPress bez pluginów i to nie jest komplement dla EmDash, tylko komentarz o tym jak nisko ustawił poprzeczkę WordPress przez 24 lata. OG tagi, canonical, JSON-LD natywnie, bez Yoasta to powinien być standard, nie feature.”

Jakub Sawa, SEO ekspert, fratreSEO.com

Co jest i działa poprawnie

Rendering. Pełny SSR. curl na URL posta zwraca kompletny HTML z contentem. Zero JavaScript-dependency do wyświetlenia treści. Googlebot dostaje to samo co przeglądarka. Tu EmDash jest wzorowy.

Meta tagi. Gdy pola SEO są wypełnione, EmDash generuje kompletny zestaw:

  • <title> w formacie {tytuł posta} | {nazwa serwisu} (na mojej wersji nie działa nadpisanie SEO title)
  • <meta name="description"> – z pola excerpt/SEO description
  • <link rel="canonical"> – pełny URL, poprawny
  • Open Graph: og:type, og:title, og:description, og:image, og:url, og:site_name, article:published_time, article:modified_time
  • Twitter Cards: twitter:card (automatyczny upgrade z summary na summary_large_image gdy jest obrazek), twitter:title, twitter:description, twitter:image

Ważne: gdy pola SEO są puste – EmDash nie wstawia pustych tagów. To poprawne zachowanie. Brak <meta name="description"> w moim testowym poście to nie bug CMS-a, tylko puste pole.

Structured Data. JSON-LD BlogPosting z headline, description, image, url, datePublished, dateModified, publisher (Organization), mainEntityOfPage. Schema jest osadzona w <head>, nie generowana przez JS.

Semantyczny HTML. <article>, <header>, <main>, <nav>, <aside>, <footer>, <time> z atrybutem datetime. Komentarze renderowane server-side – crawlowalne od razu.

URL structure. Czyste slugi: /posts/notes-on-simplicity. Bez trailing slash. Kategorie: /category/design. Tagi: /tag/webdev. Konfigurowalne przez Astro routing.

Taksonomie. Coś, z czym goły WP ma problem, tutaj jest out of the box.

Inne. RSS feed (/rss.xml linkowany w footerze). Sitemap deklarowana w robots.txt. Lazy loading na obrazkach (loading="lazy" + decoding="async"). Wbudowany live search z fallbackiem na /search?q=. Responsive images z aspect-ratio i max-width: 100%.

Czego brakuje

Schema author. W HTML jest byline („Guest Contributor”), ale w JSON-LD BlogPosting nie ma property author. Google wymaga author dla eligibility do Top Stories i artykułowych rich results. To jest duży brak.

Schema articleSection. Kategorie istnieją w HTML (sidebar), ale nie trafiają do structured data.

Breadcrumbs. Brak – ani w HTML, ani w Schema.

og:locale / hreflang. Config Astro ma zdefiniowane locales (en, fr, es z fallbackiem), ale zero outputu w <head>. Ani og:locale, ani <link rel="alternate" hreflang>.

Kontrola robots per-post. Brak <meta name="robots">. Domyślnie index/follow – OK, jest możliwość ustawienia w panelu noindex na konkretnym poście, ale u mnie nie zadziałało.

Heading hierarchy w sidebarze. Widgety (Search, Categories, Tags) używają <h3> bez parent <h2>. Między <h1> tytułu posta a <h3> widgetów jest dziura w hierarchii. Drobnostka, ale Lighthouse zgłasza.

Tabela porównawcza: EmDash vs WordPress

FeatureEmDash v0.1WordPress 6.x
SSR / pełny HTMLTak (Astro)Tak (PHP)
Meta descriptionTak (gdy wypełnione)Wymaga pluginu (Yoast/RankMath)
CanonicalTakTak (od WP 5.x)
Open GraphTak, kompletWymaga pluginu
Twitter CardsTak, z auto-upgradeWymaga pluginu
JSON-LD SchemaBlogPosting (bez author)Wymaga pluginu
SitemapTak (robots.txt)Tak (od WP 5.5)
robots per-postTak (nie działa)Wymaga pluginu
hreflangNie (mimo config)Wymaga pluginu
Breadcrumbs schemaNieWymaga pluginu
RSSTakTak
Inline editingTak (toolbar na froncie)Tak (Gutenberg)
Heading hierarchyProblematyczna w sidebarZależy od motywu


Wniosek: EmDash OOTB daje więcej niż goły WordPress bez pluginów. Meta tagi, OG, Twitter Cards, JSON-LD – to wszystko w WordPressie wymaga Yoasta lub RankMath. EmDash ma to natywnie. Ale brakuje fundamentów (author w schema, hreflang, breadcrumbs) które pluginy WP dawno rozwiązały.

Co interesujące z perspektywy AEO

Trzy rzeczy w EmDash nie mają odpowiednika w żadnym innym CMS-ie:

  • Portable Text.
    Content przechowywany jako JSON, nie HTML. To znaczy że AI agent parsujący treść EmDash nie musi interpretować DOM-a – dostaje strukturalny format z typowanymi blokami (paragraph, heading, image, code). Dla RAG pipelines to ogromna różnica między „wyciągnij tekst z HTML” a „przeczytaj JSON”. Konsekwencja: content łatwiej migrować, łatwiej transformować, łatwiej serwować w różnych formatach.
  • MCP Server.
    Każda instancja EmDash eksponuje Model Context Protocol server. Podłączasz Claude Desktop, Cursor albo dowolnego klienta MCP i zarządzasz contentem przez AI. Tworzenie postów, migracje, bulk operations – programowo, przez agenta. To nie jest feature dla redaktora. To jest feature dla developera, który buduje pipeline content automation.
  • x402 Payment Required.
    Natywna obsługa mikropłatności za content. AI agent requestuje stronę, dostaje 402 z ceną, płaci stablecoinem, dostaje content. Bez subskrypcji, bez formularzy. W świecie gdzie 60% ruchu wkrótce mogą generować AI agenty – to jest ciekawy business model. Pytanie, czy ktokolwiek będzie z tego korzystał w v0.1. Na razie czysta spekulacja.

Werdykt: dla kogo jest EmDash dzisiaj

Deweloperzy na Cloudflarestack, którzyy budują nowe projekty i chcą CMS z SSR, edge deployment i TypeScript end-to-end. Dla nich warto postawić demo, pobawić się, obserwować development.

Agencje SEO i content teamy: za wcześnie. Zero ekosystemu pluginów. Brak marketplace’u z motywami. Brak community. Jeden oficjalny motyw (blog). Problemy z seedem, auth, dokumentacją. To nie jest tool do produkcji dzisiaj.

SEO consultanci: warto znać architekturę. Klienci zaczną pytać. Architektura EmDash (SSR na edge, structured content, sandboxowane pluginy) to kierunek w którym idzie cała branża CMS. Nawet jeśli EmDash sam nie wygra, pattern który ustanawia jest istotny.

Matt Mullenweg skomentował: EmDashpowstał, żeby sprzedawać więcej usług Cloudflare. I ma rację – D1, R2, Workers, Dynamic Workers, Images, Stream – każda warstwa EmDash to binding do płatnego produktu CF. Ale to nie dyskwalifikuje architektury. WordPress też powstał w ekosystemie firm hostingowych, które na nim zarabiają.

Wersja 0.1. Dużo obietnic, trochę realnego kodu, sporo ostrych krawędzi. Ale HTML, który generuje jest solidny i to mówi coś o priorytetach zespołu.

Artykuł oparty na hands-on instalacji EmDash v0.1.0 na Cloudflare Workers, 4 kwietnia 2026. Instancja testowa: emdash-demo.qbeczek.workers.dev.

Podobne wpisy