opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
opcache.memory_consumption, so PHP is not recompiled per request.If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.
Apache and Nginx are both mature, both fast enough for a site doing a few thousand visits a day, and both maintained by large communities. Picking between them on raw throughput benchmarks is a waste of your time. The real question for a small VPS is narrower: which one leaves you more RAM for PHP, MySQL and the page cache, and which one costs you fewer hours per year in configuration and debugging? This article answers that with measured numbers from a 1 GB and a 2 GB VPS running the same WordPress workload, plus the exact configuration differences that produce them.
Why “Nginx is faster” is the wrong framing
In a synthetic static-file benchmark, Nginx will beat Apache by a wide margin — sometimes 3x on requests per second per core. That benchmark is irrelevant to the server you are actually running, because your bottleneck is almost never the HTTP daemon. On a low-traffic WordPress VPS, time is spent in PHP-FPM (waiting on MySQL), in MySQL itself, and in TLS handshakes. The web server is a pass-through. What matters is how much memory it takes away from the layers doing real work, and how much operational friction it adds.
For a broader look at what a small VPS can realistically handle, see our notes on choosing and sizing a VPS for real workloads.
Measured baseline: 1 GB VPS, WordPress + PHP-FPM + MariaDB
Test setup: 1 vCPU, 1 GB RAM, 20 GB SSD, Debian 12, PHP 8.3-FPM (4 children), MariaDB 10.11, one WordPress site with WP Super Cache serving 95% static HTML. Load generated with wrk -t2 -c20 -d60s against the cached homepage. Memory figures are RSS after 30 minutes of steady traffic, measured with ps_mem.
| Metric | Nginx 1.24 + PHP-FPM | Apache 2.4 mpm_event + PHP-FPM | Apache 2.4 prefork + mod_php |
|---|---|---|---|
| Web server RSS (idle) | 6 MB | 19 MB | 24 MB |
| Web server RSS (under load) | 11 MB | 28 MB | 41 MB |
| Requests/sec, cached page | 4 180 | 3 390 | 2 150 |
| TTFB p50 (ms) | 4.1 | 5.6 | 7.9 |
| TTFB p95 (ms) | 11 | 18 | 31 |
| Free RAM left for MySQL | ~610 MB | ~580 MB | ~490 MB |
| Config files for 3 vhosts | 3 + 1 shared | 3 + 1 shared | 3 + 1 shared |
| Per-vhost overhead per request | negligible | moderate | high |
The story in that table is not the 23% throughput gap between Nginx and Apache mpm_event. It is the 120 MB difference between prefork+mod_php and a modern event-driven setup — 120 MB that on a 1 GB box is the difference between a 192 MB InnoDB buffer pool and a 320 MB one. That buffer pool will affect your site’s speed far more than the HTTP layer ever will. If you want to see how that budget is divided, our guide to building a MySQL memory budget for a 1 GB VPS works through the arithmetic.
The three Apache modes, and which one to actually run
- prefork + mod_php — the legacy default. One process per connection, PHP embedded in the child. Memory scales linearly with concurrency and PHP cannot be tuned independently of Apache. On a small VPS this is the worst option, but it is also the simplest and still works fine at very low traffic.
- mpm_event + PHP-FPM — the correct modern Apache. Apache handles connections asynchronously and forwards PHP requests to a separately managed FPM pool. Memory is close to Nginx and you get Apache’s
.htaccessmodel, which many hosting panels and legacy apps depend on. - mpm_worker + PHP-FPM — similar to event but thread-based without the async connection handling. Rarely worth choosing over event today.
Switching Apache to event mode is a two-line change and the single highest-value Apache tuning step on a memory-constrained VPS:
a2dismod php8.3 mpm_prefork && a2enmod mpm_event proxy_fcgi setenvif && a2enconf php8.3-fpm && systemctl restart apache2
Then cap concurrency in /etc/apache2/mods-available/mpm_event.conf so it cannot exhaust RAM:
StartServers 2
MinSpareThreads 5
MaxSpareThreads 10
ThreadLimit 32
ThreadsPerChild 16
MaxRequestWorkers 48
MaxConnectionsPerChild 10000
The two configuration models: rewrite rules and file scope
The operational difference people underestimate is where configuration lives. Apache reads .htaccess files from disk on every request in the directory tree (unless AllowOverride None), which means per-directory settings are editable by anyone with file write access — convenient, but a per-request stat and a documented performance tax. Nginx has no per-directory overrides at all; every rule lives in the server block, owned by root.
A typical WordPress front controller in each:
Nginx — location / { try_files $uri $uri/ /index.php?$args; }
Apache — the default WordPress .htaccess with RewriteEngine On and a RewriteRule . /index.php [L] inside a <IfModule mod_rewrite.c> block.
If you host several sites for clients who need to add redirects themselves, Apache’s model saves you real time. If you are the only person touching the configuration, Nginx’s centralised model is less error-prone and slightly faster.
TLS, HTTP/2 and HTTP/3 support
Both terminate TLS with OpenSSL and support HTTP/2. Nginx exposes HTTP/3 (QUIC) in mainline builds with a one-line listen 443 quic reuseport; plus an Alt-Svc header. Apache needs the mod_http3 module and a build against a QUIC-capable library, which most distribution packages do not ship. On a small VPS, HTTP/3 buys you a modest TTFB improvement on lossy mobile connections and is worth enabling in Nginx; it is not a reason to abandon Apache if Apache is already there.
Maintenance and upgrade costs over a year
- Package updates: equivalent. Both track distro security updates and both have occasionally shipped config-syntax-breaking patches that need a manual merge of your config.
- Debugging: Nginx fails with a single log line and a generic 502 when the upstream is wrong; Apache gives a more human error but is noisier. Neither is clearly easier.
- Third-party compatibility: some PHP applications and panels insist on
.htaccess. Converting those to Nginx costs an afternoon and a source of future bugs. - Attack surface: Apache’s module ecosystem is larger, so its patch cadence matters more. Keep both minimal: enable only the modules you use.
Decision guide
Use Nginx when: you are comfortable editing config files directly, want the smallest possible memory footprint, plan to serve static assets or proxy to application servers, or need HTTP/3 and fine-grained caching controls (including fastcgi_cache, which can cut PHP-FPM hits dramatically on a small box).
Use Apache with mpm_event + PHP-FPM when: you rely on .htaccess, run software that ships Apache configuration (cPanel-style panels, some legacy CMS plugins), or want per-directory overrides for other administrators. The memory penalty versus Nginx is roughly 15 MB steady-state — about 2.5% of a 1 GB VPS. That is a trivial price for a model you already understand.
Never choose prefork + mod_php for a new deployment on a VPS under 2 GB. It is the one option that materially changes how much memory you have left for the database.
What actually moves the needle on a low-traffic site
In order of impact, and none of them are the web server:
- Page caching at the HTTP layer, so most requests never reach PHP.
- A correctly sized InnoDB buffer pool — the database is usually the real bottleneck.
- An object cache (Redis or APCu) so repeated queries are served from memory.
- OPcache with a generous
opcache.memory_consumption, so PHP is not recompiled per request. - Only then, the choice of HTTP daemon.
If you are already running Nginx and thinking about migrating to Apache or vice versa, measure first. Run ps_mem, check vmstat 1 for swap activity, and look at your slow query log. If none of those are alarming, the migration will buy you nothing. For workloads where the HTTP layer genuinely is the constraint — high connection counts, static-heavy sites, reverse-proxy front ends — see our deeper walkthrough of Nginx performance tuning on a VPS.

Leave a Reply
You must be logged in to post a comment.