Ubuntu 26.04 Tutorial / Issue HTTPS Certificates and Verify Renewal and Expiry
Set up HTTPS only after preparing a domain you control and its validation route. Certificate issuance, the certificate actually served externally, scheduled renewal, and expiry monitoring are separate checks.
Verify domain and public HTTP prerequisites
The loopback .test site on 8080 from the Nginx lesson is not eligible for public certificate issuance. Prepare a real site whose DNS, Nginx server_name, document root, and port 80 response are correct. If an AAAA record exists, the IPv6 route must work too.
Enter the real domain for the following queries. Install Ubuntu’s dnsutils package if dig is absent. Verify DNS and HTTP from a separate network and review host, router, and cloud rules together.
read -r -p "Domain you control: " domain
dig +short A "$domain"
dig +short AAAA "$domain"
curl -I "http://$domain/"
Complete the real-domain HTTP site first
This section targets a separate Ubuntu web server reached at a public address through a domain you control. The host-only VM used in the static IP lab is not directly internet-reachable. Without public hosting/DNS, defer issuance or separately choose an official DNS-01 plugin and suitable credential storage.
Prepare Nginx/curl and point the domain’s A record at the public IPv4 address. An advertised AAAA also needs a working IPv6 route. Save the complete script as ~/prepare-public-http.sh and run bash ~/prepare-public-http.sh YOUR_DOMAIN. It accepts ordinary ASCII hostnames; use verified punycode for an internationalized domain. It stops on existing lab paths.
set -eu
if [ "$#" -ne 1 ]; then
printf 'Usage: bash prepare-public-http.sh YOUR_DOMAIN\n' >&2; exit 2
fi
domain=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]')
if [[ ! "$domain" =~ ^([a-z0-9]([a-z0-9-]*[a-z0-9])?\.)+[a-z]([a-z0-9-]*[a-z0-9])?$ ]]; then
printf 'Use a valid ASCII hostname you control.\n' >&2; exit 2
fi
case "$domain" in
*.test|*.invalid|*.localhost|*.example|example.com|*.example.com)
printf 'Use your real domain, not an example name.\n' >&2; exit 2 ;;
esac
site=/etc/nginx/sites-available/ubuntu-course-public
enabled=/etc/nginx/sites-enabled/ubuntu-course-public
root_dir=/srv/ubuntu-course-public
for path in "$site" "$enabled" "$root_dir"; do
if sudo test -e "$path" || sudo test -L "$path"; then
printf 'Existing lab path: %s; stop and inspect.\n' "$path" >&2; exit 1
fi
done
backup_dir=$(sudo mktemp -d /root/nginx-before-public.XXXXXX)
sudo cp -a /etc/nginx "$backup_dir/"
sudo install -d -m 0755 "$root_dir"
printf 'public-http-lab\n' | sudo sh -c 'set -C; cat > "$1/index.html"' sh "$root_dir"
sudo chmod 0644 "$root_dir/index.html"
sudo sh -c 'set -C; cat > "$1"' sh "$site" <<EOF
server {
listen 80;
listen [::]:80;
server_name $domain;
root /srv/ubuntu-course-public;
index index.html;
location / {
try_files \$uri \$uri/ =404;
}
}
EOF
sudo ln -s "$site" "$enabled"
sudo nginx -t
sudo systemctl reload nginx
printf 'HTTP site prepared; backup: %s\n' "$backup_dir"
If IPv6 listening is unavailable, adjust that listen line to the actual policy and do not advertise unusable AAAA records. Continue only after creation, validation, and reload succeed. On failure, compare only this change with the backup and preserve other sites.
On the new public server, if policy permits public HTTP/HTTPS and UFW is already managed, add these allowances. Preserve SSH and prepare matching provider firewall/security-group paths. This does not enable UFW. Allowing only lab client .10 is insufficient for external HTTP-01 validation.
sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw status verbose
The standalone script’s domain variable does not persist in the parent shell. Re-enter the real hostname in the current Bash session and verify public-http-lab from another internet connection too. Compare hostname, body, and A/AAAA paths before proceeding to Certbot.
read -r -p "Domain you control: " domain
dig +short A "$domain"
dig +short AAAA "$domain"
curl --fail --show-error "http://$domain/"
Choose one Certbot installation route
If Certbot already exists, inspect its origin and renewal job before mixing APT, Snap, or pip installations. This example chooses the official Snap route on a new dedicated server. Review its classic-access requirement and use the full command path to avoid invoking another installation.
sudo snap install --classic certbot
/snap/bin/certbot --version
Certbot’s official Nginx/Snap instructions cover installation, issuance, and renewal testing.
Back up and validate before issuance
(
set -eu
backup=$(sudo mktemp -d /root/nginx-before-tls.XXXXXX)
sudo cp -a /etc/nginx "$backup/"
sudo nginx -t
sudo /snap/bin/certbot --nginx -d "$domain"
)
Confirm that domain contains the real hostname in the same Bash session and run Certbot only after a successful syntax check. Review email, terms, and redirect choices. Automatic installation edits Nginx files, so compare changes with the backup.
HTTP-01 needs a route reachable by the external validator. For other environments, select a documented alternative such as an official DNS plugin and protect minimally scoped DNS credentials. Investigate DNS, ports, and validation logs before repeating failed issuance requests.
Check external HTTPS and the served certificate
curl --fail --show-error "https://$domain/"
openssl s_client -connect "$domain:443" -servername "$domain" -verify_return_error -verify_hostname "$domain" </dev/null
Check hostname and chain verification and the expected web response. Do not manufacture success with curl -k. A local certificate file may be current while a proxy, load balancer, or another IPv6 server serves an older one; test the user-facing route.
Test renewal and monitor expiry
sudo /snap/bin/certbot certificates
sudo /snap/bin/certbot renew --dry-run
systemctl list-timers --all | grep certbot
A dry run tests the current validation/renewal route; it does not guarantee future scheduled success. Inspect timers and logs and arrange failure and external-expiry alerts. Read the actual notAfter date instead of assuming a fixed certificate lifetime.
For rollback, review only this Certbot/Nginx change and preserve other domains’ certificates and renewal settings. Verify the updated served certificate and service response after renewal to complete the operational check.




