FreeBSD.software
Home/Blog/NGINX vs Apache on FreeBSD: Web Server Showdown
comparison2026-03-29

NGINX vs Apache on FreeBSD: Web Server Showdown

Detailed comparison of NGINX and Apache on FreeBSD. Covers architecture, performance, configuration, modules, SSL, reverse proxy, use cases, and recommendations.

# NGINX vs Apache on FreeBSD: Web Server Showdown

NGINX and Apache are the two web servers that power the vast majority of the internet. Both run well on FreeBSD. Both have been deployed at massive scale for decades. But they are fundamentally different in how they handle connections, how you configure them, and what tradeoffs they force you to accept.

This guide compares them specifically on FreeBSD -- not generic Linux advice repackaged, but practical differences you will actually hit when running either server on a FreeBSD system. If you want broader context covering Caddy and Lighttpd as well, see the [best web servers for FreeBSD comparison](/blog/best-web-server-freebsd/).

Quick Verdict

**NGINX is the better default choice for most FreeBSD servers.** Its event-driven architecture maps directly to FreeBSD's kqueue, it uses significantly less memory under load, and its configuration is predictable once you learn the block syntax.

**Apache is the right choice when** you need per-directory .htaccess overrides (common in shared hosting and legacy PHP applications), you depend on a specific Apache module like mod_rewrite with complex rulesets already written, or your team already has deep Apache expertise and switching costs outweigh the performance gains.

If you are starting fresh with no legacy constraints, pick NGINX. If you are maintaining an existing Apache deployment that works, migrating is not always worth the effort -- read the full comparison below to decide.

---

Architecture: How Each Server Handles Connections

This is the single biggest difference between the two servers. Everything else -- performance, memory, configuration complexity -- flows from this architectural split.

NGINX: Event-Driven and Asynchronous

NGINX uses an event-driven, asynchronous architecture. A small number of worker processes (typically one per CPU core) each run a non-blocking event loop. A single worker can handle thousands of concurrent connections because it never blocks waiting for I/O. When data arrives on a socket, kqueue notifies the worker, which processes the data and moves on.

The result: connection count has minimal impact on memory usage. Whether you have 100 or 10,000 concurrent connections, NGINX worker processes stay roughly the same size.

On FreeBSD, NGINX uses kqueue natively. This is not a compatibility shim -- NGINX's kqueue support has been production-tested for over fifteen years. kqueue provides O(1) event notification, batches event registration and retrieval in a single system call, and handles file descriptors, timers, signals, and filesystem changes through one unified API.

Apache: Process and Thread Models (MPMs)

Apache uses a modular architecture for connection handling called Multi-Processing Modules (MPMs). The three main MPMs are:

**prefork** -- One process per connection. Each child process handles exactly one request at a time. This is the safest model for non-thread-safe PHP extensions (mod_php), but it is the most resource-intensive. At 1,000 concurrent connections, you have 1,000 processes, each consuming 10-30 MB of RAM.

**worker** -- Hybrid model. Multiple child processes each spawn multiple threads. A typical configuration might use 4 processes with 25 threads each, handling 100 concurrent connections with far less memory than prefork. This works well for thread-safe application code.

**event** -- Apache's answer to the C10K problem. Similar to worker, but with a dedicated thread that handles keep-alive connections, freeing worker threads to process new requests. This is Apache's most efficient MPM and the closest it gets to NGINX's event model. On FreeBSD, the event MPM uses kqueue for its event loop.

The key difference: even with the event MPM, Apache still allocates a thread per active connection. Threads are lighter than processes, but they are not free. Under heavy concurrency, Apache's memory footprint grows in ways that NGINX's does not.

---

Performance on FreeBSD

FreeBSD gives both servers access to the same kernel-level optimizations, but they benefit from them differently.

kqueue

Both NGINX and Apache (event MPM) use kqueue on FreeBSD. The difference is in how deeply each server's architecture exploits it. NGINX was built around event loops from day one -- kqueue is not an optimization bolted on later, it is the core of how NGINX operates. Apache's event MPM uses kqueue effectively, but the thread-per-connection model limits how much it can benefit.

sendfile

FreeBSD's sendfile system call performs zero-copy file transmission -- data moves directly from the filesystem cache to the network socket without passing through userspace. Both NGINX and Apache support this. NGINX enables it by default (sendfile on; in the stock configuration). Apache enables it via EnableSendfile On, which is also the default in most configurations.

For static file serving, both servers perform comparably when sendfile is active. The differences emerge under high concurrency, where NGINX's lower per-connection overhead means it can sustain higher throughput before running into resource limits.

accept_filter

FreeBSD offers accf_http and accf_data accept filters that buffer incoming data in the kernel before waking the server process. This reduces context switches and improves throughput for HTTP workloads.

NGINX supports accept filters natively:

nginx

listen 80 accept_filter=httpready;

Apache also supports accept filters through its AcceptFilter directive:

apache

AcceptFilter http httpready

AcceptFilter https dataready

Static vs Dynamic Content

For **static files** (HTML, CSS, JS, images), NGINX is consistently faster in benchmarks. Typical results on FreeBSD hardware:

- NGINX: 25,000-40,000 requests/sec for small static files (1 KB) on a 4-core system.

- Apache (event MPM): 15,000-25,000 requests/sec for the same workload and hardware.

For **dynamic content** (PHP, Python, Ruby), the web server matters less because the bottleneck shifts to the application runtime. Both servers pass requests to the same backend (php-fpm, gunicorn, etc.), so the difference in throughput for dynamic content is typically 5-15%, favoring NGINX slightly due to lower connection overhead.

---

Configuration Style

NGINX: Declarative Block Syntax

NGINX uses a C-like block syntax with nested contexts: http, server, and location blocks. Configuration is hierarchical -- directives set in an outer block are inherited by inner blocks unless overridden.

nginx

http {

server {

listen 80;

server_name example.com;

location / {

root /usr/local/www/example;

index index.html;

}

location /api/ {

proxy_pass http://127.0.0.1:8080;

}

}

}

There is no equivalent to .htaccess. All configuration lives in files that NGINX reads at startup (or reload). This is a deliberate design choice -- it eliminates the per-request filesystem stat calls that .htaccess requires and makes the configuration fully deterministic.

On FreeBSD, NGINX configuration lives in /usr/local/etc/nginx/. The main file is nginx.conf, and the convention is to include per-site files from a sites-enabled/ or conf.d/ directory.

Apache: Directive-Based with .htaccess

Apache uses a flat directive syntax with section containers (, , ). Its most distinctive feature is .htaccess -- per-directory configuration files that Apache checks on every request.

apache

ServerName example.com

DocumentRoot /usr/local/www/example

AllowOverride All

Require all granted

ProxyPass /api/ http://127.0.0.1:8080/

ProxyPassReverse /api/ http://127.0.0.1:8080/

.htaccess is simultaneously Apache's greatest strength and its biggest performance liability. It allows users on shared hosting to control URL rewrites, authentication, and caching without touching the main server config. But Apache must check every directory in the path to a requested file for .htaccess files, adding filesystem overhead to every single request.

On FreeBSD, Apache's configuration lives in /usr/local/etc/apache24/. The main file is httpd.conf, with extras in extra/ and modules in modules/.

---

Module Ecosystem

Apache

Apache's module ecosystem is one of its strongest selling points. Modules are loaded dynamically at runtime with LoadModule directives. Hundreds of modules exist, covering authentication (LDAP, Kerberos, SAML), content manipulation, caching, compression, URL rewriting, and more.

Key Apache modules with no direct NGINX equivalent:

- mod_rewrite -- Regex-based URL rewriting with conditions, flags, and backreferences. NGINX has rewrite and try_files, but complex mod_rewrite rulesets do not translate 1:1.

- mod_security -- Web application firewall. NGINX has the OWASP ModSecurity connector, but it is a separate build.

- mod_php -- Embeds PHP directly in Apache processes. Not recommended for production anymore, but some legacy applications depend on it.

- mod_dav -- WebDAV support built into Apache.

NGINX

NGINX modules are compiled into the binary. The stock FreeBSD pkg install nginx package includes the most common modules (gzip, SSL, proxy, rewrite, fastcgi, headers). If you need additional modules, you have two options:

1. **Install a variant package.** FreeBSD provides nginx-full and nginx-extras packages with more modules.

2. **Build from ports.** cd /usr/ports/www/nginx && make config lets you select exactly which modules to compile.

Third-party NGINX modules (like njs, lua-nginx-module, headers-more) require building from source or using the ports system with the right options enabled.

NGINX's module story is less flexible than Apache's at runtime, but the tradeoff is a smaller, more predictable binary with lower overhead.

---

SSL/TLS Handling

Both servers handle SSL/TLS termination well, and both integrate with [Let's Encrypt on FreeBSD](/blog/lets-encrypt-freebsd/) via certbot or acme.sh.

NGINX SSL Configuration

nginx

server {

listen 443 ssl http2;

server_name example.com;

ssl_certificate /usr/local/etc/letsencrypt/live/example.com/fullchain.pem;

ssl_certificate_key /usr/local/etc/letsencrypt/live/example.com/privkey.pem;

ssl_protocols TLSv1.2 TLSv1.3;

ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;

ssl_prefer_server_ciphers off;

ssl_session_cache shared:SSL:10m;

ssl_session_timeout 1d;

ssl_stapling on;

ssl_stapling_verify on;

}

NGINX handles SSL/TLS efficiently because the encryption/decryption happens in the same event loop as everything else. There is no context-switch overhead between an SSL termination layer and the content-serving layer.

Apache SSL Configuration

apache

ServerName example.com

SSLEngine on

SSLCertificateFile /usr/local/etc/letsencrypt/live/example.com/fullchain.pem

SSLCertificateKeyFile /usr/local/etc/letsencrypt/live/example.com/privkey.pem

SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256

SSLHonorCipherOrder off

SSLSessionCache shmcb:/var/run/ssl_scache(512000)

SSLSessionCacheTimeout 86400

SSLUseStapling on

Apache's mod_ssl is mature and full-featured. In practice, SSL/TLS performance between the two servers is close -- the bottleneck is the cryptographic operations (handled by OpenSSL or LibreSSL on FreeBSD), not the web server's event model. Under very high concurrent SSL handshake loads, NGINX's lower per-connection overhead gives it a slight edge.

Both servers support TLS 1.3 and OCSP stapling on FreeBSD. FreeBSD ships with OpenSSL in base, and the ports tree offers LibreSSL as an alternative.

---

Reverse Proxy Capabilities

Reverse proxying is one of the most common use cases for both servers -- sitting in front of application servers (Node.js, Python, Ruby, Go) to handle SSL termination, static files, load balancing, and caching.

NGINX as Reverse Proxy

NGINX was designed as a reverse proxy from the start. Its proxy_pass directive is clean and the upstream module provides load balancing with multiple algorithms (round-robin, least connections, IP hash).

nginx

upstream app_backend {

least_conn;

server 127.0.0.1:3000;

server 127.0.0.1:3001;

server 127.0.0.1:3002;

}

server {

listen 443 ssl http2;

server_name example.com;

location / {

proxy_pass http://app_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;

proxy_buffering on;

proxy_cache_valid 200 10m;

}

}

NGINX also supports WebSocket proxying, gRPC proxying, and connection keep-alive to backends out of the box. For a full reverse proxy and load balancing setup, see the [NGINX production setup guide](/blog/nginx-freebsd-production-setup/).

Apache as Reverse Proxy

Apache uses mod_proxy and its sub-modules (mod_proxy_http, mod_proxy_balancer, mod_proxy_wstunnel).

apache

BalancerMember http://127.0.0.1:3000

BalancerMember http://127.0.0.1:3001

BalancerMember http://127.0.0.1:3002

ProxySet lbmethod=bybusyness

ServerName example.com

SSLEngine on

ProxyPreserveHost On

ProxyPass / balancer://app_backend/

ProxyPassReverse / balancer://app_backend/

RequestHeader set X-Forwarded-Proto "https"

Apache's reverse proxy is fully functional, but NGINX's implementation is more commonly used and has better documentation coverage for edge cases like WebSocket upgrades, gRPC, and streaming responses. Apache's mod_proxy_balancer also includes a built-in web-based management interface (balancer-manager), which NGINX lacks without the commercial NGINX Plus product.

---

PHP Integration

PHP is the most common dynamic language served by both web servers, and how each handles it matters for both performance and security.

NGINX + php-fpm

NGINX delegates PHP processing to php-fpm (FastCGI Process Manager) over a Unix socket or TCP connection. NGINX handles the static files and passes .php requests to the fpm pool.

nginx

location ~ \.php$ {

fastcgi_pass unix:/var/run/php-fpm.sock;

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

include fastcgi_params;

}

This separation means NGINX and PHP scale independently. You can tune fpm pool sizes based on available RAM without affecting NGINX's connection handling.

Apache + mod_php vs php-fpm

Apache historically embedded PHP directly via mod_php, which loaded the PHP interpreter into every Apache process. This was simple but wasteful -- every Apache process consumed memory for PHP even when serving static files or non-PHP requests. It also required the prefork MPM because mod_php is not thread-safe.

Modern Apache deployments should use php-fpm via mod_proxy_fcgi, which gives the same process separation as NGINX:

apache

SetHandler "proxy:unix:/var/run/php-fpm.sock|fcgi://localhost"

With php-fpm, Apache can use the event MPM, recovering much of the performance gap with NGINX for PHP workloads. The recommendation is clear: use php-fpm regardless of which web server you choose.

On FreeBSD, install php-fpm with:

sh

pkg install php83 php83-extensions

sysrc php_fpm_enable=YES

service php-fpm start

---

Resource Usage

Memory

This is where NGINX wins most decisively. Under load with 10,000 concurrent connections:

- **NGINX**: 2-4 worker processes, each using 10-30 MB. Total: 40-120 MB.

- **Apache (event MPM)**: Thread pool consuming 150-500 MB depending on configuration.

- **Apache (prefork + mod_php)**: 2-10 GB, since each connection requires a separate process with a loaded PHP interpreter.

CPU

For static content, NGINX uses less CPU per request due to fewer context switches and no thread synchronization overhead. For dynamic content proxied to a backend, CPU usage is comparable -- the backend does the heavy lifting.

Under Spike Traffic

NGINX degrades gracefully under traffic spikes. When it runs out of worker connections, new connections queue in the kernel's listen backlog. Existing connections continue being served at full speed.

Apache (prefork/worker) can hit process or thread limits and start refusing connections outright. The event MPM handles spikes better, but it still has a harder ceiling than NGINX's event loop.

---

FreeBSD-Specific Considerations

Installation Paths

| Item | NGINX | Apache |

|------|-------|--------|

| Package | pkg install nginx | pkg install apache24 |

| Config directory | /usr/local/etc/nginx/ | /usr/local/etc/apache24/ |

| Main config | nginx.conf | httpd.conf |

| Binary | /usr/local/sbin/nginx | /usr/local/sbin/httpd |

| Log directory | /var/log/nginx/ | /var/log/ |

| Document root | /usr/local/www/nginx/ | /usr/local/www/apache24/data/ |

| rc.d service | nginx | apache24 |

Ports Options

Building from ports gives you fine-grained control over compiled modules:

sh

# NGINX

cd /usr/ports/www/nginx && make config && make install clean

# Apache

cd /usr/ports/www/apache24 && make config && make install clean

The NGINX ports options dialog lets you toggle modules like HTTP_DAV, HTTP_GEOIP, HTTP_IMAGE_FILTER, HTTP_PERL, MAIL, STREAM, and more. Apache's ports options control MPM selection and which modules get built.

Kernel Tuning

Both servers benefit from the same FreeBSD kernel tuning. Add to /etc/sysctl.conf:

sh

kern.ipc.somaxconn=4096

net.inet.tcp.msl=5000

net.inet.tcp.recvbuf_auto=1

net.inet.tcp.sendbuf_auto=1

Load the accept filters:

sh

kldload accf_http

kldload accf_data

To make them persistent, add to /boot/loader.conf:

sh

accf_http_load="YES"

accf_data_load="YES"

---

Comparison Table

| Feature | NGINX | Apache |

|---------|-------|--------|

| Architecture | Event-driven, async | Process/thread (MPM-based) |

| FreeBSD kqueue | Native, first-class | Supported (event MPM) |

| sendfile support | Yes (default on) | Yes (default on) |

| Memory under 10K connections | 40-120 MB | 150 MB - 10 GB (MPM-dependent) |

| Static file speed | Excellent | Good |

| Dynamic content speed | Excellent (via proxy) | Good-Excellent (via proxy or module) |

| .htaccess support | No | Yes |

| Module loading | Compile-time | Runtime (LoadModule) |

| Reverse proxy | Built-in, excellent | mod_proxy, good |

| Load balancing | Built-in upstream module | mod_proxy_balancer |

| WebSocket proxy | Native support | mod_proxy_wstunnel |

| gRPC proxy | Native support | Limited |

| PHP integration | php-fpm (FastCGI) | php-fpm or mod_php |

| Config reload | nginx -s reload (zero downtime) | apachectl graceful |

| Config complexity | Moderate (learn block syntax) | Low-Moderate (familiar directive syntax) |

| Community docs | Extensive | Extensive |

| FreeBSD package | nginx | apache24 |

| License | BSD-like (2-clause) | Apache License 2.0 |

---

Migration Tips: Apache to NGINX

If you are running Apache on FreeBSD and want to switch to NGINX, here is a practical migration path.

1. Audit Your Apache Configuration

Before touching anything, catalog what Apache features you actually use:

sh

apachectl -M | sort

This lists loaded modules. If you see mod_rewrite, mod_headers, and mod_proxy -- those all have NGINX equivalents. If you see mod_security, mod_dav, or mod_ldap, plan for those gaps.

2. Convert VirtualHosts to Server Blocks

Apache blocks map to NGINX server blocks. The translation is usually straightforward:

- ServerName becomes server_name

- DocumentRoot becomes root

- ProxyPass becomes proxy_pass inside a location block

- permissions become location blocks with allow/deny

3. Replace .htaccess Rules

This is the hardest part. Collect every .htaccess file across your document roots:

sh

find /usr/local/www -name '.htaccess' -exec cat {} \;

Convert rewrite rules to NGINX syntax. Common patterns:

| Apache .htaccess | NGINX equivalent |

|------------------|------------------|

| RewriteRule ^(.*)$ index.php/$1 [L] | try_files $uri $uri/ /index.php$request_uri; |

| RewriteCond %{HTTPS} off / RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301] | return 301 https://$host$request_uri; |

| Header set X-Frame-Options DENY | add_header X-Frame-Options DENY; |

4. Run Both in Parallel

Install NGINX alongside Apache on a different port (e.g., 8080). Test your converted configuration, compare responses, and only switch DNS or firewall rules once everything matches.

5. Switch and Monitor

Move NGINX to port 80/443, stop Apache, and monitor closely for 48 hours. Keep Apache installed but disabled so you can roll back quickly.

sh

sysrc apache24_enable=NO

service apache24 stop

service nginx start

---

Frequently Asked Questions

Is NGINX always faster than Apache on FreeBSD?

For static content and high-concurrency workloads, yes. For low-traffic sites serving dynamic content through php-fpm, the difference is negligible -- maybe 5-10% in response time. The performance gap widens as concurrent connections increase. At 10,000+ concurrent connections, NGINX uses a fraction of the memory and CPU that Apache requires.

Can I run both NGINX and Apache on the same FreeBSD server?

Yes. A common pattern is to run NGINX as a reverse proxy on port 80/443 and Apache on port 8080 as the backend. This gives you NGINX's connection handling and static file performance with Apache's module ecosystem for dynamic content. This is practical for legacy applications that require mod_rewrite or mod_php.

Does Apache's event MPM close the gap with NGINX?

It narrows the gap significantly but does not close it. The event MPM still uses threads, which have higher per-connection overhead than NGINX's event loop. For moderate traffic (under 1,000 concurrent connections), the difference is small enough that other factors -- team familiarity, existing configuration, module requirements -- matter more than raw performance.

Which is more secure?

Neither server is inherently more secure. Both have had vulnerabilities over their long histories. Security depends on your configuration, update practices, and how quickly you patch. NGINX's smaller codebase and lack of .htaccess (which can be a misconfiguration vector) give it a slight edge in attack surface reduction. Apache's mod_security provides WAF capabilities that require extra work to replicate with NGINX.

Should I use the FreeBSD package or build from ports?

Use the package (pkg install) for most deployments. It is faster to install, easier to update, and the default module set covers common use cases. Build from ports only if you need a specific module not included in the package, want to link against a different SSL library (like LibreSSL), or need to apply patches. For a production NGINX setup guide, see the [NGINX on FreeBSD tutorial](/blog/nginx-freebsd-production-setup/).

Which web server do Netflix and WhatsApp use on FreeBSD?

Both Netflix and WhatsApp have publicly discussed their use of FreeBSD. Netflix uses a heavily customized version of NGINX (called Open Connect) on FreeBSD for their content delivery network. This serves a significant portion of global internet traffic. WhatsApp used FreeBSD with Erlang for their messaging backend. For more on why these companies chose FreeBSD, see the [best web servers for FreeBSD overview](/blog/best-web-server-freebsd/).

---

Conclusion

For most FreeBSD deployments in 2026, NGINX is the better web server. Its architecture aligns naturally with FreeBSD's kqueue event system, it uses less memory and CPU under load, and its configuration -- while different from what Apache veterans expect -- is clean and predictable once learned.

Apache remains a solid choice when you need .htaccess per-directory overrides, depend on specific Apache modules, or are running a shared hosting environment where users need to control their own configuration without server-level access.

If you are starting a new project on FreeBSD, install NGINX. If you are running Apache and it works fine for your traffic levels, only migrate if you are hitting performance walls or want to simplify your stack. Either way, make sure you are using php-fpm instead of mod_php, the event MPM instead of prefork, and [Let's Encrypt](/blog/lets-encrypt-freebsd/) for SSL.