The usual answer to "can WordPress run on Cloudflare?" is no: Workers run JavaScript and WebAssembly, WordPress needs PHP and MySQL, so you either export a static copy or keep a server. That answer is out of date. The WordPress Playground project builds PHP for WebAssembly, and that build runs inside a Worker.
We maintain HostlessWP, an open source tool (GPL-2.0) that uses it to move an existing WordPress site onto Workers: npx hostlesswp reads wp-config.php, runs WordPress core, the theme and the plugins in php-wasm (PHP 8.3), reaches the site's existing MySQL database through Hyperdrive, and puts the media library in R2 or any S3-compatible bucket. These are the things that decided whether it was usable, with the numbers we measured.
1. Placement matters more than anything else
WordPress sends its queries one after another. A core home page made 29 queries in our tests, a WooCommerce home page 74. Every one of them waits for a round trip to the database.
With the database in US East (Virginia) and requests entering Cloudflare in Singapore, a Worker with default placement runs near the visitor, so every query crosses the Pacific:
| Configuration | Per query | Core home page | WooCommerce home page |
|---|---|---|---|
| Worker near the visitor (Singapore) | ~230 ms | 7.7 s | 19.6 s |
Worker next to the database (placement.region = "aws:us-east-1") |
~33 ms | 1.2 to 1.3 s | 2.9 to 3.2 s |
| Next to the database, direct TCP instead of Hyperdrive (not shipped) | ~2 ms | 0.07 s | 0.19 s |
PHP processing time per page, warm requests, including database waits; medians of 1 to 5 samples per cell, so read them as orders of magnitude. Pinning the Worker next to the database made pages about six times faster, which is why the tool looks up where the database is and sets placement.region automatically.
Pages for logged-out visitors come from a page cache in Workers KV and do not start PHP at all, so these numbers are what logged-in users, the admin and uncached pages see.
2. Hyperdrive adds about 30 ms per query, and it does not keep sessions
Inside the same Worker invocation, a direct SELECT 1 took about 2 ms and opening the connection 2 to 7 ms, against about 33 ms through Hyperdrive. We could not tell how much of that is the distance to Hyperdrive's pool and how much is its own processing. The direct path is not shipped yet because the test database used a private certificate authority that the Workers TLS client does not accept.
The less obvious part: Hyperdrive may run consecutive statements on different server connections. Anything WordPress or a plugin sets with SET for the session can be gone on the next query. We carry session state per statement with SET_VAR hints, answer SELECT @@var from SHOW VARIABLES, and run SQL_CALC_FOUND_ROWS paging as a parallel COUNT. That last change took the REST posts list from about 2.5 s to 1.6 s.
3. The clock does not move while you compute
Workers freeze the clock during CPU work. PHP code that loops until the time changes, uniqid() for example, spins forever. The fix is a clock that is strictly monotonic: every read returns a slightly later time than the previous one.
4. OPcache is off in php-wasm unless you turn it on
php-wasm's OPcache does not start by default because its SAPI is not on OPcache's whitelist. Patching that, putting OPcache in shared memory and mounting precompiled scripts makes a large difference for a code base the size of WordPress plus WooCommerce.
5. What does not work yet
- PHP
mail(). Use a mail plugin that sends over an HTTP API. - Installing or updating plugins from the admin. You change them on the source site and redeploy.
- Multisite.
- Object cache drop-ins such as Redis are left out; WordPress uses its built-in cache.
- Imagick; images are processed with GD.
- Placement next to the database caused more cold starts (11 to 16 in 25 sequential requests, against 0 with default placement). We do not know why yet.
The 30 most-installed plugins on WordPress.org loaded and worked at page level, and WooCommerce checkout, the Elementor editor and Yoast sitemaps were tested end to end. The per-plugin results are in the compatibility table.
Try it
npx hostlesswp check # read-only compatibility report, changes nothing
npx hostlesswp # guided setup and deploy
Your current site is not changed, and it keeps serving until you move the domain. There is a public WooCommerce demo running this way at https://hostlesswp-demo.qc2uyes.workers.dev (log in with demo / demo, it resets every 3 hours). Full method and numbers: https://hostlesswp.com/benchmarks
It is an early preview (v0.1). If you run it on your own site, we would like to see your numbers.
Top comments (0)