Both are mature open source web servers. Apache loads modules into a choice of process models (prefork, worker or event) and lets each directory override settings with .htaccess files. nginx runs one master process and a few event-driven workers, reads only its central config, and is commonly placed in front of application servers as a reverse proxy.
| Topic | Apache httpd 2.4 | nginx |
|---|---|---|
| Connection handling | Pick an MPM: prefork, worker or event (event is the usual default on modern Unix) | One master process plus worker processes using an event-based model |
| Per-directory config | .htaccess files, if AllowOverride permits (default None) | None: everything lives in the main config files |
| Rewrites | mod_rewrite: RewriteCond and RewriteRule | return, rewrite, try_files and location blocks |
| PHP via FastCGI | mod_proxy_fcgi to a PHP-FPM pool | fastcgi_pass to a PHP-FPM pool |
| Reverse proxy | mod_proxy: ProxyPass and ProxyPassReverse | proxy_pass inside a location |
| Load balancing | mod_proxy_balancer: byrequests, bytraffic, bybusyness, heartbeat | upstream: round robin, least_conn, ip_hash, hash, random |
| Test and reload | apachectl configtest, then apachectl graceful | nginx -t, then nginx -s reload |
Updated
Choosing between them is mostly about where configuration lives and who is allowed to change it. This page skips benchmark claims, which depend entirely on the workload and tuning, and focuses on the differences you meet while running a site: how each server handles connections, why .htaccess exists, and how the rewrite rules you already have translate. Use the translator to see the nginx version of a familiar Apache rule.
Pick a common job to see the Apache rule and the nginx equivalent side by side, filled in with your domain.
Apache tests the HTTPS flag; nginx uses a separate port 80 server that only redirects.
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}The nginx docs recommend a dedicated server block with return instead of an if on the host.
RewriteEngine On
RewriteCond %{HTTP_HOST} ^example\.com$ [NC]
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]server {
listen 443 ssl;
server_name example.com;
return 301 https://www.example.com$request_uri;
}Serve real files when they exist and index.html for every other path.
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]location / {
try_files $uri $uri/ /index.html;
}mod_alias Redirect needs no rewrite engine; nginx matches the exact path with location =.
Redirect permanent "/old-page" "https://example.com/new-page"location = /old-page {
return 301 https://example.com/new-page;
}Both point the error at a page on the same site.
ErrorDocument 404 /404.htmlerror_page 404 /404.html;Apache needs mod_proxy and mod_proxy_http loaded, with matching trailing slashes on both arguments; nginx proxying is built in.
ProxyPass "/api/" "http://127.0.0.1:3000/"
ProxyPassReverse "/api/" "http://127.0.0.1:3000/"location /api/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $host;
}Keeps files whose names start with a dot from being served.
<FilesMatch "^\.">
Require all denied
</FilesMatch>location ~ /\. {
deny all;
}With always, both servers add the header to error responses as well, not only to successful ones.
Header always set X-Content-Type-Options "nosniff"add_header X-Content-Type-Options "nosniff" always;Static files from one folder plus an SPA fallback, configured in the vhost so no .htaccess is needed.
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/site/dist
<Directory /var/www/site/dist>
AllowOverride None
Require all granted
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]
</Directory>
ErrorDocument 404 /404.html
</VirtualHost>The same behaviour: root folder, try_files fallback and a custom error page.
server {
listen 80;
server_name example.com;
root /var/www/site/dist;
location / {
try_files $uri $uri/ /index.html;
}
error_page 404 /404.html;
}
Apache delegates connection handling to a Multi-Processing Module. prefork uses one process per connection and needs no threads. worker runs several processes with many threads each. event builds on worker and uses thread-safe polling so idle keep-alive connections do not tie up a thread. On current Linux and BSD systems event is picked by default.
nginx has one master process that reads the configuration and manages workers, and a small number of worker processes that each handle many connections through an event loop.
.htaccess lets someone without root access change rules for one directory, which is why shared hosts rely on it. The price is that every request makes Apache check each parent directory for the file, even when none exists. With AllowOverride None, which is the default, those files are ignored entirely.
It depends on the job and the tuning, so be wary of any single number. nginx's event-driven workers are designed to hold many idle connections cheaply. Apache's event MPM narrowed that gap by also using thread-safe polling. Measure your own workload before switching for speed alone.
No. nginx has no per-directory override files, so rules from an .htaccess must be moved into the server configuration and nginx reloaded. That is a deliberate trade: no file lookups on each request, but only people with access to the main config can change rules.
Simple redirects and fallbacks translate cleanly, as the translator on this page shows. Long chains of RewriteCond with flags such as [P] or environment variables need a person to read them, because nginx expresses the same intent with different building blocks.
If your host only gives you file access, Apache with .htaccess is what lets you change rules at all. On a server you control, either works with PHP-FPM; in nginx you write the pretty-URL fallback once with try_files in the server block.
For nginx, run nginx -s reload: the master checks the new config, starts new workers and asks the old ones to finish gracefully. For Apache, apachectl graceful does the equivalent for its child processes.
Related: Caddy vs nginx, nginx load balancing, Redirect rules generator, Redirect checker, HTTP status codes.
Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.
We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.