DevToolBoxGRATIS
Blog

Nginx vs Apache 2026: Welke webserver kiezen?

13 minby DevToolBox

Nginx and Apache are the two most widely deployed web servers in the world. Together they power over 60% of all websites. Nginx dominates high-traffic sites with its event-driven architecture, while Apache remains popular for shared hosting and applications needing per-directory configuration via .htaccess files. This comprehensive guide compares their architectures, performance characteristics, configuration approaches, and use cases to help you choose the right web server for your project.

Architecture: The Fundamental Difference

The core architectural difference between Nginx and Apache determines their performance characteristics, resource usage, and configuration philosophy.

Apache: Process/Thread-Based (MPM)

Apache uses a Multi-Processing Module (MPM) architecture. The default MPM on most systems is event (Apache 2.4+), but prefork and worker are also available. In prefork mode, each connection gets its own process. In worker mode, connections are handled by threads within a pool of processes. The event MPM improves on worker by using a dedicated listener thread for keep-alive connections.

Apache Architecture (Process/Thread Model):

  Master Process
  β”œβ”€β”€ Worker Process 1
  β”‚   β”œβ”€β”€ Thread 1 β†’ handles connection A
  β”‚   β”œβ”€β”€ Thread 2 β†’ handles connection B
  β”‚   └── Thread N β†’ handles connection C
  β”œβ”€β”€ Worker Process 2
  β”‚   β”œβ”€β”€ Thread 1 β†’ handles connection D
  β”‚   └── Thread N β†’ handles connection E
  └── Worker Process N...

  MPM Modes:
  - prefork: 1 process per connection (safe for non-thread-safe modules like mod_php)
  - worker:  threads within process pool (more efficient)
  - event:   listener thread for keep-alive (best for Apache 2.4+)

Nginx: Event-Driven, Non-Blocking

Nginx uses an asynchronous, event-driven architecture. A master process manages multiple worker processes, each handling thousands of concurrent connections using a single thread with an event loop (epoll on Linux, kqueue on BSD). This means Nginx can serve 10,000+ concurrent connections with minimal memory because it does not create a thread or process per connection.

Nginx Architecture (Event-Driven):

  Master Process (reads config, manages workers)
  β”œβ”€β”€ Worker Process 1 (single thread, event loop)
  β”‚   └── Event Loop: handles 1000s of connections via epoll/kqueue
  β”‚       β”œβ”€β”€ Connection A (non-blocking I/O)
  β”‚       β”œβ”€β”€ Connection B (non-blocking I/O)
  β”‚       β”œβ”€β”€ Connection C (non-blocking I/O)
  β”‚       └── ... (thousands more)
  β”œβ”€β”€ Worker Process 2 (single thread, event loop)
  β”‚   └── Event Loop: handles 1000s more connections
  └── Worker Process N (typically = CPU cores)

  Key: No thread-per-connection overhead
  Result: 10K+ connections with ~30-50MB RAM

Performance Benchmarks

Performance comparisons between Nginx and Apache depend heavily on the workload type. Here are benchmarks for common scenarios.

MetricApache 2.4 (event MPM)Nginx 1.27
Static file serving (req/s)~15,000-25,000~50,000-100,000
Concurrent connections (10K)Degrades (thread exhaustion)Stable (event loop)
Memory per 10K connections~300-600MB~30-50MB
PHP (via PHP-FPM)mod_php or PHP-FPMPHP-FPM (FastCGI)
Reverse proxy throughputGood (mod_proxy)Excellent (native)
SSL/TLS terminationGood (mod_ssl)Excellent (native)

Configuration Comparison

Apache and Nginx take fundamentally different approaches to configuration.

Apache: .htaccess + httpd.conf

Apache supports distributed configuration through .htaccess files, which allow per-directory overrides without restarting the server. This is popular in shared hosting because each user can configure their own directory. However, .htaccess files have a performance cost because Apache must scan for them on every request.

# Apache Virtual Host Configuration (/etc/apache2/sites-available/example.conf)
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example/public

    <Directory /var/www/example/public>
        AllowOverride All
        Require all granted
        Options -Indexes +FollowSymLinks
    </Directory>

    # Enable mod_rewrite
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

    # PHP via mod_php (prefork MPM required)
    <FilesMatch \.php$>
        SetHandler application/x-httpd-php
    </FilesMatch>

    # Or PHP via PHP-FPM (recommended with event MPM)
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/run/php/php8.3-fpm.sock|fcgi://localhost"
    </FilesMatch>

    # Logging
    ErrorLog ${APACHE_LOG_DIR}/example-error.log
    CustomLog ${APACHE_LOG_DIR}/example-access.log combined
</VirtualHost>

# .htaccess file (per-directory, no restart needed)
# /var/www/example/public/.htaccess
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

Nginx: Centralized Configuration

Nginx uses centralized configuration files (nginx.conf and included files). There is no .htaccess equivalent. All configuration changes require a reload (which is graceful and near-instant). This centralized approach is faster because Nginx reads the config once at startup rather than scanning directories per request.

# Nginx Server Block (/etc/nginx/sites-available/example.conf)
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;
    root /var/www/example/public;
    index index.html index.php;

    # SSL
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Static files with caching
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|woff2)$ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # PHP via PHP-FPM
    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    # SPA fallback (equivalent to .htaccess rewrite)
    location / {
        try_files $uri $uri/ /index.html;
    }

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;

    # Gzip compression
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
    gzip_min_length 1000;

    access_log /var/log/nginx/example-access.log;
    error_log /var/log/nginx/example-error.log;
}

Feature Comparison

Feature              Apache 2.4                   Nginx 1.27
-------------------------------------------------------------------
Module System        Dynamic (load at runtime)     Static + dynamic (since 1.9.11)
.htaccess            Full support                  Not supported
URL Rewriting        mod_rewrite (regex)            rewrite directive (simpler)
Reverse Proxy        mod_proxy + mod_proxy_http     Native proxy_pass
Load Balancing       mod_proxy_balancer             Native upstream module
Caching              mod_cache                      Native proxy_cache + FastCGI
WebSocket            mod_proxy_wstunnel             Native support
HTTP/2               mod_http2                      Native
HTTP/3 (QUIC)        Experimental                   Native since 1.25.0
Scripting            mod_lua                        OpenResty (Lua), njs (JS)
Config Reload        Restart required               Graceful reload (zero downtime)
Per-dir Config       .htaccess (runtime)            Not available
License              Apache 2.0                     BSD 2-Clause

Use Cases: When to Choose Each

Choose Nginx When:

  • High traffic - Serving thousands of concurrent connections efficiently
  • Reverse proxy - Proxying requests to backend application servers (Node.js, Python, Go)
  • Load balancing - Distributing traffic across multiple backend servers
  • Static content - Serving static files, images, and assets at maximum speed
  • Microservices - API gateway and service mesh entry point
  • CDN/Edge - Caching and serving content at the edge
  • Docker/Kubernetes - Lightweight container-friendly web server and ingress

Choose Apache When:

  • Shared hosting - Multiple users needing per-directory .htaccess configuration
  • WordPress/PHP - Traditional LAMP stack with mod_php
  • Dynamic modules - Loading and unloading modules at runtime without recompilation
  • .htaccess required - Applications that ship with .htaccess rules (WordPress, Drupal, Magento)
  • Complex rewrite rules - mod_rewrite provides the most powerful regex-based URL rewriting

Using Both Together

A popular architecture uses Nginx as a reverse proxy in front of Apache. Nginx handles static files, SSL termination, and load balancing, while Apache runs the application (PHP via mod_php). This gives you the best of both worlds.

# Nginx as reverse proxy in front of Apache
# /etc/nginx/sites-available/example.conf

upstream apache_backend {
    server 127.0.0.1:8080;  # Apache listens on port 8080
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    # Nginx serves static files directly (fast)
    location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff2)$ {
        root /var/www/example/public;
        expires 30d;
        access_log off;
    }

    # Dynamic requests go to Apache (PHP, .htaccess)
    location / {
        proxy_pass http://apache_backend;
        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;
    }
}

Reverse Proxy Configuration

# Nginx reverse proxy to Node.js / Python / Go backend
upstream app_servers {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    keepalive 64;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    location / {
        proxy_pass http://app_servers;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # WebSocket support
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

Load Balancing

# Nginx Load Balancing Strategies

# Round Robin (default)
upstream backend_rr {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080;
}

# Weighted Round Robin
upstream backend_weighted {
    server 10.0.0.1:8080 weight=5;   # gets 5x traffic
    server 10.0.0.2:8080 weight=3;
    server 10.0.0.3:8080 weight=1;
}

# Least Connections
upstream backend_least {
    least_conn;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

# IP Hash (sticky sessions)
upstream backend_ip {
    ip_hash;
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

# Health checks and failover
upstream backend_health {
    server 10.0.0.1:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.2:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.3:8080 backup;  # only used when others fail
}

SSL/TLS Configuration

# Nginx SSL/TLS Best Practices (2026)
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;

    # Modern TLS configuration
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    ssl_prefer_server_ciphers off;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 1.1.1.1 8.8.8.8 valid=300s;

    # Session resumption
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    # HSTS (15768000 seconds = 6 months)
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;
}

Security Comparison

Both servers have strong security records, but their approaches differ.

Security Aspect          Apache                    Nginx
-----------------------------------------------------------------
CVE History              More CVEs (larger surface)  Fewer CVEs (smaller codebase)
.htaccess Risk           Can expose config errors    N/A (no .htaccess)
Default Config           Permissive                  Restrictive
Module Surface           Large (many loaded)          Minimal (compile what you need)
Rate Limiting            mod_ratelimit               limit_req / limit_conn (native)
WAF Integration          mod_security (mature)        ModSecurity + NAXSI
Access Control           .htaccess / <Directory>      allow/deny directives
Request Size Limits      LimitRequestBody             client_max_body_size

Frequently Asked Questions

Is Nginx faster than Apache?

For static content and high-concurrency scenarios, Nginx is significantly faster due to its event-driven architecture. For dynamic content processed by PHP-FPM, both perform similarly since the bottleneck is the PHP process, not the web server. Nginx uses dramatically less memory under high concurrent load.

Can Nginx replace Apache completely?

For most modern web applications, yes. The main scenario where Apache remains necessary is when you need .htaccess per-directory configuration (common in shared hosting) or mod_rewrite compatibility for legacy applications like WordPress with complex rewrite rules.

Which is better for WordPress?

Apache is traditionally used with WordPress because WordPress ships with .htaccess files for URL rewriting. However, Nginx with proper configuration performs better for WordPress, especially under high traffic. Many managed WordPress hosts use Nginx with custom FastCGI caching. You need to manually configure Nginx rewrite rules that WordPress handles automatically via .htaccess on Apache.

Should I use Nginx as a reverse proxy for Apache?

This is a popular pattern that gives you the best of both worlds. Nginx handles static files, SSL termination, caching, and rate limiting at the front. Apache runs behind it for PHP applications that need .htaccess or mod_php. The overhead of the extra layer is minimal compared to the performance gains.

What about Caddy and Traefik?

Caddy is a modern web server written in Go with automatic HTTPS (Let's Encrypt). It is excellent for simple deployments. Traefik is a cloud-native reverse proxy designed for container orchestration (Docker, Kubernetes). Both are great alternatives for specific use cases, but Nginx and Apache remain the most battle-tested and widely deployed options.

Related Tools and Guides

𝕏 Twitterin LinkedIn
Was dit nuttig?

Blijf op de hoogte

Ontvang wekelijkse dev-tips en nieuwe tools.

Geen spam. Altijd opzegbaar.

Try These Related Tools

{ }JSON Formatter

Related Articles

Nginx Configuratiegids: Van Basisinstelling tot Productie

Complete Nginx configuratiegids. Leer server blocks, reverse proxy, SSL/TLS en load balancing.

Nginx Config Voorbeelden: Reverse Proxy, SSL en Statische Sites

Productie-klare Nginx configuraties: reverse proxy, SSL/TLS, statische bestanden, load balancing.

Docker Compose Tutorial: Van basis tot productie-klare stacks

Compleet Docker Compose tutorial: docker-compose.yml syntax, services, netwerken, volumes, omgevingsvariabelen, healthchecks en voorbeelden.