This episode covers PHP integration: the php_fastcgi directive, connections through Unix sockets and TCP, PHP-FPM setup, WordPress, Laravel, and Symfony patterns with try_files, and OPcache and FPM pool optimization.

PHP still powers most of the web — WordPress, Laravel, and hundreds of other frameworks. For PHP to run efficiently, applications talk to the server through PHP-FPM, and the web server acts as a FastCGI client. Episode 24 covers this integration in Caddy.
You'll learn the php_fastcgi directive, connections via Unix socket or TCP, PHP-FPM setup with correct pools, configuration patterns for WordPress, Laravel, and Symfony, and performance optimization with OPcache.
Caddy handles PHP-FPM natively — no extra plugins or modules needed. This is one of the advantages that makes Caddy comfortable for PHP applications.
The php_fastcgi directive connects to PHP-FPM and routes requests to it:
example.com {
root * /var/www
php_fastcgi unix//run/php/php-fpm.sock
file_server
}php_fastcgi unix//run/php/php-fpm.sock uses a Unix socket — the fastest connection because there's no network involved. Caddy sends the requested PHP file to FPM and displays the result.
If PHP-FPM is on another host or container:
example.com {
root * /var/www
php_fastcgi 127.0.0.1:9000
file_server
}php_fastcgi 127.0.0.1:9000 connects over TCP to FPM on port 9000. Choose TCP when PHP-FPM and Caddy are separated — for example, in different containers (episode 27).
Make sure PHP-FPM is installed:
sudo apt install php-fpmPool configuration lives at /etc/php/*/fpm/pool.d/www.conf. A few important values:
user = www-data
listen = /run/php/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 20listen must match what Caddy uses.listen.mode gives read-write permission on the socket.pm and max_children control the process count — covered in the optimization section.The most common error: the socket can't be accessed. Make sure the Caddy user can read the socket. If Caddy runs as the caddy user and FPM as www-data, set listen.owner and listen.group correctly, then restart FPM.
WordPress needs its index.php and clean permalinks:
example.com {
root * /var/www/wordpress
php_fastcgi unix//run/php/php-fpm.sock
try_files {path} /index.php?{query}
file_server
}try_files {path} /index.php?{query} routes WordPress permalinks to index.php — the standard pattern for pretty permalink structures.
Laravel and Symfony use a front controller:
example.com {
root * /var/www/laravel/public
php_fastcgi unix//run/php/php-fpm.sock {
env LARAVEL_ENV production
}
try_files {path} /index.php?{query}
file_server
}A key detail: root points to Laravel's public folder, not the project root. php_fastcgi with a block can send environment variables:
example.com {
root * /var/www/laravel/public
php_fastcgi unix//run/php/php-fpm.sock {
env APP_ENV production
env DB_HOST localhost
}
try_files {path} /index.php?{query}
file_server
}env APP_ENV production passes the environment variable to the PHP process. For secrets, use system environment variables set by the service manager, not values written in the Caddyfile.
OPcache stores compiled PHP bytecode in memory:
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 10000
opcache.validate_timestamps = 0opcache.validate_timestamps = 0 disables file-change checks — production only, because it requires a PHP reload after code updates. Without OPcache, every request recompiles the entire framework.
The pm mode determines the process count:
pm.max_children is the process ceiling. Set it according to RAM: roughly 30 to 50 MB per PHP process. Don't exceed the server's memory capacity.
If PHP pages are blank or return error 502:
sudo systemctl status php-fpm
tail -f /var/log/php-fpm.logtail -f /var/log/php-fpm.log streams FPM's error log live. A 502 Gateway error almost always means Caddy can't talk to FPM — check the socket, permissions, and whether FPM is running.
Episode 24 opened up PHP-FPM integration: the php_fastcgi directive with Unix socket or TCP, FPM pool setup with correct socket permissions, WordPress, Laravel, and Symfony patterns with try_files and a front controller, and OPcache and process management optimization.
Key takeaways:
php_fastcgi unix//run/php/php-fpm.sock for local connections.public folder.try_files {path} /index.php?{query} for front controllers.env in php_fastcgi forwards variables to PHP.In the next episode, episode 25, we'll cover WebSocket & real-time applications — automatic WebSocket detection, reverse proxying upgraded connections, applications like Socket.io and real-time chat, timeout troubleshooting, and load balancer considerations for long-lived connections.