How to Install an SSL Certificate on Apache — Step-by-Step

Apache HTTP Server remains one of the most widely used web servers, especially on LAMP stacks running on VPS. Installing SSL on Apache requires enabling mod_ssl and configuring a proper HTTPS VirtualHost. This guide covers both a commercial SSL certificate and a free Let's Encrypt certificate via the Certbot Apache plugin.

Prerequisites: Ubuntu 20.04/22.04 VPS with Apache (apache2) already installed, a domain whose DNS A record points to your server IP, and root or sudo access.

Section 1 — What You Need First

Verify Apache is running:

sudo systemctl status apache2

Open ports 80 and 443 in UFW:

sudo ufw allow 80
sudo ufw allow 443
sudo ufw status

Confirm your domain resolves to your server IP:

dig example.com +short

Section 2 — Enable mod_ssl on Apache

Ubuntu's Apache requires mod_ssl to be explicitly enabled before it can serve HTTPS:

# Enable mod_ssl and mod_rewrite (for redirects)
sudo a2enmod ssl
sudo a2enmod rewrite

# Restart Apache so the modules take effect
sudo systemctl restart apache2

Verify mod_ssl is active:

apache2ctl -M | grep ssl

You should see ssl_module (shared) in the output.

Section 3 — Install a Commercial SSL Certificate on Apache

Once you have received the certificate files from your CA, follow these steps:

3.1 Place the Certificate Files

# Create a secure directory for the SSL files
sudo mkdir -p /etc/apache2/ssl/example.com

# Copy certificate and key files
sudo cp your_domain.crt /etc/apache2/ssl/example.com/certificate.crt
sudo cp your_private.key /etc/apache2/ssl/example.com/private.key

# If the CA provided an intermediate certificate
sudo cp intermediate.crt /etc/apache2/ssl/example.com/ca_bundle.crt

# Set secure permissions
sudo chmod 600 /etc/apache2/ssl/example.com/private.key
sudo chmod 644 /etc/apache2/ssl/example.com/certificate.crt

3.2 Create the HTTPS VirtualHost

sudo nano /etc/apache2/sites-available/example.com-ssl.conf
<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com

    SSLEngine on
    SSLCertificateFile      /etc/apache2/ssl/example.com/certificate.crt
    SSLCertificateKeyFile   /etc/apache2/ssl/example.com/private.key
    # Include this only if you have an intermediate certificate
    SSLCertificateChainFile /etc/apache2/ssl/example.com/ca_bundle.crt

    <Directory /var/www/example.com>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/example.com-ssl-error.log
    CustomLog ${APACHE_LOG_DIR}/example.com-ssl-access.log combined
</VirtualHost>
# Enable the new site
sudo a2ensite example.com-ssl.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

Section 4 — Install Let's Encrypt with the Certbot Apache Plugin

Certbot's Apache plugin manages everything automatically, including VirtualHost modifications:

# Install Certbot and the Apache plugin
sudo apt update
sudo apt install certbot python3-certbot-apache -y

# Obtain a certificate for your domain
sudo certbot --apache -d example.com -d www.example.com

Certbot prompts for:

Tip: The --apache plugin automatically edits your VirtualHost, adds the required SSL directives, and configures the redirect — no manual config changes needed.

Section 5 — Configure VirtualHost and HTTP→HTTPS Redirect Manually

To control the redirect yourself without Certbot modifying your config, edit the port-80 VirtualHost:

sudo nano /etc/apache2/sites-available/example.com.conf
# Port-80 VirtualHost: redirect all traffic to HTTPS
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    # 301 Permanent Redirect
    Redirect permanent / https://example.com/
</VirtualHost>

# Alternative: use mod_rewrite for more flexibility
<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>

⚠️ Caution: Only enable the redirect after confirming your HTTPS VirtualHost is working correctly. Enabling Redirect permanent before HTTPS is live will result in an immediate error for all visitors.

Section 6 — Test Configuration and Reload

Always test your Apache config before reloading:

# Validate Apache configuration syntax
sudo apache2ctl configtest

# If Syntax OK, reload Apache
sudo systemctl reload apache2

# Test HTTPS with curl
curl -I https://example.com

# Verify certificate details with openssl
echo | openssl s_client -connect example.com:443 2>/dev/null | openssl x509 -noout -dates

A valid apache2ctl configtest output ends with Syntax OK. If you see errors, read the message carefully and fix before reloading.

Test Auto-Renewal for Let's Encrypt

# Dry-run: test renewal without actually renewing
sudo certbot renew --dry-run

Auto-Renewal: Certbot registers a systemd timer automatically. Check it with systemctl list-timers | grep certbot. Certificates renew automatically 30 days before expiry.

Let's Encrypt via the Certbot Apache Plugin — Full Walkthrough

Let's Encrypt is a free SSL provider that issues DV (Domain Validation) certificates valid for 90 days with automatic renewal. The Certbot Apache plugin is the friendliest option for beginners because it edits the VirtualHost for you, so you barely have to touch the config yourself. Here is the complete process from start to finish.

# 1) Update the package list and install Certbot + the Apache plugin
sudo apt update
sudo apt install certbot python3-certbot-apache -y

# 2) Confirm Apache has a port-80 VirtualHost whose ServerName matches your domain
#    Certbot reads ServerName/ServerAlias to know which domain to issue for
sudo apache2ctl -S

# 3) Request the certificate and let Certbot edit the VirtualHost automatically
sudo certbot --apache -d example.com -d www.example.com

# 4) To obtain the cert only, without Certbot touching your config (manual VirtualHost)
sudo certbot certonly --apache -d example.com -d www.example.com

When you run certbot --apache, it asks for an email for expiry notices, requires you to accept the Terms of Service, and lets you choose whether to redirect HTTP to HTTPS (choose Redirect). Certbot then creates an example.com-le-ssl.conf file in sites-available automatically, and the certificate files are stored under /etc/letsencrypt/live/example.com/.

Let's Encrypt file locations: fullchain.pem (cert + chain combined), privkey.pem (private key), plus cert.pem and chain.pem — all inside /etc/letsencrypt/live/example.com/. Certbot references fullchain.pem and privkey.pem in the VirtualHost for you.

Configure the Port-443 VirtualHost Manually (SSLCertificateFile / Key / Chain)

For a commercial SSL certificate — or if you want full control over a Let's Encrypt config — create a port-443 VirtualHost and point it at all three certificate parts: the domain certificate, the private key, and the chain (intermediate) certificate.

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com

    SSLEngine on
    # the domain certificate (.crt or cert.pem)
    SSLCertificateFile      /etc/apache2/ssl/example.com/certificate.crt
    # the private key used when generating the CSR (.key or privkey.pem)
    SSLCertificateKeyFile   /etc/apache2/ssl/example.com/private.key
    # the intermediate / chain certificate from the CA (.ca-bundle or chain.pem)
    SSLCertificateChainFile /etc/apache2/ssl/example.com/ca_bundle.crt

    # Recommended: allow only TLS 1.2+ and disable insecure legacy protocols
    SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1
    SSLHonorCipherOrder     on

    <Directory /var/www/example.com>
        AllowOverride All
        Require all granted
    </Directory>

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

On Apache 2.4.8 and later you can bundle the cert and chain into a single file and use SSLCertificateFile alone (no SSLCertificateChainFile needed), but keeping the files separate as shown is easier to read and maintain. The table below summarizes the key directives and their matching files.

Directive File used Description
SSLCertificateFile certificate.crt / fullchain.pem the domain certificate issued by the CA
SSLCertificateKeyFile private.key / privkey.pem the private key paired with the CSR; chmod 600
SSLCertificateChainFile ca_bundle.crt / chain.pem the CA intermediate cert (avoids chain errors on mobile)

Force HTTPS Redirect + Enable HSTS on Apache (.htaccess / VirtualHost)

Once SSL is installed, you should force every request to HTTPS and enable HSTS (HTTP Strict Transport Security) to tell browsers this domain must only be reached over HTTPS. You can do this at the VirtualHost level or in a .htaccess file.

Option 1 — Redirect in the port-80 VirtualHost

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    # 301 permanent redirect to HTTPS
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
</VirtualHost>

Option 2 — Force HTTPS in .htaccess (good for shared DocumentRoot)

# Place this in .htaccess at your DocumentRoot (requires AllowOverride All)
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Option 3 — Enable the HSTS header

Add the HSTS header to the port-443 VirtualHost (enable mod_headers first with sudo a2enmod headers):

# Inside VirtualHost *:443, after the SSL settings
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

⚠️ HSTS caution: Once a browser caches HSTS, it forces HTTPS for the entire max-age period. If your SSL expires or breaks, visitors will be locked out entirely. Only enable HSTS after you are confident HTTPS is stable and auto-renews correctly, and do not add preload until you are truly ready.

Enable mod_ssl, Test with apachectl configtest, Renew, and Common Issues

Before every reload, validate the syntax and confirm the required modules are enabled. The most common verification commands are:

# Enable the required modules (ssl, rewrite, headers)
sudo a2enmod ssl rewrite headers

# Always validate config syntax before reloading
sudo apachectl configtest        # equivalent to apache2ctl configtest

# If you get Syntax OK, reload to apply the config
sudo systemctl reload apache2

# Dry-run a Let's Encrypt renewal (no real renewal)
sudo certbot renew --dry-run

# Check the expiry dates of all certificates
sudo certbot certificates

Common Issues and Fixes

Frequently Asked Questions (FAQ)

Is Let's Encrypt really free, and how does it differ from a paid SSL certificate?

Let's Encrypt is 100% free and DV (it only verifies domain ownership), valid for 90 days with automatic renewal — ideal for general websites. Paid SSL adds OV/EV options that validate organization identity, plus a warranty and CA support. AsiaGB offers both free Let's Encrypt and paid DV SSL starting at 1,000 THB/year with installation support.

I installed SSL on Apache but the site still shows "Not secure" — why?

The most common causes are mixed content (images or scripts loaded over http://), a missing SSLCertificateChainFile leaving an incomplete chain, or not forcing a redirect to HTTPS. Check for mixed content in your browser Console and add the full intermediate certificate.

Should I restart or reload Apache after installing SSL?

Use sudo systemctl reload apache2 — it reloads the config without dropping active connections. Use restart only when first enabling or disabling a module (such as mod_ssl), and always pass configtest first.

Does Certbot really auto-renew Let's Encrypt, or do I need my own cron job?

Modern Certbot installs a systemd timer automatically, so no manual cron is needed. Verify it with systemctl list-timers | grep certbot and test with sudo certbot renew --dry-run. As long as port 80 stays open, certificates renew automatically 30 days before expiry.

SSL on Apache — Setup Checklist

  1. Install Apache and enable mod_ssl and mod_rewrite.
  2. Confirm DNS A record points to your server IP.
  3. Store certificate and key files with permissions 644/600.
  4. Create an HTTPS VirtualHost with the correct SSLCertificateFile directives.
  5. Add a port-80 VirtualHost to redirect HTTP to HTTPS.
  6. Always run apache2ctl configtest before reloading.
  7. Verify with curl -I and openssl s_client.
  8. For Let's Encrypt: test renewal with --dry-run.

SSL Certificates for Your Website

AsiaGB offers RapidSSL, GeoTrust, and DigiCert certificates starting at 1,000 THB/year, backed by global CAs with installation support.

View SSL Plans