Like Nginx, Apache can be secured with free SSL/TLS certificates from Let’s Encrypt using Certbot. The python3-certbot-apache plugin performs domain validation, obtains the certificate, and automatically updates your Apache virtual host configuration with the SSL directives, HTTP-to-HTTPS redirect, and modern cipher settings. This guide covers installing Certbot on RHEL 9, obtaining a certificate for your Apache virtual host, hardening the TLS configuration, and verifying automatic renewal.

Prerequisites

  • Apache installed and running on RHEL 9 with a configured virtual host
  • A domain with a DNS A record pointing to your server’s public IP
  • Ports 80 and 443 open in firewalld

Step 1 — Install Certbot with the Apache Plugin

dnf install -y epel-release
dnf install -y certbot python3-certbot-apache

Step 2 — Obtain and Install a Certificate

# The --apache plugin reads your virtual host and configures SSL automatically
certbot --apache -d example.com -d www.example.com 
    --agree-tos --no-eff-email -m [email protected]

Certbot will:

  1. Read your existing /etc/httpd/conf.d/example.com.conf
  2. Validate domain ownership via HTTP-01 challenge on port 80
  3. Generate a certificate in /etc/letsencrypt/live/example.com/
  4. Create a new /etc/httpd/conf.d/example.com-le-ssl.conf HTTPS virtual host
  5. Add a Redirect permanent in the HTTP block

Step 3 — Verify the Certificate

certbot certificates
curl -I https://example.com
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | 
    openssl x509 -noout -dates

Step 4 — Harden the TLS Configuration

Create a shared SSL hardening file that all virtual hosts can include:

vi /etc/httpd/conf.d/ssl-hardening.conf
# Modern TLS configuration
SSLProtocol             all -SSLv3 -TLSv1 -TLSv1.1
SSLCipherSuite          ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305
SSLHonorCipherOrder     off
SSLSessionTickets       off
SSLUseStapling          on
SSLStaplingCache        shmcb:/run/httpd/ocsp(128000)

# Security headers
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
Header always set Referrer-Policy "no-referrer-when-downgrade"
apachectl configtest && systemctl reload httpd

Step 5 — Test Automatic Renewal

# Verify the renewal timer
systemctl status certbot-renew.timer

# Dry run (simulates renewal without changing files)
certbot renew --dry-run

# Apache is reloaded automatically via the renewal hook
ls /etc/letsencrypt/renewal-hooks/deploy/

Step 6 — Wildcard Certificate (DNS Challenge)

For wildcard certificates (*.example.com), the HTTP-01 challenge cannot be used — you need the DNS-01 challenge:

# Wildcard — requires DNS provider API support or manual DNS update
certbot certonly --manual --preferred-challenges dns 
    -d example.com -d '*.example.com' 
    --agree-tos --no-eff-email -m [email protected]

Certbot will prompt you to add a _acme-challenge TXT record to your DNS zone. After adding the record and waiting for propagation, press Enter to complete validation.

Verification Checklist

certbot certificates
curl -si https://example.com | grep -E "HTTP|Strict|X-Frame"
apachectl configtest

Conclusion

Your Apache server on RHEL 9 is now secured with a Let’s Encrypt certificate, with automatic renewal via systemd timer and hardened TLS settings that disable legacy protocols and weak ciphers. The shared ssl-hardening.conf file applies consistently across all SSL virtual hosts.

Next steps: How to Configure Nginx as a Reverse Proxy on RHEL 9, How to Configure Apache mod_proxy as a Reverse Proxy on RHEL 9, and How to Enable HTTP/2 with Nginx on RHEL 9.