nginx and Apache httpd are both mature, well maintained and fast enough for the vast majority of websites. Arguments about which is quicker usually miss the point. The better question is which one fits the way your sites are built and the way your server is managed. This guide sets out where each one is the stronger choice, how to run them together, and what catches people out when they move from one to the other.
The short version
nginx is usually the better fit when:
- it sits in front of other services as a reverse proxy or load balancer;
- you expect many simultaneous or slow connections;
- a lot of the traffic is static files;
- you want HTTP/3 at the origin or built in request rate limiting;
- whoever runs the server controls all of the configuration, for example in containers.
Apache is usually the better fit when:
- site owners need to change redirects and rules through
.htaccesswithout server access; - your applications ship
.htaccessfiles and expect them to work; - you rely on Apache modules such as
mod_securitywith a rule set already tuned for it; - it is shared hosting, or a server running a mix of older and newer applications;
- your team already knows Apache well.
How each server handles requests
nginx is event driven. A small, fixed number of worker processes, typically one per CPU core, each handle thousands of connections. A worker never waits on one slow client; it moves on to whichever connection is ready next. An idle keep alive connection costs very little memory.
Apache uses multi processing modules, or MPMs, which decide how connections map to processes and threads. The old prefork MPM gives every connection its own process, which is simple but heavy. The worker MPM uses threads instead. The event MPM builds on worker and hands idle keep alive connections to a listener thread, so they no longer tie up a worker. Event is the default on modern Debian and Ubuntu packages and on most current distributions, and it closes much of the gap people remember from the prefork days. The Apache MPM documentation explains how the default is chosen.
Where nginx is the better choice
Many connections and reverse proxying
nginx was designed to sit at the front and pass requests on. Proxying to an application server, balancing across several back ends, and buffering slow clients so the application does not have to wait for them are all core features, configured in a few lines.
Static files and TLS termination
Serving files straight from disk and terminating TLS are things nginx does efficiently with little memory. That makes it a common choice as the layer that handles HTTPS and static assets, then hands everything dynamic to something else.
HTTP/2 and HTTP/3
Both servers support HTTP/2. nginx also supports HTTP/3 over QUIC through its ngx_http_v3_module, available since version 1.25.0 and included when nginx is built with it, which many packaged builds are. Stock Apache httpd does not offer HTTP/3. If you sit behind a CDN, the CDN often provides HTTP/3 to visitors anyway, so this matters less than it first appears.
Built in rate limiting and request mapping
nginx ships with limit_req for request rate limiting and a map directive for choosing values based on parts of the request. Together they let you slow down abusive clients on a login page or an expensive search URL without touching ordinary visitors.
# In the http block
limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;
# In the server block
location /login {
limit_req zone=perip burst=10 nodelay;
}Behind a proxy or CDN, make sure nginx sees the real visitor address first, or every request will appear to come from a handful of proxy addresses. Lock your origin to Cloudflare covers that side. Apache has no equivalent in its core: its bundled mod_ratelimit limits bandwidth per response rather than requests per second, so request rate limiting on Apache needs a third party module such as mod_evasive or rules in mod_security.
Small configuration footprint
An nginx site is usually one short file, and there is no per directory override to account for. Everything that affects a request is in the server configuration, which makes it easy to template, review and keep in version control. That is also why nginx is popular in containers and at the edge, where one image does one job and configuration is baked in.
Where Apache is the better choice
Per directory overrides
Apache reads .htaccess files in each directory, so a site owner can add redirects, rewrites, access rules or caching headers without server access. On hosting where customers do not have root, that is genuinely valuable. nginx does not read .htaccess at all; every change goes through whoever manages the server.
Applications that ship .htaccess
Many PHP applications and plugins ship an .htaccess file, or write rules into it when you change a setting. On nginx those rules are silently ignored, which can quietly disable redirects or expose files the application meant to protect. If you run lots of different applications, keeping translated rules in step by hand is real ongoing work.
Modules and habits
Apache has a long list of modules, and some, such as mod_security with an established rule set, have no drop in nginx equivalent. A lot of documentation and support answers also assume Apache and mod_rewrite. For shared hosting and mixed workloads, and for teams who know Apache well, that familiarity is worth more than a theoretical gain from switching.
Using both together
You do not have to pick one. A common pattern puts nginx in front as a reverse proxy, handling TLS, static files and rate limiting, with Apache behind it on a local port for applications that depend on .htaccess. The cost is two servers to configure, patch and monitor, and you need to pass the real client address and protocol through to Apache, for example with mod_remoteip, so logs and redirects stay correct.
PHP on either server
Both servers can run PHP through PHP-FPM, a separate pool of PHP processes, and that is the sensible default. The old mod_php approach loads PHP into every Apache process and requires the prefork MPM, so even image requests carry PHP's memory. Apache with the event MPM and mod_proxy_fcgi talking to PHP-FPM is a solid, modern setup; see the mod_proxy_fcgi documentation. There are more suggestions in Apache tips for a faster, safer site.
Migration gotchas
Translating .htaccess rules
Every rewrite, redirect and access rule in every .htaccess file, including those in subdirectories, has to be found, translated and tested. Redirects hurt most when wrong, because lost search traffic is easy to miss. Test the old URLs you care about after the move, not just the home page.
Location matching order
nginx picks one location block per request, and not in the order you wrote them. Exact matches win first. Otherwise nginx remembers the longest matching prefix, then checks regular expressions in order and uses the first that matches, unless that prefix was marked ^~. The location documentation sets this out. Most surprises after a migration come from a block that never runs because another one caught the request.
try_files
try_files replaces the usual Apache rule that sends anything that is not a real file to index.php. A typical form is try_files $uri $uri/ /index.php?$args;. Get it wrong and you will see 404s on pretty URLs or PHP served as a download.
Different defaults
Upload limits, timeouts, directory listings and hidden files all behave differently out of the box. nginx rejects request bodies over 1 MB by default until you raise client_max_body_size, for example, while PHP's own limits apply on either server. If something worked before and fails after, check the defaults before blaming the application.
Test before you reload
Both servers can check their configuration before you apply it. Make it a habit.
# nginx
sudo nginx -t && sudo systemctl reload nginx
# Apache on Debian or Ubuntu
sudo apache2ctl configtest && sudo systemctl reload apache2With Apache, also confirm which files are actually in force, since includes and enabled sites can surprise you: see check the Apache configuration that is actually loaded.
Upgrades replace the binary
A package upgrade replaces the nginx binary on disk, but running processes keep the old code until they are restarted or upgraded on the fly. Check what is running as well as what is installed, and run nginx -t afterwards in case a module or directive changed.
Security basics for both
- Hide version details. Use
server_tokens off;in nginx, andServerTokens ProdwithServerSignature Offin Apache. - Limit request sizes and headers. Set
client_max_body_sizeand the header buffer sizes in nginx, andLimitRequestBodyandLimitRequestFieldSizein Apache, to what your applications genuinely need. - Use sensible TLS settings. Allow TLS 1.2 and 1.3 only, and keep certificates renewing automatically.
- Keep up to date. Take security releases from your distribution or the upstream packages promptly, on whichever server you run.
To see which server answers a site, check the response header, then confirm on the server itself, since a CDN or a quiet configuration can hide it.
curl -sI https://example.com | grep -i '^server'
# On the server itself
nginx -v
apache2 -v # or httpd -v on RHEL family systemsA decision checklist
- Who changes the configuration? If site owners need to without server access, choose Apache.
- Do your applications depend on
.htaccessor specific Apache modules? Count the rules and modules before deciding, not after. - Is the job mostly proxying, TLS and static files? nginx is the natural fit.
- Do you need HTTP/3 at the origin or built in rate limiting? nginx makes both easier.
- Does a control panel or platform expect one of them? Go with it unless there is a strong reason not to.
- Is PHP running through PHP-FPM? On either server, it should be.
- What does your team know? A well run server you understand beats a fashionable one you do not.
If the checklist splits down the middle, running nginx in front of Apache is a reasonable compromise. If it points clearly one way, take that answer. Neither choice is a mistake when it matches the workload and the people managing it.