Hardened nginx for PHP-FPM apps: security headers, rate limiting, health check & stdout logs.
1.4K
A hardened nginx (stable) image, preconfigured to serve a Symfony-style PHP
application through lampelk/php-fpm.
/var/www/html/public. Static files are served directly, and everything else goes to index.php.index.php to PHP-FPM at localhost:9000, so nginx and PHP-FPM must share a network namespace:
awsvpc networkingnetwork_mode: "service:php" (see below)lampelk/nginx forwards PHP requests to localhost:9000, so it shares the
PHP container's network:
# compose.yaml
services:
php:
image: lampelk/php-fpm-dev:latest
volumes:
- ./:/var/www/html
ports:
- "8080:80" # nginx shares this container's network, so publish its port here
extra_hosts:
- "host.docker.internal:host-gateway" # lets Xdebug reach your IDE on Linux
nginx:
image: lampelk/nginx:latest
network_mode: "service:php"
volumes:
- ./:/var/www/html
depends_on:
- php
Run docker compose up -d, then open http://localhost:8080. The app's front controller must be at
public/index.php.
| Area | Behaviour |
|---|---|
| PHP routing | Front controller only. Any .php file other than index.php returns 404 |
| Hidden files | Dotfiles are denied, except /.well-known/ |
| Security headers | Strict Content-Security-Policy, Strict-Transport-Security (1 year, includeSubDomains; preload), X-Frame-Options: SAMEORIGIN, X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy, Cross-Origin-Opener-Policy, Cross-Origin-Resource-Policy |
| Rate limiting | 10 requests per second per client IP, burst of 20 |
| Hardening | server_tokens off, X-Powered-By removed, 12s client body/header timeouts, 10 MB upload limit |
| Performance | gzip, sendfile, 6-month immutable caching for static assets |
| Logging | Access log to stdout, error log to stderr |
| Health check | curl -f http://localhost/ every 30 seconds |
| Port | 80 |
⚠️ HSTS. The
Strict-Transport-Securityheader includesincludeSubDomains; preload. Only serve this image behind HTTPS (for example, a load balancer that terminates TLS) on a domain where you want that policy for every subdomain.
The configuration is in /etc/nginx/nginx.conf (global settings and headers) and /etc/nginx/conf.d/default.conf
(the server block). To override it, replace either file:
FROM lampelk/nginx:latest
COPY my-site.conf /etc/nginx/conf.d/default.conf
If your app loads scripts, styles, fonts or images from other origins, you'll need to relax the
Content-Security-Policy in nginx.conf.
| Tag | Description |
|---|---|
latest | Current stable build (PHP 8.5.12). Rebuilt on every change and for each PHP security release. |
Any other tag is a pre-release build of work in progress. Don't use one in production.
latest moves. For reproducible deploys, pin the image by digest, for example image@sha256:…. Then bump the digest
when a new build is published.
These images are rebuilt from the official PHP and nginx images. Each build picks up the latest upstream and Debian security fixes, and the PHP version is bumped for every PHP security release. To get a fix, pull and redeploy. Running containers are not updated automatically.
| Image | Purpose |
|---|---|
lampelk/php-fpm | PHP-FPM for web apps in production |
lampelk/php-cli | PHP CLI for workers, consumers and cron in production |
lampelk/php-fpm-dev | php-fpm + Xdebug, Composer and Symfony CLI for local development |
lampelk/php-cli-dev | php-cli + Xdebug, Composer and Symfony CLI for local development |
lampelk/nginx | Hardened nginx to sit in front of php-fpm |
Released under the MIT License.
Content type
Image
Digest
sha256:add585247…
Size
62.9 MB
Last updated
3 days ago
docker pull lampelk/nginx