Every web application needs a web server to handle HTTP requests. Whether you're serving a static site, reverse-proxying to a Node.js backend, or hosting a WordPress installation, your choice of web server affects performance, security, and how much time you spend on configuration.
This guide compares the three most popular options — Nginx, Apache, and Caddy — with real configuration examples and practical advice for choosing the right one.
Quick Comparison
| Feature | Nginx | Apache | Caddy |
|---|---|---|---|
| Architecture | Event-driven, async | Process/thread per connection | Event-driven, Go goroutines |
| Performance | Excellent for static files and high concurrency | Good, but higher memory under load | Very good, slightly behind Nginx |
| Configuration | nginx.conf (custom syntax) |
httpd.conf + .htaccess (XML-like) |
Caddyfile (minimal, human-readable) |
| HTTPS | Manual (Let's Encrypt + certbot) | Manual (Let's Encrypt + certbot) | Automatic (built-in ACME) |
| Per-directory config | No (centralized only) | Yes (.htaccess) |
No (centralized only) |
| Module system | Compiled modules | Dynamic modules (loaded at runtime) | Plugins (Go modules) |
| Market share | ~34% (most popular) | ~29% (declining) | ~1% (growing fast) |
| Best for | High-traffic sites, reverse proxy | Shared hosting, PHP apps | Small-to-medium sites, automatic HTTPS |
Nginx
Nginx (pronounced engine-x
) was created in 2004 to solve the C10K problem — handling 10,000 concurrent connections. Its event-driven architecture uses a small number of worker processes, each handling thousands of connections asynchronously.
Strengths
- Performance: Serves static files extremely fast with minimal memory
- Reverse proxy: The de facto standard for proxying to application servers (Node.js, Python, Ruby, Go)
- Load balancing: Built-in upstream balancing with multiple algorithms
- Low memory footprint: Handles thousands of connections with minimal RAM
Configuration Example
Serving a static site:
server {
listen 80;
server_name example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# Cache static assets
location ~* \.(css|js|png|jpg|gif|ico)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
Reverse proxy to a Node.js app:
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
When to Use Nginx
- High-traffic websites needing maximum performance
- Reverse proxying to application servers
- VPS deployments where you control the server
- Load balancing across multiple backends
- Serving static assets alongside dynamic applications
Apache
Apache HTTP Server has been running the web since 1995. It introduced the concept of dynamically loadable modules and per-directory configuration via .htaccess files — features that made shared hosting possible.
Strengths
.htaccesssupport: Per-directory configuration without server restart- Module ecosystem: Hundreds of modules (mod_php, mod_rewrite, mod_security)
- Shared hosting: Still the default on most shared hosting providers
- Documentation: Decades of community knowledge and tutorials
Configuration Example
Serving a static site:
<VirtualHost *:80>
ServerName example.com
DocumentRoot /var/www/example.com
<Directory /var/www/example.com>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# Cache static assets
<FilesMatch "\.(css|js|png|jpg|gif|ico)$">
ExpiresActive On
ExpiresDefault "access plus 30 days"
Header set Cache-Control "public, immutable"
</FilesMatch>
</VirtualHost>
Reverse proxy to a Node.js app:
<VirtualHost *:80>
ServerName api.example.com
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
RequestHeader set X-Forwarded-Proto "http"
</VirtualHost>
When to Use Apache
- Shared hosting environments where Nginx isn't available
- PHP applications that benefit from mod_php
- Projects that need
.htaccessfor per-directory configuration - Legacy applications with existing Apache configurations
Caddy
Caddy (v2, released 2020) is the newest of the three. Its headline feature is automatic HTTPS — it obtains and renews TLS certificates from Let's Encrypt without any configuration. The Caddyfile syntax is designed to be as simple as possible.
Strengths
- Automatic HTTPS: Certificates obtained and renewed automatically via ACME
- Simple configuration: A basic site takes 2-3 lines
- Modern defaults: HTTP/2, HTTP/3, TLS 1.3 out of the box
- Single binary: No dependencies, easy to install and update
Configuration Example
Serving a static site (with automatic HTTPS):
example.com {
root * /var/www/example.com
file_server
}
That's it. Caddy automatically obtains a TLS certificate, redirects HTTP to HTTPS, and serves the files.
Reverse proxy to a Node.js app:
api.example.com {
reverse_proxy 127.0.0.1:3000
}
When to Use Caddy
- Small-to-medium sites where simplicity matters
- Projects where automatic HTTPS saves significant ops time
- Development environments needing local HTTPS
- APIs and microservices with straightforward routing
Performance Comparison
For static file serving and high concurrency, Nginx leads. Apache's process-per-connection model uses more memory under load. Caddy performs well but sits between the two.
| Metric | Nginx | Apache | Caddy |
|---|---|---|---|
| Static file throughput | Highest | Moderate | High |
| Memory per 10K connections | ~2.5 MB | ~10+ MB (prefork) | ~5 MB |
| Concurrent connection handling | Excellent | Good (with worker MPM) | Very good |
| TLS handshake overhead | Low (with tuning) | Low (with tuning) | Low (default) |
In practice, the web server is rarely the bottleneck. Your application code, database queries, and network latency have far more impact on user-perceived performance.
How Web Server Choice Affects Deployment
When you deploy with DeployHQ, your code is transferred to the server — the web server configuration ships alongside it. Understanding the relationship between your application code and web server is important:
- Configuration files deploy with your code. Your
nginx.conf,.htaccess, orCaddyfilelives in your Git repository and deploys automatically when you push from GitHub or GitLab - Server restart may be needed. After deploying Nginx or Apache config changes, you often need a reload command. DeployHQ's build pipelines can run post-deploy commands to handle this
- The web server is already running. DeployHQ deploys your application code. The web server itself is installed and managed separately on your server
For teams managing multiple client sites, having a consistent web server across projects simplifies operations. Agencies often standardize on Nginx for its performance and flexibility.
FAQ
Can I run Nginx and Apache together? Yes. A common pattern is Nginx as a reverse proxy in front of Apache. Nginx handles static files and SSL termination; Apache handles PHP via mod_php. This gives you the best of both worlds.
Is Caddy production-ready? Yes. Caddy v2 is stable and used in production by many companies. Its automatic certificate management actually reduces ops risk compared to manual cert renewal.
Which is best for WordPress?
Apache is the traditional choice because WordPress relies on .htaccess for URL rewriting. However, Nginx works well with WordPress using a try_files directive, and many high-traffic WordPress sites run on Nginx.
Do I need a web server if I'm using Node.js/Express? Node.js can serve HTTP directly, but a reverse proxy (typically Nginx or Caddy) in front of it provides SSL termination, static file serving, load balancing, and protection against slow clients. For production, always use a reverse proxy.
Which web server is easiest to learn? Caddy, by a wide margin. A functional Caddyfile can be 3 lines. Nginx has a moderate learning curve. Apache's configuration is the most verbose but extremely well-documented.
Your web server is the front door to your application. Whether you choose Nginx for performance, Apache for compatibility, or Caddy for simplicity, the important thing is that your deployment pipeline handles the configuration reliably.
Try DeployHQ free — deploy your web server configuration alongside your application code on every push. See pricing for team plans.
Questions? Reach out at support@deployhq.com or @deployhq.