Articles

Nginx vs Apache: How the Two Web Servers Differ

Nginx and Apache both serve HTTP traffic, but Nginx's event-driven model and Apache's process-per-connection design lead to real differences under load.

Chisato Chisato · · 4 min read
Server racks with cabling in a data center

Nginx and Apache HTTP Server are the two most widely deployed web servers, and the difference between them comes down to how each one handles concurrent connections. Apache spawns a process or thread per connection; Nginx uses an event-driven, asynchronous model that handles many connections on a small, fixed pool of worker processes. That one architectural choice explains most of the practical differences developers run into.

Both can serve static files, act as a reverse proxy, terminate TLS, and route requests to application servers. Neither has been “obsolete” for the other in a long time — they’re just tuned for different shapes of workload.

Apache’s process/thread model

Apache’s classic MPM (multi-processing module), prefork, spawns a separate OS process for every connection. Each process handles one request at a time, which makes Apache’s request-handling model simple to reason about and extremely resilient to a single request crashing the whole server — a worker process dying takes down one connection, not the server. The worker and event MPMs improve on this with threads instead of full processes, narrowing but not eliminating the per-connection overhead.

Apache’s other defining feature is .htaccess — per-directory configuration files that let you override server behavior (redirects, access control, caching headers) without touching the main config or restarting the server. This is convenient on shared hosting where you don’t have access to the main configuration, but it comes at a real cost: Apache has to check every directory in a request’s path for an .htaccess file on every single request, unless that’s explicitly disabled.

Nginx’s event-driven model

Nginx uses a small, fixed number of worker processes — typically one per CPU core — each running a non-blocking event loop that can handle thousands of connections concurrently. Instead of a process or thread per connection, a worker moves between connections as I/O events (a socket becoming readable, a file finishing a read) come in. This design is why Nginx tends to use dramatically less memory under high concurrency than a process-per-connection model, and why it became the default choice for serving static assets and acting as a load balancer or API gateway in front of application servers.

The tradeoff is configuration style: Nginx has no .htaccess equivalent by design — all configuration lives in files the server reloads on restart, not per-directory files checked on every request. That’s a deliberate performance decision, but it means Nginx configuration has to be centrally managed rather than delegated per-directory.

Comparing the two

ApacheNginx
Concurrency modelProcess/thread per connection (MPM-dependent)Event-driven, async, fixed worker pool
Memory under high concurrencyGrows with connection countStays roughly flat
Per-directory config (.htaccess)YesNo — centralized config only
Dynamic contentCan embed interpreters (mod_php, etc.) directlyProxies to a separate app server (PHP-FPM, etc.)
Typical role todayFull app server, shared hosting, legacy stacksStatic files, reverse proxy, TLS termination, load balancer
Config reloadSome directives via .htaccess without restartRequires a config reload for changes

Where each one fits

Apache’s process-per-request model and .htaccess support still make it a reasonable default on shared hosting environments where multiple unrelated sites live on one server and per-site overrides matter more than raw throughput. It also embeds language runtimes directly (historically mod_php and similar modules), which simplifies deployment for some legacy PHP stacks even though it couples the web server to the application runtime.

Nginx’s efficiency under concurrent load is why it’s the common choice in front of modern deployments: serving static assets directly, terminating TLS, and proxying dynamic requests to an application server or a container behind it. It’s also frequently layered with Docker as a reverse proxy in front of containerized services, and shows up in blue-green and canary deployment setups where request routing needs to shift between versions without dropping connections.

In practice, a lot of infrastructure runs both: Apache or an application server handling dynamic requests, with Nginx in front doing TLS termination, static file serving, and load balancing across multiple backend instances. That combination plays to each server’s strengths rather than asking one tool to do everything.

Performance under load

The concurrency model matters most under two conditions: many simultaneous connections, and connections that stay open for a while (long-polling, streaming responses, slow clients on high-latency networks). Apache’s prefork MPM in particular can exhaust available processes if enough clients hold connections open — a classic “slow client” attack pattern — while Nginx’s event loop handles idle or slow connections without tying up a whole worker per client. This is one of the concrete reasons Nginx became the standard choice for internet-facing edges, even in stacks where Apache still does the actual application serving behind it.

None of this means Apache is “slow” for typical request volumes — for many workloads the difference is invisible. It shows up specifically at high concurrency, with many idle or long-lived connections, which is exactly the profile of a public-facing edge server.

The takeaway

Apache and Nginx both serve HTTP, but their concurrency models point them at different roles: Apache’s process-per-connection design and .htaccess support suit shared hosting and tightly coupled application stacks, while Nginx’s event-driven architecture suits high-concurrency edges — static file serving, TLS termination, and reverse proxying in front of application servers. A lot of production infrastructure uses both together rather than picking one exclusively, putting Nginx at the edge and an application server, sometimes Apache, behind it.

The Lycoris Team The Lycoris Team · · 4 min read

Microservices vs Monolith: How to Choose

Monoliths ship faster early; microservices buy independent scaling and team autonomy at the cost of distributed complexity. How to choose.

#DevOps #Cloud #Web Development
Chisato Chisato · · 4 min read

A Hands-On Guide to AWS Spot Instances

AWS Spot Instances sell unused EC2 capacity at a steep discount. Here's how to launch one, handle interruptions, and pick workloads that fit.

#Cloud #AWS #DevOps