When containerizing WordPress applications, many developers instinctively pair PHP-FPM with Nginx/Apache. However, this creates unnecessary complexity when you want strict separation of concerns between containers. The fundamental question is: Can PHP-FPM handle static file delivery natively?
Out of the box, PHP-FPM is configured to process only .php files through its security.limit_extensions directive. This means requests for static assets (CSS, JS, images) typically return 404 errors when hitting PHP-FPM directly.
; Default php-fpm.conf snippet
security.limit_extensions = .php .php3 .php4 .php5 .php7
To enable static file serving, modify your php-fpm.conf:
; Allow processing of static files
security.limit_extensions = .php .html .css .js .png .jpg .jpeg .gif .svg
; Optional: Set proper MIME types
default_mimetype = "text/html"
Combine this with a docker-compose.yml that excludes Nginx:
version: '3'
services:
php-fpm:
image: php:8.2-fpm
volumes:
- ./wordpress:/var/www/html
ports:
- "9000:9000"
reverse-proxy:
image: nginx
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
While technically possible, serving static files through PHP-FPM has tradeoffs:
- Pros: Simplified architecture, no shared volumes needed
- Cons: Higher memory usage, no built-in caching
For better performance, configure your reverse proxy to cache static content:
# nginx.conf snippet
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
proxy_pass http://php-fpm:9000;
proxy_cache static_cache;
proxy_cache_valid 200 1d;
expires max;
}
For development environments, consider PHP's built-in server with router script:
// router.php
if (preg_match('/\.(?:png|jpg|jpeg|gif|css|js)$/', $_SERVER["REQUEST_URI"])) {
return false; // serve the requested resource as-is
} else {
include __DIR__ . '/index.php';
}
Then run:
php -S 0.0.0.0:8080 router.php
When containerizing WordPress, many developers instinctively reach for Nginx or Apache to handle static files while using PHP-FPM exclusively for PHP processing. But what if we want PHP-FPM to handle everything?
This architecture offers several advantages:
- Simplified containerization (single service container)
- Clear separation between reverse proxy and application
- No shared volumes between containers
- Consistent request handling path
Edit your www.conf (typically in /etc/php/{version}/fpm/pool.d/):
[www] ... ; Enable static file serving security.limit_extensions = .php .html .htm .css .js .jpg .jpeg .png .gif .ico
For WordPress, you'll need to modify the .htaccess equivalent behavior. Create a router.php:
<?php
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$ext = pathinfo($path, PATHINFO_EXTENSION);
// Static files check
if ($ext && !in_array($ext, ['php', 'phtml', 'phar'])) {
if (file_exists(__DIR__ . $path)) {
$mime_types = [
'css' => 'text/css',
'js' => 'application/javascript',
'png' => 'image/png',
// Add other mime types
];
header('Content-Type: ' . ($mime_types[$ext] ?? mime_content_type(__DIR__ . $path)));
readfile(__DIR__ . $path);
exit;
}
}
// Fall back to WordPress
include __DIR__ . '/index.php';
While functional, this approach has tradeoffs:
- PHP-FPM isn't optimized for static file serving
- No native sendfile support
- Memory overhead per request
The reverse proxy (Nginx) in front should handle caching aggressively:
location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
proxy_pass http://php-fpm-container;
}
For development environments, consider:
php -S 0.0.0.0:80 -t /var/www/html router.php
For production workloads, I'd recommend one of these approaches:
- Use Nginx unit (supports both PHP and static files)
- Accept the minor complexity of Nginx + PHP-FPM separation
- Implement a CDN for static assets
Remember to benchmark any solution under expected production loads.