Handling PHP-FPM using Caddy
I've been using Caddy for years as a reverse proxy in front of other services, but recently wanted to use it to directly route traffic to PHP-FPM.
Caddy has a specialized reverse proxy directive for PHP-FPM, php_fastcgi
, which seemed like it would do the trick, but I found that no traffic was ever getting routed to my FPM pool.
What was happening
There's a fantastic StackOverflow answer describing what happens, and it's great reading.
The long and the short of it is that the php_fastcgi
directive looks for matching files on the disk, and in the case of paths that look like directories, an index.php
under that directory.
In other words, it cannot match arbitrary paths (aka pretty URLs), just files.
Which doesn't work with basically any fraį¹ework-based application; as the author of that SO answer posits, the directive was likely written to target Wordpress.
Additionally, this causes issues if the PHP-FPM web pool is using a different root directory than you're using in your Caddy server, as Caddy will look for the files in the root defined for the server, and return a 404 if it can't find it, without ever passing it on to PHP-FPM.
The solution
The solution is to use the standard reverse_proxy
directive, and have it use the fastcgi
transport.
You wrap this in a handle
directive so that you can also specify a different filesystem root to pass to PHP-FPM.
As an example, let's assume:
- I'm running PHP-FPM on port 9000 of the host "app".
- The filesystem root for my PHP application is in
/var/www/app.example.org/public
- I want to route all requests to
index.php
in that root so that they are handled by the application.
I can write the Caddyfile as follows:
app.example.org {
# Note that this is the filesystem root for the _server_
root * /static/app.example.org
file_server
handle {
# This is the filesystem root for the FPM pool
root * /var/www/app.example.org/public
rewrite index.php
reverse_proxy app:9000 {
transport fastcgi {
split .php
capture_stderr
}
}
}
}
If the file is not found in the filesystem, it will try to pass it on to PHP-FPM.
When it does, it rewrites to index.php
in the PHP-FPM root (which is a different root than Caddy uses for its own file server!); PHP-FPM will use the matched path as the PATH_INFO
, and your framework remains happy.