Can FrankenPHP Classic Mode Really Outperform PHP‑FPM? A Deep Benchmark
This article benchmarks FrankenPHP classic mode against PHP‑FPM on a Hetzner VPS using Vegeta, measuring request‑per‑second and latency across HTML, PDF, random data and high‑concurrency scenarios, and finds only marginal differences that rarely justify switching runtimes.
Overview
FrankenPHP markets itself as a “modern PHP application server” offering a single‑process runtime (Worker Mode) that reduces framework initialization time. However, many applications rely on global state, making the worker mode unsuitable in some cases. This article evaluates whether FrankenPHP’s classic mode can also improve performance compared with traditional PHP‑FPM.
Test Environment Setup
All tests run on a Hetzner VPS with 8 dedicated vCores (AMD EPYC 7003/9004) and Debian 13 ("Trixie"). The software stack includes:
FrankenPHP v1.9.1 (PHP 8.4.12) with Caddy v2.10.2
nginx 1.26.3 with PHP‑FPM 8.4.11
Vegeta v12.12.0 as the load generator
FrankenPHP was installed via the curl | sh binary installer; nginx and PHP‑FPM were installed from the official Debian repositories. Default configurations were used for both setups, with only minimal port adjustments for nginx.
HTML Response Generation Test
A typical server‑side rendered login page (~50 KiB) is used as the baseline scenario. The test script:
<?php
header('content-type: text/html; charset=utf-8');
$str = str_repeat('x', 1023) . "
";
for ($i = 0; $i < 50; $i++) {
echo $str;
}Results (60 s, 8 clients, 1 worker per core):
Response Type | FPM RPS | FrankenPHP Classic RPS | FPM 99% ms | FrankenPHP 99% ms
HTML | 7023.11 | 6934.06 | 2.022 | 2.06nginx+FPM processes 7023 RPS (slightly lower latency) while FrankenPHP handles 6934 RPS, a 1.3 % difference that is negligible in practice.
Binary Response Generation Test
The same script is modified to emit application/pdf instead of HTML:
<?php
header('content-type: application/pdf');
$str = str_repeat('x', 1023) . "
";
for ($i = 0; $i < 50; $i++) {
echo $str;
}Results:
Response Type | FPM RPS | FrankenPHP Classic RPS | FPM 99% ms | FrankenPHP 99% ms
PDF | 7610.31 | 5368.85 | 1.828 | 4.246Switching the Content‑Type to PDF improves FPM latency but slows FrankenPHP considerably.
Random Data (Base64‑Encoded) Test
Random data is generated to simulate encrypted payloads:
<?php
$random = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar());
for ($i = 0; $i < 50; $i++) {
echo $random->getBytesFromString("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 1023), "
";
}Results:
Response Type | FPM RPS | FrankenPHP Classic RPS | FPM 99% ms | FrankenPHP 99% ms
Random data | 1940.05 | 5606.22 | 7.704 | 3.143Both runtimes slow down due to heavier processing, but FrankenPHP delivers roughly 2.9× higher throughput.
Minimal "Hello World" Test
<?php
echo "Hello World!"; Response Type | FPM RPS | FrankenPHP Classic RPS | FPM 99% ms | FrankenPHP 99% ms
Hello World | 18478.69 | 18403.81 | 0.904 | 0.877The difference shrinks to 0.4 % with virtually identical latency.
High‑Concurrency Test
Using the same "Hello World" script, concurrency is increased to 100 workers per core:
Response Type | FPM RPS | FrankenPHP Classic RPS | FPM 99% ms | FrankenPHP 99% ms
Hello World 100 | 21847.61 | 22675.24 | 9.742 | 23.365FrankenPHP gains about 3.7 % in RPS but suffers from higher latency distribution.
Optimized Configuration Test
Default settings were tuned:
Disable nginx access logs (to match FrankenPHP).
Enable fastcgi_keep_conn in nginx.
Set PHP‑FPM to pm=static and pm.max_children=16, mirroring FrankenPHP’s num_threads.
Even after these tweaks, performance differences remained minimal.
Impact of Output Compression
Vegeta sent accept-encoding: gzip. Both nginx and FrankenPHP automatically compress responses when appropriate. Compression behavior per response type:
Compressed? | FrankenPHP | nginx
HTML | Yes | Yes
PDF | No | No
Random data | Yes | Yes
Hello World | No | YesDisabling compression ( accept-encoding: identity) yields:
Response Type | FPM RPS | FrankenPHP Classic RPS | FPM 99% ms | FrankenPHP 99% ms
HTML (no GZIP) | 7456.47 | 5504.16 | 1.786 | 3.993Without gzip, FrankenPHP’s RPS drops further, confirming that compression quality, not runtime overhead, drives many observed differences.
Conclusion
PHP‑FPM and FrankenPHP classic mode exhibit virtually identical runtime overhead; the marginal performance gaps are insufficient to mandate a switch. Architecture choices (e.g., using Caddy vs. nginx, enabling compression) have a far greater impact than the PHP runtime itself. Teams should select the runtime based on operational familiarity and feature requirements such as Early Hints, Go extensions, or Mercure support.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Open Source Tech Hub
Sharing cutting-edge internet technologies and practical AI resources.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
