04 php-fpm and Nginx
Goal
Replace php -S with the standard production pairing: php-fpm as the PHP process manager, nginx as the web server in front of it.
Prerequisites
How the Pieces Fit Together
- nginx — the web server. Handles the actual TCP connections, serves anything static, and forwards
.phprequests onward. - php-fpm (FastCGI Process Manager) — a pool of PHP worker processes. nginx hands each PHP request to a worker over the FastCGI protocol; the worker runs your script and hands the response back.
This is a real division of labor, not a formality: nginx is far better at handling thousands of slow/idle client connections than PHP would be, and php-fpm's worker pool means one slow request doesn't block the others — both problems php -S's single-threaded model has no answer for.
php-fpm Pool Config
php-fpm ships as part of the PHP install (php-fpm is a separate binary alongside php). Create php-fpm.conf:
[global]
daemonize = no
[www]
listen = 9000
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3pm = dynamic starts a small pool and grows it under load up to pm.max_children — a reasonable default; pm = static (a fixed worker count) is the alternative for predictable, capped memory usage.
Nginx Config
Create nginx.conf (or a site config under conf.d/):
server {
listen 80;
root /var/www/html;
index router.php;
location / {
try_files $uri /router.php$is_args$args;
}
location ~ \.php$ {
fastcgi_pass php:9000;
fastcgi_index router.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root/router.php;
fastcgi_param HTTP_AUTHORIZATION $http_authorization;
}
}Two things worth calling out:
try_files $uri /router.php$is_args$args;routes every request — regardless of path — torouter.php, the same "single entry point" shapephp -S router.phpgave you for free. Without this, nginx would 404 on any path that isn't a literal file on disk.fastcgi_param HTTP_AUTHORIZATION $http_authorization;is not boilerplate — it's a fix for a real, commonly-hit gap. Unlikephp -S(confirmed in 02 JWT Auth to populate$_SERVER['HTTP_AUTHORIZATION']automatically), nginx's standardfastcgi_paramsinclude does not forward theAuthorizationheader to PHP-FPM by default in many distributions' configs. Without this line, every bearer token silently vanishes before it reachesbearerToken()— auth would appear completely broken with no error anywhere in the stack, since PHP just sees an absent header. This is one of the most commonly reported "why doesn't my auth header work" issues across PHP deployment guides — explicit, not incidental.
Checkpoint
Validate the php-fpm config without starting nginx:
php-fpm -y php-fpm.conf -tExpected:
NOTICE: configuration file php-fpm.conf test is successfulFull request-serving verification (nginx + php-fpm actually answering curl) happens in 05 Docker, where both run as containers together — that's the natural point to bring the whole stack up rather than installing nginx locally just for this page.
Next
Continue to 05 Docker.