Early access Try the live demoDemo , opens in a new tab
Technology

Serving WordPress without PHP

A WordPress page view starts a PHP worker. The worker loads WordPress core, your theme and each active plugin, opens a connection to a MySQL server, runs the loop, and tears most of it down again. It does this for each request.

Your site is the content, the theme that renders it, and the admin you edit it with. BookPress keeps those three and replaces what runs underneath.

The serving path is a C++ rewrite from the ground up. The router, the block-theme renderer, the query layer and wp-admin are native code, not PHP. PHP runs only for your themes and plugins, on BookPress's own PHP engine. A site with no plugins does all of its work without running any PHP.

You can try one. The live demo is the Kadence theme with WooCommerce, Elementor, Yoast SEO, Contact Form 7, Yoast Duplicate Post and Hello Dolly all active, served by this engine on a Google Cloud e2-micro with no page cache in front of it. Your login appears when you start your sandbox. Write a post. The sandbox is deleted 15 minutes after you start it, so put nothing personal into it.

The engine

Real theme rendering

BookPress renders WordPress block themes natively. It reads the same template files, the same block markup and the same theme.json, and produces the HTML and CSS that WordPress produces: global styles, layout classes, font presets, fluid typography. Twenty Twenty-Five, the default WordPress theme, renders pixel-close today from its unmodified theme files, web fonts included.

The admin follows the same approach. /wp-login.php checks your existing WordPress password hashes. /wp-admin gives you the dashboard, post lists and editor, served by the same engine at the same speed.

What we compared: a hard test for BookPress

We take a real site: the Kadence theme with WooCommerce, a built Elementor showcase page, Yoast SEO and Contact Form 7. We migrate the identical content into both engines, with the same row counts in both databases. We tune the WordPress side as a competent host would before measuring it: PHP 8.3.35 with OPcache and the JIT enabled, a Redis object cache over a socket, MariaDB, and PHP-FPM behind nginx, with WooCommerce's newer order storage (HPOS) switched on. We time a page only after checking that WordPress and BookPress show the same page: the same Elementor markup, the same product page, the same shop catalogue.

WordPress is measured without a page cache, and we check it is off before timing, because a page a cache can serve is served from the cache by either setup and does not measure the engine. Every number below is WordPress running PHP, the case that a cart, a search or a logged-in session always hits.

The servers

We used two Google Cloud servers that anyone can rent and check. The Intel server is a 4-vCPU c2-standard-4 (Intel Xeon at 3.10 GHz, 2 physical cores with two threads each, 16 GB RAM). The ARM server is a 4-vCPU c4a-standard-4 (Google Axion, 4 physical cores, 16 GB RAM). Both run Debian 12, and BookPress is compiled the same way on both.

Read the two core counts before any number. "4 vCPU" is a different amount of hardware on each family, so we compare an ARM number only with a WordPress running on the same server.

The result: more requests a second, on both servers

We drove each engine to the most requests a second it serves with 99% of them under one second and none failing. On real store pages BookPress serves about 4.5 times the requests per second of the tuned WordPress on the Intel server and over 5 times on the ARM server. Every figure below is BookPress against WordPress on the same server.

PageServerRequests / secondp50 response timep99 response time
Elementor page Intel 4.53×162 against 36 rps 3.80× 1.93×
WooCommerce shop Intel 4.85×134 against 28 rps 4.75× 2.79×
Elementor page ARM 5.30×376 against 71 rps 5.30× 3.70×
WooCommerce shop ARM 5.19×297 against 57 rps 5.20× 2.50×

Measured on Google Cloud c2-standard-4 (Intel) and c4a-standard-4 (Arm), uncached dynamic pages, BookPress 0.2.64 (the packaged build you install) against WordPress 7.1.3 / PHP 8.3.35 with nginx, PHP-FPM, Redis object cache, OPcache with JIT and MariaDB.

Requests per second is each engine at its knee; p50 and p99 are the median and 99th-percentile response-time multiples at WordPress's knee rung, the same concurrency for both. WordPress is measured without a page cache.

Store pages are mostly the plugin's own PHP, so they are the hardest case. Unlike a plugin-free page, they cannot be answered without running that PHP. BookPress runs it on its own PHP engine, which on these pages keeps up with PHP 8.3.35 and the JIT, or beats it. These pages used to be the weak case, and BookPress now leads on them.

A site with no plugins runs no PHP interpreter at all. There BookPress serves 112.9 times the requests per second of WordPress on the Intel server (5,234 against 46) and 125 times on the ARM server (11,436 against 91), with no plugins installed.

Page caches are a separate layer

A page cache serves stored HTML without rendering anything, on WordPress or on BookPress. A cache hit therefore measures neither engine. Each figure on this site is an uncached dynamic render on both stacks.

Some classic PHP themes. WordPress's own classic templates are lighter than its block themes. BookPress's classic-theme rendering is newer than its block path, and a few of the two-dozen bundled classic themes still fall back to an error today. The fix is on our roadmap. The store pages above are block themes.

Memory

Memory is a separate, earlier measurement, taken on BookPress 0.2.57. We measured it on both servers after a full load run, on the same site with all plugins active, counting memory shared between processes once.

Under load, four BookPress workers used about 0.6 GB. That is about the same as a right-sized WordPress (8 PHP children, 0.55 GB), which serves fewer pages, and a third of a tuned 64-child pool (1.8 GB). Thirty seconds after the load stopped, BookPress dropped to about 0.3 GB. WordPress held 0.43 GB (8 children) or 1.8 GB (64 children).

Workers load the engine code and plugin bytecode once and share it. Each additional worker adds its own working set under load and returns most of it when idle. A worker with no request for 30 seconds gives memory back to the system, so the idle figure falls. A WordPress pool holds what it preforked, busy or idle. The memory figures were taken on BookPress 0.2.57, 13 October 2026; the throughput run above is a later and separate measurement.

No object cache, no database server, no page cache

WordPress hosts assemble these tiers to keep PHP from doing work twice. Each object-cache read is a round trip to Redis and a decode in PHP. A database server is another round trip. A page cache stores a rendered copy so nginx can answer without entering the stack.

BookPress has none of them. Storage is embedded and in-process, so there is no round trip to save. There is no rendered copy to keep, because rendering the page from scratch is the fast path. A CDN is still worth having for the usual reasons: distance to your visitors, and static assets.

Each tier can be misconfigured, run out of memory, hold stale content, or need purging after a deploy. BookPress has none of these moving parts. That matters for operations first and for speed second.

One result goes against us. If a host already runs a Redis object cache, we leave it installed. Disabling it to improve our numbers would be dishonest. Our plugin engine activates the plugin and the site serves, but the plugin cannot do its job: BookPress's PHP engine does not provide the Redis client or the low-level network calls it needs. The plugin falls back to WordPress's per-request cache and stores nothing across requests. Activating any plugin means the site runs PHP for every request, so an active object cache makes a plugin-free page much slower and gives nothing in return. BookPress cannot run this plugin's function. That differs from not needing one. Do not install an object cache on BookPress. It costs you and gives you nothing.

What a page cache does, and what it cannot do

With a page cache in front of it, WordPress served tens of thousands of requests a second in this benchmark, far more than BookPress. The number is real. It is also nginx returning a file from disk, with no PHP running.

A page cache stops where its permission stops. It does not serve a cart, a checkout, an account page, anything carrying a session cookie, anything with a query string, or anything for a logged-in visitor. Those pages decide a store's capacity. On them the cache steps aside and the request reaches the PHP it stood in front of. Every figure on this page measures that case.

The WordPress we compared against, in full

It is WordPress 7.1.3 on PHP 8.3.35-FPM, tuned as a competent host tunes one: OPcache sized so nothing is evicted with the JIT on, a warm Redis object cache over a socket, MariaDB with a healthy buffer pool, and WooCommerce on its newer order storage (HPOS). We ran it in two pools so pool size does not skew the comparison: a tuned static 64 children pool and a right-sized dynamic, 8 children pool.

Exact configuration
  • OPcache with the JIT on. 512 MB of compiled code, a 64 MB interned-strings buffer, a 100,000-file limit, timestamp validation off, and a 256 MB JIT buffer.
  • A Redis object cache (Redis 7) over a UNIX socket, with the object-cache drop-in installed and the cache warm.
  • MariaDB 11 with a 4 GB InnoDB buffer pool.
  • PHP-FPM behind nginx 1.27. A static 64 children pool and a right-sized dynamic, 8 children pool, both measured.
  • WooCommerce on HPOS, its newer order storage.

The plugins are the demo's own versions: WooCommerce 11.0.1, Elementor 4.2.3, Yoast SEO 28.3 and Contact Form 7 6.1.7, on the Kadence 1.5.2 theme. We time a page only after checking that WordPress and BookPress show the same page: the Elementor showcase, the product page and the shop catalogue, checked on both servers.

The BookPress configuration, to the same standard

bookpress --root /srv/sites/<site> --db /srv/sites/<site>/data/site.db \
          --port <port> --workers 4

BookPress runs with no php.ini, no PHP-FPM pool, no object cache, no opcode cache, no page cache and no reverse proxy. Each BookPress number on this page is a page rendered from scratch.

Which server to rent

Rent the Arm server: four real cores where the Intel server has two, about 14 percent less on list price, and about 2.2 to 2.3 times the Elementor and WooCommerce requests per second on BookPress 0.2.64.

List price, Google Cloud us-central1 on-demand: c4a-standard-4 $131.11 a month against c2-standard-4 $152.43.

Hosting economics

A host sizes a server by memory. Under full load the same site took about 0.6 GB on four BookPress workers (measured on 0.2.57), against 0.55 GB for a right-sized WordPress and 1.8 GB for a tuned 64-child pool. When idle, BookPress gives memory back and the WordPress pools do not. We are measuring how many sites one machine carries.

We work out prices from the infrastructure a host stops paying for, which is what the numbers on this page measure. They run from $1.00€0.85 per site per month at Starter down to $0.45€0.38 at Agency, and per licensed vCPU above 500 sites. The pricing page lists the tiers and shows how the figure is worked out for your own fleet.

What that means if you manage many sites →