Igor Sysoev released nginx 75 on 4 October 2004 for what prefork handled worst, thousands of idle connections: one single-threaded worker per core runs an event loop over all its sockets. Apache 129 's event MPM (MPMs and Workers) closed most of that gap; what remains is configuration. nginx has no .htaccess, so every per-directory rule goes in the server configuration, edited by root and reloaded.
| Aspect | Apache 2.4.66 | nginx 1.30.5 |
|---|---|---|
| Connection model | prefork, worker or event MPM | event loop per worker process |
| Per-directory config | .htaccess, read per request | none; reload to change |
| PHP | mod_php or PHP-FPM | PHP-FPM only (fastcgi_pass) |
| Forwarded headers | added automatically | one proxy_set_header each |
| Active health checks | free (mod_proxy_hcheck) | commercial NGINX Plus only |
| Sticky sessions | route cookie | sticky cookie, free since 1.29.6 |
| HTTP/3 | not available | since 1.25.0, experimental, opt-in build |
The same proxy in nginx, run from the official image (docker run -d -p 8803:80 nginx:stable, which reported nginx/1.30.5) against the same backends:
upstream pool {
zone pool 64k;
server host.docker.internal:9203;
server host.docker.internal:9213;
}
server {
listen 80;
server_tokens off;
absolute_redirect off;
location /app/ {
proxy_pass http://host.docker.internal:9203/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect http://127.0.0.1:9203/ /app/;
}
location /pool/ { proxy_pass http://pool/; }
}The forged-header request of ProxyPass arrived with X-Forwarded-For: 203.0.113.9, 172.17.0.1 and no X-Forwarded-Server, since nginx adds only what you list. Two lines were added after a run went wrong. Without zone, four requests to /pool/ all reached app1, because each of the 36 workers kept its own round-robin position; the shared zone makes it global. Without absolute_redirect off, the redirect became http://127.0.0.1/app/dashboard, because nginx in the container believes it is on port 80.