Key Takeaways
a2ensite/a2dissite: Ubuntu’s Apache helpers enable and disable virtual hosts by creating symlinks insites-enabled/. Always usea2ensite mysiterather than manually creating symlinks.systemctl reload apache2: Reload applies config changes without dropping active connections. Userestartonly when changing modules or after major configuration changes..htaccessis Apache’s superpower: Per-directory overrides without touching server config. WordPress, Laravel, Symfony all needAllowOverride Allin the virtual host for.htaccessto work.- mod_proxy for reverse proxying:
ProxyPass / http://127.0.0.1:3000/routes all requests to a backend application. Apache handles SSL, static files, and rate limiting; the backend only handles application logic.
Introduction
Direct Answer: How do I install and configure Apache on Ubuntu 24.04 LTS in 2026?
Install Apache with sudo apt-get install -y apache2. It starts automatically and is enabled at boot. Test with curl -I http://localhost — you should see 200 OK with Server: Apache/2.4.x. Create a virtual host by writing a config file to /etc/apache2/sites-available/mysite.conf with a <VirtualHost *:80> block containing ServerName mysite.example.com, DocumentRoot /var/www/mysite, and <Directory /var/www/mysite> AllowOverride All Require all granted </Directory>. Enable with sudo a2ensite mysite.conf, disable the default with sudo a2dissite 000-default.conf, test config with sudo apache2ctl configtest, and reload with sudo systemctl reload apache2. For SSL, install Certbot: sudo apt-get install certbot python3-certbot-apache && sudo certbot --apache -d mysite.example.com.
Part 1: Installation
sudo apt-get update
sudo apt-get install -y apache2
# Verify installation
apache2 -v
sudo systemctl status apache2 --no-pager | grep "Active:"
Expected output:
Server version: Apache/2.4.58 (Ubuntu)
Server built: 2024-01-13T12:00:00
Active: active (running)
# Test default page
curl -sI http://localhost | head -5
Expected output:
HTTP/1.1 200 OK
Date: Tue, 22 Apr 2026 12:00:00 GMT
Server: Apache/2.4.58 (Ubuntu)
Last-Modified: Tue, 22 Apr 2026 09:00:00 GMT
Content-Type: text/html
# Enable essential modules
sudo a2enmod ssl # SSL/TLS
sudo a2enmod rewrite # URL rewriting (required by most PHP frameworks)
sudo a2enmod headers # Security headers
sudo a2enmod proxy # Reverse proxy
sudo a2enmod proxy_http # HTTP proxy
sudo a2enmod deflate # Gzip compression
sudo a2enmod expires # Browser caching headers
sudo systemctl reload apache2
# List enabled modules
apache2ctl -M | grep -E "ssl|rewrite|proxy|headers" | sort
Expected output:
headers_module (shared)
proxy_http_module (shared)
proxy_module (shared)
rewrite_module (shared)
ssl_module (shared)
Part 2: Virtual Hosts
# Create a document root
sudo mkdir -p /var/www/mysite
echo "<h1>My Site — Sovereign</h1>" | sudo tee /var/www/mysite/index.html
sudo chown -R www-data:www-data /var/www/mysite
sudo chmod -R 755 /var/www/mysite
# Create the virtual host configuration
sudo tee /etc/apache2/sites-available/mysite.conf << 'EOF'
<VirtualHost *:80>
ServerName mysite.example.com
ServerAlias www.mysite.example.com
DocumentRoot /var/www/mysite
# Logging
ErrorLog ${APACHE_LOG_DIR}/mysite-error.log
CustomLog ${APACHE_LOG_DIR}/mysite-access.log combined
# Directory permissions and .htaccess support
<Directory /var/www/mysite>
Options -Indexes +FollowSymLinks
AllowOverride All # Allows .htaccess to override settings
Require all granted
</Directory>
# Deny access to hidden files (.git, .env, etc.)
<FilesMatch "^\.">
Require all denied
</FilesMatch>
</VirtualHost>
EOF
# Enable the site, disable default
sudo a2ensite mysite.conf
sudo a2dissite 000-default.conf
# Test configuration
sudo apache2ctl configtest
Expected output:
Syntax OK
sudo systemctl reload apache2
curl -sI http://localhost | grep "200"
Expected output:
HTTP/1.1 200 OK
Part 3: SSL with Let’s Encrypt
sudo apt-get install -y certbot python3-certbot-apache
# Obtain certificate and auto-configure Apache (requires DNS pointing to server)
sudo certbot --apache -d mysite.example.com --non-interactive \
--agree-tos --email [email protected] --redirect
# Verify certificate
sudo certbot certificates
Expected output:
Found the following certs:
Certificate Name: mysite.example.com
Domains: mysite.example.com
Expiry Date: 2026-07-22 (VALID: 89 days)
Certificate Path: /etc/letsencrypt/live/mysite.example.com/fullchain.pem
Private Key Path: /etc/letsencrypt/live/mysite.example.com/privkey.pem
Manually configured HTTPS virtual host:
<VirtualHost *:80>
ServerName mysite.example.com
RewriteEngine On
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
<VirtualHost *:443>
ServerName mysite.example.com
DocumentRoot /var/www/mysite
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/mysite.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mysite.example.com/privkey.pem
# Modern TLS — TLS 1.2 and 1.3 only
SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder on
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
# Security headers
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
<Directory /var/www/mysite>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/mysite-ssl-error.log
CustomLog ${APACHE_LOG_DIR}/mysite-ssl-access.log combined
</VirtualHost>
Part 4: mod_rewrite and .htaccess
.htaccess files allow per-directory configuration without server reloads:
# Example .htaccess for a PHP application (WordPress/Laravel pattern)
cat > /var/www/mysite/.htaccess << 'EOF'
# Ensure RewriteEngine is on
Options -Indexes
RewriteEngine On
# Force HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Remove trailing slash (except for directories)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [R=301,L]
# Laravel/Symfony: route all requests through index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [L]
# Block access to sensitive files
<FilesMatch "\.(env|log|sql|bak|ini|conf)$">
Require all denied
</FilesMatch>
# Gzip compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/plain text/css application/javascript application/json
</IfModule>
# Browser caching
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
</IfModule>
EOF
# Test that .htaccess is applied
sudo apache2ctl configtest && sudo systemctl reload apache2
curl -sI http://localhost/nonexistent-path | head -3
Expected output:
HTTP/1.1 200 OK ← Laravel-style: routes to index.php instead of 404
Part 5: Reverse Proxy to Node.js / Python Backend
sudo tee /etc/apache2/sites-available/proxy-app.conf << 'EOF'
<VirtualHost *:443>
ServerName api.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/api.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/api.example.com/privkey.pem
# Security headers
Header always set X-Frame-Options SAMEORIGIN
Header always set X-Content-Type-Options nosniff
# Proxy all requests to Node.js/Python backend on port 3000
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:3000/
ProxyPassReverse / http://127.0.0.1:3000/
# Pass real client IP to backend
RequestHeader set X-Real-IP %{REMOTE_ADDR}s
RequestHeader set X-Forwarded-For %{REMOTE_ADDR}s
RequestHeader set X-Forwarded-Proto https
# WebSocket support
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/?(.*) "ws://127.0.0.1:3000/$1" [P,L]
ErrorLog ${APACHE_LOG_DIR}/api-error.log
CustomLog ${APACHE_LOG_DIR}/api-access.log combined
</VirtualHost>
EOF
sudo a2ensite proxy-app.conf
sudo apache2ctl configtest && sudo systemctl reload apache2
Part 6: Security Hardening
sudo tee /etc/apache2/conf-available/security-hardening.conf << 'EOF'
# Hide Apache version from responses
ServerTokens Prod
ServerSignature Off
# Disable TRACE method (prevents XST attacks)
TraceEnable Off
# Limit request size (10MB)
LimitRequestBody 10485760
# Timeout settings
Timeout 60
KeepAliveTimeout 5
MaxKeepAliveRequests 100
EOF
sudo a2enconf security-hardening
sudo systemctl reload apache2
# Verify version is hidden
curl -sI http://localhost | grep Server
Expected output:
Server: Apache
Version number hidden — attackers can’t target specific CVEs by version.
Part 7: Performance and Monitoring
# Check virtual host routing (diagnostic command)
sudo apache2ctl -S
Expected output:
VirtualHost configuration:
*:443 mysite.example.com (/etc/apache2/sites-enabled/mysite-le-ssl.conf:2)
*:80 mysite.example.com (/etc/apache2/sites-enabled/mysite.conf:1)
ServerRoot: "/etc/apache2"
Main DocumentRoot: "/var/www/html"
# Live access log monitoring
sudo tail -f /var/log/apache2/access.log | awk '{print $1, $7, $9}'
# Most requested URLs today
sudo awk '{print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -10
# 5xx errors in last hour
sudo awk -v d="$(date '+%d/%b/%Y:%H')" '$0 ~ d && $9 ~ /^5/' /var/log/apache2/access.log | wc -l
# Test SSL configuration
curl -sI https://mysite.example.com | grep -E "HTTP|Strict|X-Frame"
Expected output:
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains
x-frame-options: SAMEORIGIN
Troubleshooting
403 Forbidden on valid files
Cause: Directory permissions wrong, or Require all granted missing from <Directory> block.
Fix:
sudo chown -R www-data:www-data /var/www/mysite
sudo chmod -R 755 /var/www/mysite
# Ensure virtual host has: Require all granted
.htaccess not working
Cause: AllowOverride None in virtual host (Apache default).
Fix: Add to the <Directory> block: AllowOverride All. Confirm mod_rewrite is enabled: apache2ctl -M | grep rewrite.
Port 80/443 already in use (conflict with Nginx)
Cause: Both Apache and Nginx cannot bind the same port.
Fix: Stop one: sudo systemctl stop nginx or configure Apache to use a different port and put Nginx in front as a reverse proxy.
Conclusion
Apache 2.4 is installed, secured, and configured: virtual hosts route domains to correct document roots, SSL terminates at Apache with Let’s Encrypt, .htaccess enables per-directory config for PHP frameworks, and mod_proxy forwards API traffic to backend applications. The security hardening hides the version string and disables unnecessary methods.
Compare with Nginx Reverse Proxy Tutorial — both serve as web front-ends, but Apache’s .htaccess support makes it the right choice for PHP applications, while Nginx’s lower memory footprint suits high-concurrency API gateways.
People Also Ask
When should I choose Apache over Nginx?
Choose Apache when: hosting PHP applications that use .htaccess (WordPress, Drupal, Laravel, Symfony), running shared hosting where tenants need per-directory configuration, or when your team has deep Apache expertise. Nginx is better for: static file serving, reverse proxying, high-concurrency (Nginx uses event-driven architecture vs Apache’s process/thread model), and memory-constrained servers. For most sovereign self-hosted setups, both work — pick Nginx for new greenfield setups, Apache when the application documentation assumes it.
What does a2enmod, a2ensite, and a2enconf do?
These are Ubuntu-specific helper scripts for Apache configuration management. a2enmod rewrite enables the rewrite module by creating a symlink from /etc/apache2/mods-available/rewrite.load to /etc/apache2/mods-enabled/. a2ensite mysite enables a virtual host from sites-available/ to sites-enabled/. a2enconf myconf enables a config snippet. Their counterparts (a2dismod, a2dissite, a2disconf) remove the symlinks to disable. Always run sudo systemctl reload apache2 after any enable/disable operation.
Further Reading
- Nginx Reverse Proxy Tutorial 2026 — Nginx as the alternative front-end
- How to Install Nginx on Ubuntu 24.04 — compare installation and configuration
- Ubuntu 24.04 LTS Server Setup Checklist — server hardening before web server setup
- Docker Compose Tutorial — containerise Apache-based applications
Tested on: Ubuntu 24.04 LTS (Hetzner CX22). Apache 2.4.58, Certbot 2.11.0. Last verified: April 22, 2026.