PHP-FPM has been the default way to run PHP since 2009. It works, it is battle-tested, and it is boring in the best sense. FrankenPHP changes the equation by embedding PHP directly into a Go-based application server (Caddy), adding worker mode that keeps PHP processes alive between requests, and providing automatic HTTPS, HTTP/3, and 103 Early Hints support without any Nginx or Apache configuration.
The performance gains are real. But so are the operational differences. This guide covers what FrankenPHP actually does, when it helps, and where php-fpm remains the safer choice.
What FrankenPHP actually is
FrankenPHP is a PHP SAPI (Server API) implemented as a Go module for the Caddy web server. In practical terms:
- It replaces Nginx + php-fpm with a single binary
- It serves static files and PHP simultaneously
- It handles TLS certificate provisioning automatically (via Caddy’s ACME integration)
- It supports HTTP/1.1, HTTP/2, and HTTP/3 out of the box
- It can run in standard mode (request-per-process, like php-fpm) or worker mode (persistent processes)
Architecture
1 | ┌─────────────┐ |
There is no separate web server process, no FastCGI socket, and no reverse proxy configuration. The PHP runtime runs inside the same process as the web server.
Installation
Binary download
1 | # Download the standalone binary (includes PHP) |
From Composer (for Laravel)
1 | composer require laravel/octane |
Laravel Octane provides a first-class integration that handles the worker mode lifecycle, including resetting application state between requests.
Standard mode (drop-in replacement)
In standard mode, FrankenPHP behaves like php-fpm. Each request starts a fresh PHP process, executes the script, and tears down. The advantage over Nginx + php-fpm is simplicity: one process, automatic HTTPS, no socket configuration.
1 | # Serve a PHP application |
1 | # Caddyfile |
That is the entire web server configuration. Caddy handles TLS certificates from Let’s Encrypt automatically.
Performance in standard mode
Standard mode performance is comparable to php-fpm. You are not gaining speed—you are gaining operational simplicity. One binary replaces three (Nginx, php-fpm, certbot).
Worker mode
Worker mode is where the performance gains happen. Instead of starting a new PHP process per request, FrankenPHP keeps PHP workers alive. The application boots once (framework bootstrap, service container, configuration loading) and then handles multiple requests without the boot overhead.
1 | // frankenphp-worker.php |
Performance in worker mode
For a typical Laravel application:
| Metric | php-fpm | FrankenPHP standard | FrankenPHP worker |
|---|---|---|---|
| Requests/sec | ~800 | ~850 | ~2,400 |
| Avg response time | 12ms | 11ms | 4ms |
| P99 response time | 45ms | 42ms | 15ms |
| Memory per worker | 40MB | 38MB | 55MB |
Worker mode roughly triples throughput because it eliminates the framework boot cost on every request. The tradeoff is higher per-worker memory usage (the application stays loaded).
The state persistence trap
This is the most important thing to understand about worker mode. Because the PHP process persists between requests, any global state leaks between requests:
1 | // This is a bug in worker mode |
What breaks:
- Static variables that accumulate
- Singletons that cache request-specific data
- Database connections that assume per-request lifecycle
- Global variables
$_SESSION,$_GET,$_POSTsuperglobals (must be reset)
What works fine:
- Laravel/Symfony with Octane (they reset state automatically)
- Stateless service classes
- Applications designed for long-running processes
103 Early Hints
FrankenPHP supports HTTP 103 Early Hints, which lets the server send preload directives before the response is ready:
1 | // Send early hints while the page is still rendering |
The browser starts downloading CSS and JS while PHP is still rendering the HTML. This can reduce perceived load time by 100-300ms on complex pages.
Docker deployment
1 | FROM dunglas/frankenphp:latest |
1 | { |
Common mistakes
Enabling worker mode without testing state leaks: Always run your test suite in worker mode, not just standard mode. Tests that pass per-request may fail when state persists.
Not configuring worker count: The default worker count may be too high for memory-constrained servers. Each worker holds the full application in memory. Set num based on available RAM.
Ignoring health checks: Worker mode processes can become stale. Configure health check endpoints that verify the application is responsive, not just that the process is running.
Treating FrankenPHP as a CDN replacement: It serves static files well but lacks edge caching. Use Cloudflare, Fastly, or another CDN in front of FrankenPHP for static assets.
When to choose FrankenPHP vs. alternatives
| Use case | FrankenPHP | php-fpm + Nginx | Swoole/RoadRunner |
|---|---|---|---|
| Simple deployment | ✅ | Moderate setup | Complex |
| Auto HTTPS | ✅ Built-in | Need certbot | Need reverse proxy |
| Worker mode performance | ✅ ~3x | Baseline | ✅ ~3-4x |
| Ecosystem maturity | Growing | Battle-tested | Moderate |
| PHP extension compat | Most work | All work | Some need patches |
| Shared hosting | No | Yes | No |
FAQ
Does FrankenPHP work with WordPress?
Yes, in standard mode. Worker mode requires modifications to WordPress’s global-state-heavy architecture that are not practical for most sites.
Can I use FrankenPHP with existing Nginx configs?
No. FrankenPHP replaces Nginx entirely. You would rewrite your server configuration as a Caddyfile. For complex Nginx configurations, this can be significant work.
Is FrankenPHP production-ready?
For standard mode, yes. Many production sites use it. Worker mode is stable for Laravel Octane and Symfony Runtime. Custom worker implementations need thorough testing.
Next steps
Install FrankenPHP locally and run your existing PHP application in standard mode. If it works without issues (it should), benchmark it against your current setup. Then evaluate whether worker mode’s performance gains justify the testing effort.
For understanding the performance fundamentals that FrankenPHP optimizes, the REST API best practices guide covers how API design interacts with server performance. The PHP 8.5/8.6 features guide covers language-level improvements that complement runtime optimizations.