Overview
Nginx is a high-performance web server and reverse proxy used by a large share of the world's busiest sites. It uses an event-driven, non-blocking architecture: a small number of worker processes each handle thousands of connections using an event loop (epoll / kqueue), instead of one thread per connection.
In system designs, Nginx typically terminates TLS, serves static files, load balances across app servers, caches responses, applies rate limits, and routes requests by host and path. Its configuration is declarative: server blocks match hosts, location blocks match paths, and upstream blocks define backend pools.
Instead of assigning one waiter per table who stands idle while guests read menus (thread per connection), one efficient waiter checks every table in turn and only acts when a table is ready. That is the event loop.
When to use it
- A reverse proxy or load balancer you run yourself.
- Serving static files and single-page apps efficiently.
- Kubernetes ingress (ingress-nginx).
- Edge caching and rate limiting close to the app.
Where it shows up in interviews
Recognize it when: on-prem or cost-sensitive deployment without a managed load balancer.
- Design a web tier for a startup
- Design an on-prem API platform
Recognize it when: rate limiting and caching before the app.
- Design a rate limiter
- Handle traffic spikes for a flash sale
Where it is used in real software
Large platforms have used Nginx for edge proxying and static content delivery.
One of the most common Kubernetes ingress controllers, generating Nginx config from Ingress resources.
Nginx with embedded Lua scripting, used to build API gateways such as Kong.
Key terms
- Worker process
- One per CPU core, each running an event loop over many connections.
- server block
- Virtual host matched by listen port and server_name.
- location block
- Rules for matching URL paths.
- upstream
- A named group of backend servers with a balancing method.
- limit_req
- Leaky-bucket rate limiting per key (for example client IP).
How it works, step by step
- 1Master reads config
Starts one worker per core (worker_processes auto).
- 2Workers accept connections
Each uses epoll to watch thousands of sockets without blocking.
- 3Request matches server and location
Host header picks the server block, path picks the location.
- 4Handler runs
Serve a file, return a redirect, or proxy_pass to an upstream.
- 5Graceful reload
nginx -s reload starts new workers with new config; old workers finish in-flight requests.
Location matching priority
Requests are matched to exactly one location
| Modifier | Example | Priority |
|---|---|---|
| = (exact) | location = /health | 1: exact match wins immediately |
| ^~ (prefix, stop regex) | location ^~ /static/ | 2: longest prefix, skip regex |
| ~ / ~* (regex) | location ~* \.(png|jpg)$ | 3: first matching regex in file order |
| (prefix) | location /api/ | 4: longest prefix if no regex matched |
NOWModifier: = (exact) | Example: location = /health | Priority: 1: exact match wins immediately
Surprising routing bugs usually come from a regex location capturing requests you expected a prefix location to handle. Use = for hot exact paths like /health.
Implementation
worker_processes auto;events { worker_connections 4096; } http { limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s; upstream app { least_conn; server 10.0.1.10:8080 max_fails=3 fail_timeout=10s; server 10.0.1.11:8080 max_fails=3 fail_timeout=10s; keepalive 64; } server { listen 443 ssl http2; server_name example.com; location = /health { return 200 "ok"; } location /api/ { limit_req zone=api burst=40 nodelay; # 429 beyond the burst proxy_pass http://app; proxy_read_timeout 15s; } location / { root /usr/share/nginx/html; try_files $uri /index.html; # single-page app fallback } }}Complexity and performance
worker_connections limit.
Event model is lightweight.
Graceful worker replacement.
Trade-offs
Nginx is free and flexible but you operate, patch, and scale it. AWS ALB or Cloudflare remove that work at a cost.
Envoy offers dynamic configuration via APIs, richer observability, and is the base of most service meshes; Nginx is simpler and very fast for classic edge use.
Variants and related techniques
Commercial edition with active health checks, a live API, and dashboards.
Nginx with Lua for custom logic at the edge.
Common mistakes
- Missing trailing slash in proxy_pass.
Fix: proxy_pass http://app/ strips the location prefix; http://app keeps it. Test both.
- Default timeouts for long requests.
Fix: Set proxy_read_timeout to match slow endpoints, or better, make them asynchronous.
- No upstream keepalive.
Fix: Add keepalive and proxy_http_version 1.1 to reuse backend connections.
Interview questions
Why is Nginx faster than a thread-per-connection server for many connections?
It uses non-blocking I/O with an event loop, so idle connections cost only a small amount of memory and no thread. Thread-per-connection servers spend memory and context switches on idle threads.
How does Nginx rate limiting work?
limit_req uses a leaky bucket per key (such as client IP): requests over the configured rate are delayed or rejected with 503/429, with a burst allowance.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Configure an SPA with an API proxy | Easy | try_files and proxy_pass. |
| Add per-IP rate limiting and caching | Medium | limit_req and proxy_cache. |