2026年11月13日
2026年11月13日
Set Up SSL for WordPress with Let’s Encrypt (Certbot, Apache/Nginx)
This article is a localized edition of the Japanese original. Read the Japanese original
Let's Encrypt is a certificate authority that issues SSL/TLS certificates (server certificates) for free. You can automate getting and renewing certificates with a tool called Certbot, and even let it update your Apache or Nginx configuration.
This guide is for WordPress running on a VPS or cloud server where you have root access. If you use shared hosting, the control panel usually has a "free SSL" feature or similar, and you set it up there (you generally cannot install Certbot yourself).
Before you start
- The domain's DNS points to this server. To get a certificate for both
example.comandwww.example.com, both A records (and AAAA records if you use IPv6) must point to this server's IP address. - Port 80 is reachable from outside. Let's Encrypt verifies domain ownership over HTTP (port 80). Open ports 80 and 443 in your firewall or cloud security group.
- The web server configuration already has a
ServerName(Apache) orserver_name(Nginx) for the domain. Certbot uses this to find what to change. - Back up the WordPress database and files before you start. A later step rewrites URLs.
# Check that DNS returns this server's IP address
dig +short example.com A
dig +short www.example.com A
Step 1: Install Certbot
The official Certbot site (certbot.eff.org) recommends installing it with snap.
# Install with snap (the official instructions)
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbot
certbot --version
You can also install it from your OS packages. If you install both an apt or dnf Certbot and the snap Certbot, it becomes hard to tell which one is running, so pick one.
# Using Ubuntu / Debian packages
sudo apt update
sudo apt install certbot python3-certbot-apache # For Apache
sudo apt install certbot python3-certbot-nginx # For Nginx
# On AlmaLinux / Rocky Linux 9 and similar, install from the EPEL repository
sudo dnf install epel-release
sudo dnf install certbot python3-certbot-apache # For Apache
sudo dnf install certbot python3-certbot-nginx # For Nginx
Step 2: Get the certificate
Let Certbot configure Apache or Nginx too
This is the easiest way. Certbot gets the certificate, adds the HTTPS configuration to the web server and also sets up the redirect from HTTP to HTTPS.
# For Apache
sudo certbot --apache -d example.com -d www.example.com
# For Nginx
sudo certbot --nginx -d example.com -d www.example.com
The first time, you are asked for an email address and to agree to the terms of service. Let's Encrypt stopped sending certificate expiration emails on June 4, 2025, so registering an email address does not get you expiry notices. Renewal is handled by the automatic renewal in Step 5.
For Apache on Ubuntu, an HTTPS configuration file whose name ends in -le-ssl.conf is created in /etc/apache2/sites-available/.
Get only the certificate and write the configuration yourself
If you want to manage the web server configuration yourself, use certonly. --webroot verifies the domain by placing a check file in the document root while the web server keeps running.
sudo certbot certonly --webroot -w /var/www/html -d example.com -d www.example.com
The certificate files are stored in the following locations. In the web server configuration, use fullchain.pem and privkey.pem.
| File | Contents |
|---|---|
/etc/letsencrypt/live/example.com/fullchain.pem |
Certificate plus intermediate certificate |
/etc/letsencrypt/live/example.com/privkey.pem |
Private key |
/etc/letsencrypt/live/example.com/cert.pem |
Certificate only |
/etc/letsencrypt/live/example.com/chain.pem |
Intermediate certificate only |
While you are practicing or experimenting with the setup, add --test-cert (same as --staging) to get a certificate from the staging environment. The production environment limits how many certificates you can get, so if you expect several failed attempts, test against staging first and then request the real certificate.
Step 3: Check the web server configuration
If you used certonly, write the HTTPS configuration yourself. Here is an Nginx example.
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on; # Syntax for Nginx 1.25.1 and later
server_name example.com www.example.com;
root /var/www/html;
index index.php;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}
}
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
http2 on;is the syntax for Nginx 1.25.1 and later. On older versions, such as Nginx 1.24 on Ubuntu 24.04, writelisten 443 ssl http2;instead of that line. Check your version withnginx -v.- The PHP-FPM socket path depends on the installed PHP version (the example uses PHP 8.3). Check it with
ls /run/php/. - For detailed settings such as cipher suites, generate a configuration that matches your web server and version with Mozilla's "SSL Configuration Generator".
If you write the Apache configuration yourself, enable the SSL module with sudo a2enmod ssl, and in the HTTPS <VirtualHost *:443> set SSLEngine on, SSLCertificateFile (fullchain.pem) and SSLCertificateKeyFile (privkey.pem).
After changing the configuration, check the syntax and then reload.
# Nginx
sudo nginx -t && sudo systemctl reload nginx
# Apache (Ubuntu / Debian)
sudo apache2ctl configtest && sudo systemctl reload apache2
Open https://example.com/ in a browser and confirm that the padlock appears and that the certificate issuer is Let's Encrypt.
Step 4: Switch WordPress URLs to HTTPS
Installing a certificate alone does not change WordPress, which keeps outputting links and images with http:// URLs. Switch the WordPress settings and the URLs written in your content. This example uses WP-CLI.
# Export the database before you start
wp db export backup-before-https.sql
# Set "WordPress Address" and "Site Address" to HTTPS
wp option update siteurl 'https://example.com'
wp option update home 'https://example.com'
# Replace http:// URLs in posts and elsewhere (check the count first with --dry-run)
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 --report-changed-only
# Require HTTPS for the admin area and login
wp config set FORCE_SSL_ADMIN true --raw
# Clear the cache
wp cache flush
- Without WP-CLI, change "WordPress Address (URL)" and "Site Address (URL)" under "Settings" → "General" to
https://. To replace URLs in your content, use a search-and-replace plugin. wp search-replacereplaces values without breaking serialized data (widget and plugin settings). Theguidcolumn is a post identifier, so leave it unchanged.- If you have also written URLs in another form, such as
http://www.example.com, replace that form too.
Even after the replacement, if a theme or plugin loads images or scripts over http://, the browser warns that parts of the page are not secure (mixed content). Find the URLs being loaded from the warnings in the browser developer tools console and fix them.
Behind Cloudflare or a load balancer
When HTTPS ends at a proxy in front and requests reach the WordPress server over HTTP, WordPress cannot tell that it is being accessed over HTTPS, and you can get a redirect loop. In that case, add the following to wp-config.php before require_once ABSPATH . 'wp-settings.php';.
<?php
// Tell WordPress the request came over HTTPS, based on the header the proxy adds
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& false !== strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) ) {
$_SERVER['HTTPS'] = 'on';
}
Anyone can send this header, so use this setting only when requests cannot reach the WordPress server without going through the proxy.
Step 5: Check automatic renewal
Certbot packages install a timer (or cron job) that renews certificates automatically. Only certificates that are due for renewal are renewed.
# Test the renewal process (does not actually renew)
sudo certbot renew --dry-run
# Check the timer
systemctl list-timers | grep -i certbot
The timer name depends on how you installed Certbot: snap.certbot.renew.timer for snap, certbot.timer for Ubuntu / Debian packages and certbot-renew.timer for EPEL packages. With EPEL, if it does not appear in the list, enable it with sudo systemctl enable --now certbot-renew.timer.
For certificates obtained with --apache or --nginx, Certbot reloads the web server after renewal. If you used certonly --webroot, set up your own step (a deploy hook) to reload the web server after renewal.
# Create a hook that reloads Nginx only when a certificate is renewed
printf '#!/bin/sh\nsystemctl reload nginx\n' | sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Certificate lifetimes are getting shorter
As of October 2026, Let's Encrypt certificates are valid for 90 days by default. According to Let's Encrypt's announcement, the default lifetime is scheduled to drop to 64 days in February 2027 and to 45 days in February 2028. If you have hard-coded renewal in cron, such as "renew on the 1st of every month", it will no longer be frequent enough, so leave renewal to Certbot's timer and let certbot renew renew whatever is close to expiring.
Notes
- If you set HSTS (the
Strict-Transport-Securityheader), browsers stop using HTTP for the period you specify, which makes it hard to go back if you later want to stop using HTTPS. If you set it, start with a shortmax-age(for example 300 seconds) and increase it later. - A wildcard certificate such as
*.example.comcannot be obtained with HTTP validation; it requires validation through a DNS TXT record (DNS-01). Use a Certbot plugin for your DNS provider, or register the TXT record by hand with--manual --preferred-challenges dns. The manual method cannot renew automatically as is. - When you cannot get a certificate, check the domain and reason shown in the error message. The usual causes are that DNS still points to a different server, port 80 is closed, or
.htaccessor redirect settings keep requests from reaching the validation URL (/.well-known/acme-challenge/).
Summary
- Check DNS and port 80, then install Certbot with snap or your OS packages
- Use
certbot --apacheorcertbot --nginxto get the certificate and configure the web server in one step - In WordPress, set
siteurlandhometo HTTPS, replace URLs in content withwp search-replaceand setFORCE_SSL_ADMIN - Confirm automatic renewal with
certbot renew --dry-runand the timer. Lifetimes are scheduled to get shorter from 2027, so leave renewal to Certbot
Related articles
Questions about WordPress? The publisher of this site is based in Japan and replies in Japanese. Contact (Japanese)