All web servers do the same job, but they differ in how they juggle many visitors at once. Process-based servers dedicate a process or thread to each connection; event-driven servers let a few workers handle thousands of connections each. This architectural difference explains most of the performance gaps between Apache, Nginx and LiteSpeed.
Before you start
- Basic idea of what a web server does: what is a web server.
The problem: most connections are idle
A visitor's browser opens a connection, sends a request, then spends most of its time doing nothing: reading the page, waiting on a slow mobile network, or keeping the connection alive for the next click. In the late 1990s this became known as the C10k problem: how can one server hold 10,000 connections at once when most of them are idle?
Process-based (and thread-based) servers
Classic Apache with the prefork MPM gives each connection its own process. The worker MPM uses one thread per connection.
- Simple model: code runs top to bottom; a slow operation just blocks that one worker.
- Isolation: a crash affects only one connection (prefork).
- Cost: each process or thread needs its own memory and stack. Thousands of idle keep-alive connections mean thousands of mostly sleeping workers, and the OS spends time switching between them.
When all workers are busy, new visitors wait, even if the existing workers are only waiting on slow clients.
Event-driven servers
Nginx, LiteSpeed, OpenLiteSpeed and Caddy run a small number of workers, typically one per CPU core. Each worker runs an event loop:
- Ask the kernel which of its thousands of sockets are ready (Linux
epoll, BSDkqueue). - For each ready socket, do a small piece of non-blocking work: read some bytes, write some bytes, accept a connection.
- Go back to step 1.
An idle connection costs only a few kilobytes of state and no CPU. One worker can serve tens of thousands of connections.
Comparison
| Aspect | Process/thread per connection | Event-driven |
|---|---|---|
| Examples | Apache prefork, Apache worker | Nginx, LiteSpeed, OpenLiteSpeed, Caddy, Apache event (hybrid) |
| Memory per idle connection | High (a process or thread) | Very low |
| Slow clients | Tie up a worker each | Almost free |
| Static file serving | Good | Excellent |
| Running app code inside the server | Easy (mod_php) | Not done; code runs in separate processes |
Apache event: a hybrid
Apache's event MPM keeps threads for active requests but hands idle keep-alive connections to a dedicated listener thread. It closes much of the gap for keep-alive, though each active request still occupies a thread. See Apache MPMs explained.
Where does PHP fit?
PHP code is blocking: a script that queries a database waits. Event-driven servers therefore never run PHP inside the event loop. They pass the request to a pool of separate PHP processes (PHP-FPM, or LSAPI on LiteSpeed) and carry on serving other connections while waiting for the answer. So the limit on dynamic pages is the number of PHP workers, while the web server itself rarely becomes the bottleneck. Background: application servers explained.
What it means in practice
- Static files, TLS and slow clients: event-driven servers win clearly in memory and concurrency.
- Dynamic pages: speed depends on PHP and the database; caching helps far more than the server model.
- Traffic spikes: event-driven servers degrade gracefully; process-based servers can hit their worker limit and queue requests.
- Shared hosting: MdmHost uses LiteSpeed, an event-driven server that still reads Apache-style
.htaccess, combining both worlds.
FAQ: event-driven vs process-based
Is Apache obsolete?
No. With the event MPM and PHP-FPM it performs well for most sites and remains very flexible.
Why does Node.js feel similar to Nginx?
Node.js also uses an event loop, which is why it handles many concurrent connections well, and why blocking code hurts it.
Does an event-driven server use all CPU cores?
Yes, by running one worker process per core.
