Configure Apache web server with virtual hosts and SSL certificates

Intermediate 45 min Apr 03, 2026 30 views
Ubuntu 24.04 Ubuntu 22.04 Debian 12 AlmaLinux 9 Rocky Linux 9 Fedora 41

Set up Apache HTTP server with multiple virtual hosts, secure SSL certificates from Let's Encrypt, and implement security hardening for production workloads.

Prerequisites

  • Root or sudo access
  • Domain names pointed to your server
  • Basic command line knowledge

What this solves

Apache web server is one of the most widely used HTTP servers, powering millions of websites worldwide. This tutorial shows you how to configure Apache with multiple virtual hosts to serve different domains from a single server, secure them with Let's Encrypt SSL certificates, and implement security hardening measures for production environments.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest versions of all packages.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install Apache web server

Install the Apache HTTP server package along with essential SSL modules for HTTPS support.

sudo apt install -y apache2 apache2-utils ssl-cert
sudo dnf install -y httpd mod_ssl openssl

Enable and start Apache service

Enable Apache to start automatically on boot and start the service immediately.

sudo systemctl enable --now apache2
sudo systemctl status apache2
sudo systemctl enable --now httpd
sudo systemctl status httpd

Configure firewall rules

Open HTTP (port 80) and HTTPS (port 443) in your firewall to allow web traffic.

sudo ufw allow 'Apache Full'
sudo ufw status
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Configure virtual hosts

Create directory structure for websites

Create document root directories for each domain you want to host. This example uses example.com and blog.example.com.

sudo mkdir -p /var/www/example.com/html
sudo mkdir -p /var/www/blog.example.com/html
sudo mkdir -p /var/www/example.com/logs
sudo mkdir -p /var/www/blog.example.com/logs

Set correct ownership and permissions

Set proper ownership for web directories so Apache can read the files. Never use chmod 777 as it creates security vulnerabilities.

sudo chown -R www-data:www-data /var/www/
sudo chmod -R 755 /var/www/
sudo chown -R apache:apache /var/www/
sudo chmod -R 755 /var/www/
Never use chmod 777. It gives every user on the system full access to your files. Instead, fix ownership with chown and use minimal permissions like 755 for directories and 644 for files.

Create sample HTML files

Create simple test pages for each domain to verify virtual host configuration.

<!DOCTYPE html>
<html>
<head>
    <title>Welcome to example.com</title>
</head>
<body>
    <h1>Success! example.com virtual host is working</h1>
    <p>This is the main website.</p>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
    <title>Welcome to blog.example.com</title>
</head>
<body>
    <h1>Success! blog.example.com virtual host is working</h1>
    <p>This is the blog subdomain.</p>
</body>
</html>

Create virtual host configuration files

Create Apache virtual host configuration files for each domain. These define how Apache handles requests for different domains.

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/html
    ErrorLog /var/www/example.com/logs/error.log
    CustomLog /var/www/example.com/logs/access.log combined
    
    <Directory /var/www/example.com/html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
<VirtualHost *:80>
    ServerName blog.example.com
    DocumentRoot /var/www/blog.example.com/html
    ErrorLog /var/www/blog.example.com/logs/error.log
    CustomLog /var/www/blog.example.com/logs/access.log combined
    
    <Directory /var/www/blog.example.com/html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/html
    ErrorLog /var/www/example.com/logs/error.log
    CustomLog /var/www/example.com/logs/access.log combined
    
    <Directory /var/www/example.com/html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
<VirtualHost *:80>
    ServerName blog.example.com
    DocumentRoot /var/www/blog.example.com/html
    ErrorLog /var/www/blog.example.com/logs/error.log
    CustomLog /var/www/blog.example.com/logs/access.log combined
    
    <Directory /var/www/blog.example.com/html>
        Options -Indexes +FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Enable virtual hosts

Enable the virtual host configurations and reload Apache to apply changes.

sudo a2ensite example.com.conf
sudo a2ensite blog.example.com.conf
sudo a2dissite 000-default.conf
sudo systemctl reload apache2
sudo systemctl reload httpd

Configure SSL certificates with Let's Encrypt

Install Certbot

Install Certbot, the official Let's Encrypt client, to automatically obtain and manage SSL certificates.

sudo apt install -y certbot python3-certbot-apache
sudo dnf install -y certbot python3-certbot-apache

Obtain SSL certificates

Use Certbot to automatically obtain SSL certificates for your domains. This will also automatically configure Apache SSL virtual hosts.

sudo certbot --apache -d example.com -d www.example.com
sudo certbot --apache -d blog.example.com
Note: Ensure your domains are pointed to your server's IP address before running certbot, as Let's Encrypt needs to verify domain ownership.

Test automatic renewal

Let's Encrypt certificates expire after 90 days. Test the automatic renewal process to ensure your certificates stay valid.

sudo certbot renew --dry-run

Set up automatic renewal cron job

Create a cron job to automatically renew certificates before they expire.

sudo crontab -e

Add this line to run renewal checks twice daily:

0 12   * /usr/bin/certbot renew --quiet

Security hardening and performance optimization

Enable essential Apache modules

Enable security and performance modules for better protection and speed.

sudo a2enmod ssl
sudo a2enmod headers
sudo a2enmod rewrite
sudo a2enmod deflate
sudo a2enmod expires

These modules are typically enabled by default in RHEL-based distributions. Verify with:

sudo httpd -M | grep -E '(ssl|headers|rewrite|deflate|expires)'

Configure security headers

Add security headers to protect against common web vulnerabilities. Create a security configuration file.

# Security Headers
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"

Hide Apache version

ServerTokens Prod ServerSignature Off
sudo a2enconf security-headers
# Security Headers
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';"

Hide Apache version

ServerTokens Prod ServerSignature Off

Configure compression and caching

Enable gzip compression and browser caching to improve website performance.

# Enable compression
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>

Browser caching

<IfModule mod_expires.c> ExpiresActive On ExpiresByType text/css "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" ExpiresByType image/png "access plus 1 year" ExpiresByType image/jpg "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" </IfModule>
sudo a2enconf performance
# Enable compression
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>

Browser caching

<IfModule mod_expires.c> ExpiresActive On ExpiresByType text/css "access plus 1 month" ExpiresByType application/javascript "access plus 1 month" ExpiresByType image/png "access plus 1 year" ExpiresByType image/jpg "access plus 1 year" ExpiresByType image/jpeg "access plus 1 year" ExpiresByType image/gif "access plus 1 year" ExpiresByType image/svg+xml "access plus 1 year" </IfModule>

Configure rate limiting

Install and configure mod_evasive to protect against DDoS attacks and excessive requests.

sudo apt install -y libapache2-mod-evasive
sudo a2enmod evasive
<IfModule mod_evasive24.c>
    DOSHashTableSize    10000
    DOSPageCount        2
    DOSSiteCount        50
    DOSPageInterval     1
    DOSSiteInterval     1
    DOSBlockingPeriod   3600
    DOSLogDir           "/var/log/apache2"
    DOSEmailNotify      admin@example.com
</IfModule>
sudo dnf install -y epel-release
sudo dnf install -y mod_evasive
LoadModule evasive24_module modules/mod_evasive24.so

<IfModule mod_evasive24.c>
    DOSHashTableSize    10000
    DOSPageCount        2
    DOSSiteCount        50
    DOSPageInterval     1
    DOSSiteInterval     1
    DOSBlockingPeriod   3600
    DOSLogDir           "/var/log/httpd"
    DOSEmailNotify      admin@example.com
</IfModule>

Restart Apache to apply all changes

Restart Apache to activate all security and performance configurations.

sudo systemctl restart apache2
sudo systemctl restart httpd

Verify your setup

Test your Apache configuration and SSL certificates to ensure everything is working correctly.

# Check Apache configuration syntax
sudo apache2ctl configtest

Check service status

sudo systemctl status apache2

Test HTTP to HTTPS redirect

curl -I http://example.com

Test SSL certificate

ssl-labs-ssl-test --verbosity=info example.com

Check virtual host configuration

sudo apache2ctl -S

You can also test in your browser by visiting your domains. HTTP requests should automatically redirect to HTTPS, and you should see valid SSL certificates.

Common issues

Symptom Cause Fix
403 Forbidden error Incorrect file permissions or ownership Run sudo chown -R www-data:www-data /var/www/ and sudo chmod -R 755 /var/www/
Virtual host shows default page Virtual host not enabled or DNS not configured Run sudo a2ensite sitename.conf and check DNS settings
SSL certificate errors Domain not pointing to server or firewall blocking port 80 Verify DNS and ensure port 80 is open for Let's Encrypt validation
Apache won't start Configuration syntax error Run sudo apache2ctl configtest to identify errors
Performance issues Default Apache configuration not optimized Tune MaxRequestWorkers and enable caching modules

Next steps

Automated install script

Run this to automate the entire setup

#apache #virtual-hosts #ssl-certificates #lets-encrypt #web-server

Need help?

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. From initial setup to ongoing operations.

Talk to an engineer