2026年11月12日

2026年11月12日

Enable SSL/TLS (HTTPS) on Apache with Let’s Encrypt and Certbot

This article is a localized edition of the Japanese original. Read the Japanese original

To use HTTPS with Apache HTTP Server, you obtain a server certificate, enable mod_ssl, and configure the certificate in a VirtualHost on port 443. The usual approach is to use a free Let's Encrypt certificate and Certbot, the client Let's Encrypt recommends, to obtain and renew it.

Older guides obtained certificates with a package and command named letsencrypt. That is Certbot's former name; today you use the certbot package and the certbot command. Using Apache 2.4 on Ubuntu / Debian as the example, this article covers obtaining a certificate with Certbot, configuring Apache, checking automatic renewal, and changing the settings on the WordPress side. Differences on RHEL-based systems (AlmaLinux, Rocky Linux, and so on) are summarized in a later section.

Prerequisites and preparation

Before you start, confirm the following.

  • The A / AAAA records for the domain (for example example.com and www.example.com) point to this server's IP address
  • Ports 80 and 443 are reachable from outside (Let's Encrypt validates over port 80)
  • The site loads over HTTP (port 80), and its VirtualHost has a ServerName

If you use ufw as your firewall, open Apache's ports 80 and 443.

sudo ufw allow 'Apache Full'

Here is an example port 80 VirtualHost. Put it in /etc/apache2/sites-available/example.com.conf and enable it with sudo a2ensite example.com.conf.

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

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

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

AllowOverride All lets WordPress use its .htaccess (permalinks). You don't need it if you aren't hosting WordPress.

Install Certbot

On Ubuntu / Debian, you can install Certbot and its Apache plugin as packages.

sudo apt update
sudo apt install certbot python3-certbot-apache

The official Certbot site also describes installing it via snap. Use only one of the two; don't install both.

Method A: Let Certbot configure Apache too

The quickest way is to use the Apache plugin (--apache) to obtain the certificate and configure Apache in one go.

sudo certbot --apache -d example.com -d www.example.com

The first time, you're asked for an email address and to agree to the terms of service. Certbot finds the target from the ServerName / ServerAlias of the port 80 VirtualHost and does the following.

  • Saves the certificate in /etc/letsencrypt/live/example.com/
  • Creates a VirtualHost for port 443 (for example /etc/apache2/sites-available/example.com-le-ssl.conf) and enables mod_ssl
  • Sets up a redirect from HTTP to HTTPS (recent Certbot versions do this by default)

When it finishes, open https://example.com/ in a browser to check. If you'd rather manage the configuration yourself, use Method B below.

Method B: Obtain only the certificate and configure Apache by hand

Obtain the certificate

With certonly and --webroot, Certbot obtains only the certificate without touching the Apache configuration. For -w, specify the DocumentRoot of the port 80 VirtualHost.

sudo certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com \
  --deploy-hook "systemctl reload apache2"

--deploy-hook is a command that runs when the certificate has been renewed successfully. It's saved in the renewal configuration file (/etc/letsencrypt/renewal/example.com.conf), so Apache loads the new certificate after every automatic renewal.

Once obtained, the following files exist.

File Contents
/etc/letsencrypt/live/example.com/fullchain.pem Server certificate and intermediate certificate
/etc/letsencrypt/live/example.com/privkey.pem Private key

Enable mod_ssl

sudo a2enmod ssl

Create the port 443 VirtualHost

Create /etc/apache2/sites-available/example.com-ssl.conf.

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

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

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

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

On Apache 2.4.8 and later, if you point SSLCertificateFile at fullchain.pem, which includes the intermediate certificate, you don't need SSLCertificateChainFile. If the intermediate certificate is missing, some browsers and curl report a certificate error.

Enable the site, check the syntax, then reload.

sudo a2ensite example.com-ssl.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

If apache2ctl configtest doesn't return Syntax OK, fix the line it reports before reloading.

Redirect HTTP to HTTPS

Replace the port 80 VirtualHost with one that only forwards to HTTPS.

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    Redirect permanent / https://example.com/
</VirtualHost>

Certificate renewal keeps working with this setup. Let's Encrypt's validation requests (/.well-known/acme-challenge/) are forwarded to HTTPS, and if the port 443 DocumentRoot is the same directory you passed with -w, they can be answered there. If you change DocumentRoot, update the webroot setting in /etc/letsencrypt/renewal/example.com.conf to match.

Specify the TLS versions

The old TLS 1.0 / 1.1 no longer work in major browsers. In the server-wide configuration (/etc/apache2/mods-available/ssl.conf on Ubuntu / Debian), allow only TLS 1.2 and 1.3.

SSLProtocol -all +TLSv1.2 +TLSv1.3

For detailed recommendations such as cipher suites, use Mozilla's "SSL Configuration Generator" and enter your server and OpenSSL versions. After making changes, run sudo apache2ctl configtest and sudo systemctl reload apache2.

Check automatic renewal

As of October 2026, Let's Encrypt certificates are valid for 90 days. Let's Encrypt has announced that it will shorten the validity period in stages: default certificates are scheduled to become 64 days from February 2027 and 45 days from February 2028. Manual renewal becomes impractical, so make sure automatic renewal is working.

# Is the renewal timer registered?
systemctl list-timers | grep certbot

# Test the renewal process without actually renewing
sudo certbot renew --dry-run

# List obtained certificates and their expiry dates
sudo certbot certificates

Certbot installed with apt uses certbot.timer, and the snap version uses snap.certbot.renew.timer; both check twice a day whether renewal is needed. If --dry-run fails, check that port 80 isn't closed, that DNS hasn't changed, and that the webroot path is correct.

Let's Encrypt stopped sending expiration notice emails in 2025, so it's a good idea to set up your own expiry monitoring.

Switch WordPress to HTTPS

Once the server responds over HTTPS, change the site URLs in WordPress as well.

  1. In the admin area, go to "Settings" → "General" (設定 → 一般 in the Japanese UI), change "WordPress Address (URL)" and "Site Address (URL)" to URLs starting with https://, and save.
  2. Replace any http:// URLs left in content and settings. If you can use WP-CLI, first check the count with --dry-run, then run it for real.
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid --dry-run
wp search-replace 'http://example.com' 'https://example.com' --skip-columns=guid

Back up the database before you start. If images or scripts are still loaded over http://, browsers warn about or block them as "mixed content".

Verify it works

# Check the response headers
curl -I https://example.com/

# Check that HTTP redirects
curl -I http://example.com/

# Check the certificate's subject and expiry dates
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

Also open https://example.com/ and https://www.example.com/ in a browser and confirm there are no certificate errors.

Differences on RHEL-based systems (AlmaLinux, Rocky Linux)

Item Ubuntu / Debian RHEL-based
SSL module sudo a2enmod ssl sudo dnf install mod_ssl
Configuration files /etc/apache2/sites-available/ /etc/httpd/conf.d/*.conf
Certbot apt install certbot python3-certbot-apache Enable EPEL, then dnf install certbot python3-certbot-apache
Service name apache2 httpd
Syntax check apache2ctl configtest apachectl configtest
Firewall ufw allow 'Apache Full' firewall-cmd --permanent --add-service=https, then firewall-cmd --reload

When you use Certbot from EPEL on RHEL-based systems, the renewal timer (certbot-renew.timer) may not be enabled automatically. Enable it with sudo systemctl enable --now certbot-renew.timer and confirm with systemctl list-timers. Also change the reload command in --deploy-hook to systemctl reload httpd.

Common problems

  • Obtaining the certificate fails: typical causes are DNS still pointing to another server, port 80 being closed, or a proxy such as Cloudflare preventing the validation request from arriving. Check whether the URL in the error can be opened from outside.
  • Apache won't start because of SSLEngine: mod_ssl isn't enabled. Run sudo a2enmod ssl.
  • HTTPS shows a different site: the ServerName / ServerAlias of the port 443 VirtualHost doesn't match, or the site isn't enabled. Use sudo apache2ctl -S to see which VirtualHost answers which name.
  • Redirects loop: if .htaccess or a WordPress plugin also forwards to HTTPS, the redirects can double up and loop. Keep the redirect in one place only.

Summary

  • Obtain certificates with certbot, not the old letsencrypt name.
  • For speed, use certbot --apache; to manage the configuration yourself, use certbot certonly --webroot and write the port 443 VirtualHost.
  • Set SSLCertificateFile to fullchain.pem and SSLCertificateKeyFile to privkey.pem.
  • Allow only TLS 1.2 and 1.3, and permanently redirect HTTP to HTTPS.
  • Confirm automatic renewal with certbot renew --dry-run and the timer, and change the WordPress URLs to https://.

Related articles

Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲