How to Install and Configure Apache on FreeBSD
Apache HTTP Server remains the most widely deployed web server in the world. On FreeBSD, it integrates cleanly with the system's package infrastructure, rc.d service management, and filesystem layout. This guide covers installation, core configuration, virtual hosts, SSL/TLS with Let's Encrypt, MPM selection, essential modules, and performance tuning for production deployments.
Prerequisites
- FreeBSD 14.0 or later
- Root access
- A domain name pointing to your server (for SSL configuration)
Step 1: Install Apache
Apache is available as a binary package. The current stable branch is 2.4:
shpkg install apache24
This installs the Apache HTTP server, utilities (apachectl, htpasswd, ab), and default configuration files under /usr/local/etc/apache24/.
Enable Apache at Boot
shsysrc apache24_enable="YES"
Start Apache
shservice apache24 start
Verify it is running:
shservice apache24 status curl -I http://localhost
You should see a "200 OK" response with "Apache" in the Server header. The default document root is /usr/local/www/apache24/data/.
Step 2: Understand the File Layout
Apache on FreeBSD follows the standard /usr/local prefix:
| Path | Purpose |
|------|---------|
| /usr/local/etc/apache24/httpd.conf | Main configuration file |
| /usr/local/etc/apache24/extra/ | Additional configuration files (vhosts, SSL, etc.) |
| /usr/local/etc/apache24/modules.d/ | Module-specific configurations |
| /usr/local/etc/apache24/Includes/ | Drop-in include directory |
| /usr/local/www/apache24/data/ | Default document root |
| /usr/local/www/apache24/cgi-bin/ | CGI scripts directory |
| /var/log/httpd-access.log | Access log (default) |
| /var/log/httpd-error.log | Error log (default) |
| /usr/local/libexec/apache24/ | Compiled modules (.so files) |
| /var/run/httpd.pid | PID file |
Step 3: Configure httpd.conf
The main configuration file is /usr/local/etc/apache24/httpd.conf. The default file is heavily commented and functional out of the box. Key directives to review and modify:
Server Identity
sh# Edit the main config vi /usr/local/etc/apache24/httpd.conf
Set the server name to avoid the "Could not reliably determine the server's fully qualified domain name" warning:
shServerName www.example.com:80
Listen Directives
By default, Apache listens on port 80 on all interfaces:
shListen 80
To bind to a specific IP:
shListen 192.168.1.10:80
User and Group
Apache runs worker processes as the www user and group on FreeBSD:
shUser www Group www
Do not change this unless you have a specific reason. The www user has minimal privileges by design.
Directory Permissions
The default document root configuration:
sh<Directory "/usr/local/www/apache24/data"> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory>
For production, disable directory indexing and enable .htaccess if needed:
sh<Directory "/usr/local/www/apache24/data"> Options FollowSymLinks AllowOverride All Require all granted </Directory>
Enable Essential Modules
Uncomment these lines in httpd.conf to enable commonly needed modules:
shLoadModule rewrite_module libexec/apache24/mod_rewrite.so LoadModule ssl_module libexec/apache24/mod_ssl.so LoadModule socache_shmcb_module libexec/apache24/mod_socache_shmcb.so LoadModule headers_module libexec/apache24/mod_headers.so LoadModule expires_module libexec/apache24/mod_expires.so LoadModule deflate_module libexec/apache24/mod_deflate.so LoadModule proxy_module libexec/apache24/mod_proxy.so LoadModule proxy_fcgi_module libexec/apache24/mod_proxy_fcgi.so
After editing, test the configuration:
shapachectl configtest
If the output says "Syntax OK", restart Apache:
shservice apache24 restart
Step 4: Set Up Virtual Hosts
Virtual hosts allow one Apache instance to serve multiple websites. Enable the virtual hosts configuration file by uncommenting this line in httpd.conf:
shInclude etc/apache24/extra/httpd-vhosts.conf
Create a Virtual Host
Edit /usr/local/etc/apache24/extra/httpd-vhosts.conf:
sh<VirtualHost *:80> ServerName example.com ServerAlias www.example.com DocumentRoot "/usr/local/www/example.com" ErrorLog "/var/log/example.com-error.log" CustomLog "/var/log/example.com-access.log" combined <Directory "/usr/local/www/example.com"> Options FollowSymLinks AllowOverride All Require all granted </Directory> </VirtualHost>
Create the document root and set permissions:
shmkdir -p /usr/local/www/example.com chown -R www:www /usr/local/www/example.com
Create a test page:
shecho "<h1>example.com works</h1>" > /usr/local/www/example.com/index.html chown www:www /usr/local/www/example.com/index.html
Multiple Virtual Hosts
Add additional blocks for each site. Apache matches by ServerName and ServerAlias:
sh<VirtualHost *:80> ServerName site1.example.com DocumentRoot "/usr/local/www/site1" <Directory "/usr/local/www/site1"> AllowOverride All Require all granted </Directory> </VirtualHost> <VirtualHost *:80> ServerName site2.example.com DocumentRoot "/usr/local/www/site2" <Directory "/usr/local/www/site2"> AllowOverride All Require all granted </Directory> </VirtualHost>
Test and reload:
shapachectl configtest service apache24 graceful
The graceful restart allows active connections to complete before the new configuration takes effect.
Step 5: Configure SSL/TLS with Let's Encrypt
Install Certbot
shpkg install py311-certbot py311-certbot-apache
Obtain a Certificate
Certbot can automatically configure Apache SSL:
shcertbot --apache -d example.com -d www.example.com
Certbot will:
- Verify domain ownership via HTTP challenge
- Obtain the certificate from Let's Encrypt
- Create or modify the SSL virtual host configuration
- Set up automatic redirect from HTTP to HTTPS
Manual SSL Configuration
If you prefer manual configuration or use certificates from another CA, enable the SSL configuration in httpd.conf:
shInclude etc/apache24/extra/httpd-ssl.conf
Edit /usr/local/etc/apache24/extra/httpd-ssl.conf or create a dedicated vhost:
sh<VirtualHost *:443> ServerName example.com DocumentRoot "/usr/local/www/example.com" SSLEngine on SSLCertificateFile "/usr/local/etc/letsencrypt/live/example.com/fullchain.pem" SSLCertificateKeyFile "/usr/local/etc/letsencrypt/live/example.com/privkey.pem" # Modern TLS configuration SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384 SSLHonorCipherOrder off # HSTS header Header always set Strict-Transport-Security "max-age=63072000" <Directory "/usr/local/www/example.com"> AllowOverride All Require all granted </Directory> </VirtualHost>
HTTP to HTTPS Redirect
Add a redirect in the HTTP virtual host:
sh<VirtualHost *:80> ServerName example.com ServerAlias www.example.com Redirect permanent "/" "https://example.com/" </VirtualHost>
Automatic Certificate Renewal
Certbot sets up a cron job or periodic task for renewal. Verify:
shcertbot renew --dry-run
Add to /etc/periodic.conf for FreeBSD periodic integration:
sh# Add to /etc/crontab or use crontab -e 0 3 * * * /usr/local/bin/certbot renew --quiet --deploy-hook "service apache24 graceful"
Listen on port 443 -- make sure this line exists in httpd.conf:
shListen 443
Step 6: Choose an MPM
MPM (Multi-Processing Module) determines how Apache handles concurrent connections. FreeBSD supports three MPMs:
prefork
One process per connection. Compatible with non-thread-safe modules (e.g., mod_php). Higher memory usage.
shLoadModule mpm_prefork_module libexec/apache24/mod_mpm_prefork.so
Configuration in httpd.conf:
sh<IfModule mpm_prefork_module> StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxRequestWorkers 250 MaxConnectionsPerChild 0 </IfModule>
worker
Hybrid multi-process/multi-threaded. Lower memory footprint. Not compatible with mod_php (use PHP-FPM instead).
shLoadModule mpm_worker_module libexec/apache24/mod_mpm_worker.so
sh<IfModule mpm_worker_module> StartServers 3 MinSpareThreads 75 MaxSpareThreads 250 ThreadsPerChild 25 MaxRequestWorkers 400 MaxConnectionsPerChild 0 </IfModule>
event
Improved worker MPM with better handling of keep-alive connections. Recommended for modern deployments with PHP-FPM.
shLoadModule mpm_event_module libexec/apache24/mod_mpm_event.so
sh<IfModule mpm_event_module> StartServers 3 MinSpareThreads 75 MaxSpareThreads 250 ThreadsPerChild 25 MaxRequestWorkers 400 MaxConnectionsPerChild 0 </IfModule>
Only one MPM can be loaded at a time. Comment out the others. For new deployments serving PHP through PHP-FPM, use the event MPM.
Step 7: Set Up PHP with PHP-FPM
Rather than mod_php (which requires prefork MPM), use PHP-FPM with the event or worker MPM for better performance.
Install PHP-FPM
shpkg install php83 php83-extensions
Enable PHP-FPM
shsysrc php_fpm_enable="YES" service php-fpm start
Configure Apache to Use PHP-FPM
Add to your virtual host or global configuration:
sh<FilesMatch "\.php$"> SetHandler "proxy:fcgi://127.0.0.1:9000" </FilesMatch>
Or using a Unix socket (lower overhead):
sh<FilesMatch "\.php$"> SetHandler "proxy:unix:/var/run/php-fpm.sock|fcgi://localhost" </FilesMatch>
Ensure mod_proxy and mod_proxy_fcgi are loaded (see Step 3).
Create a test PHP file:
shecho "<?php phpinfo(); ?>" > /usr/local/www/example.com/info.php chown www:www /usr/local/www/example.com/info.php
Test and restart:
shapachectl configtest service apache24 restart
Visit https://example.com/info.php to verify PHP is working. Delete this file after testing.
Step 8: Essential Modules
mod_rewrite
URL rewriting is critical for most web applications (WordPress, Laravel, etc.):
shLoadModule rewrite_module libexec/apache24/mod_rewrite.so
Example .htaccess for a PHP application:
shRewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php [QSA,L]
mod_deflate
Enable gzip compression for text-based content:
sh<IfModule mod_deflate.c> AddOutputFilterByType DEFLATE text/html text/plain text/xml AddOutputFilterByType DEFLATE text/css text/javascript AddOutputFilterByType DEFLATE application/javascript application/json AddOutputFilterByType DEFLATE application/xml application/xhtml+xml </IfModule>
mod_expires
Set caching headers for static assets:
sh<IfModule mod_expires.c> ExpiresActive On ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/png "access plus 1 year" ExpiresByType text/css "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" </IfModule>
mod_headers
Add security headers:
sh<IfModule mod_headers.c> Header always set X-Content-Type-Options "nosniff" Header always set X-Frame-Options "SAMEORIGIN" Header always set X-XSS-Protection "1; mode=block" Header always set Referrer-Policy "strict-origin-when-cross-origin" </IfModule>
Step 9: Security Hardening
Hide Server Version
shServerTokens Prod ServerSignature Off
Disable Directory Listing
sh<Directory "/usr/local/www"> Options -Indexes </Directory>
Restrict Access to Sensitive Files
sh<FilesMatch "^\.ht"> Require all denied </FilesMatch> <FilesMatch "\.(env|ini|log|bak)$"> Require all denied </FilesMatch>
Limit Request Size
shLimitRequestBody 10485760
This limits upload size to 10 MB. Adjust based on your application requirements.
Disable Unnecessary Modules
Review loaded modules and disable any you do not use. Each loaded module increases memory usage and attack surface:
sh# List loaded modules apachectl -M
Step 10: Log Management
Log Format
The default "combined" log format provides useful information:
shLogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
Log Rotation
FreeBSD's newsyslog handles log rotation. Add to /etc/newsyslog.conf:
sh/var/log/httpd-access.log www:www 640 7 * @T00 JC /var/run/httpd.pid 30 /var/log/httpd-error.log www:www 640 7 * @T00 JC /var/run/httpd.pid 30
This rotates logs daily, keeps 7 copies, compresses them, and sends a graceful restart signal to Apache.
Separate Logs Per Virtual Host
Each virtual host should have its own logs:
sh<VirtualHost *:443> ServerName example.com ErrorLog "/var/log/example.com-error.log" CustomLog "/var/log/example.com-access.log" combined </VirtualHost>
Performance Tuning
KeepAlive Settings
shKeepAlive On MaxKeepAliveRequests 100 KeepAliveTimeout 5
Keep KeepAliveTimeout low (2-5 seconds) to prevent idle connections from consuming worker slots.
File Descriptor Limits
For high-traffic servers, increase file descriptor limits in /etc/login.conf:
sh# After editing login.conf, rebuild the database cap_mkdb /etc/login.conf
Also set in /etc/sysctl.conf:
shkern.maxfiles=65536 kern.maxfilesperproc=32768
Enable sendfile
FreeBSD's sendfile system call is more efficient for serving static files:
shEnableSendfile On
This is usually enabled by default on FreeBSD.
Frequently Asked Questions
What is the difference between service apache24 restart and service apache24 graceful?
restart stops and starts Apache, dropping all active connections. graceful signals Apache to finish serving active requests before reloading configuration. Always use graceful on production servers when possible.
Can I run Apache and NGINX on the same server?
Yes, but not on the same port. A common pattern is NGINX on port 80/443 as a reverse proxy, with Apache on port 8080 as the backend. This combines NGINX's static file performance with Apache's module ecosystem.
How do I check which MPM is loaded?
shapachectl -V | grep MPM
Why does Apache fail to start after enabling SSL?
Ensure both mod_ssl and mod_socache_shmcb are loaded. Check that your certificate and key files exist and are readable by the www user. Test with apachectl configtest for specific errors.
How do I password-protect a directory?
Use htpasswd to create a password file and configure the directory:
shhtpasswd -c /usr/local/etc/apache24/.htpasswd admin
sh<Directory "/usr/local/www/example.com/admin"> AuthType Basic AuthName "Restricted Area" AuthUserFile /usr/local/etc/apache24/.htpasswd Require valid-user </Directory>
How do I enable HTTP/2?
Load the HTTP/2 module and enable it in SSL virtual hosts:
shLoadModule http2_module libexec/apache24/mod_http2.so
sh<VirtualHost *:443> Protocols h2 http/1.1 # ... rest of SSL config </VirtualHost>
HTTP/2 requires the event or worker MPM. It does not work with prefork.
How do I serve multiple sites with different PHP versions?
Run multiple PHP-FPM pools on different ports or sockets. Configure each virtual host to proxy to the appropriate PHP-FPM instance:
sh# Site using PHP 8.3 <FilesMatch "\.php$"> SetHandler "proxy:unix:/var/run/php83-fpm.sock|fcgi://localhost" </FilesMatch>
Can I use Apache as a reverse proxy?
Yes. Enable mod_proxy and mod_proxy_http:
sh<VirtualHost *:443> ServerName app.example.com ProxyPreserveHost On ProxyPass "/" "http://127.0.0.1:3000/" ProxyPassReverse "/" "http://127.0.0.1:3000/" </VirtualHost>