I stopped treating HTTPS as optional a long time ago. Browsers flag plain HTTP sites as “Not Secure,” search engines factor it into rankings, and any site of mine handling logins or personal data has a basic obligation to encrypt traffic. Apache’s mod_ssl makes this straightforward, and Let’s Encrypt removed the cost excuse entirely.
Here’s exactly how I set up HTTPS on Apache — getting a certificate, configuring TLS, redirecting HTTP, and hardening the result.
How HTTPS Works (Briefly)
HTTPS wraps HTTP traffic in TLS encryption, using a certificate issued by a trusted Certificate Authority to prove the server’s identity and establish an encrypted channel between browser and server. The certificate has a public certificate file and a private key that has to stay secret.
Prerequisites
- Apache installed with a registered domain pointing to the server’s public IP
- Root or sudo access
- Port 443 open in the firewall (see my firewall configuration post)
mod_sslinstalled
# Debian/Ubuntu
sudo apt install openssl
# RHEL/CentOS
sudo dnf install mod_ssl openssl
Step 1: Enable mod_ssl
Debian/Ubuntu:
sudo a2enmod ssl
sudo systemctl restart apache2
RHEL/CentOS: mod_ssl is typically enabled automatically after dnf install mod_ssl.
Step 2: Get a Free Certificate with Let’s Encrypt (Certbot)
Certbot has automated this for me on every server I’ve set up in the last few years.
# Debian/Ubuntu
sudo apt install certbot python3-certbot-apache
# RHEL/CentOS
sudo dnf install certbot python3-certbot-apache
I run Certbot in Apache mode — it detects my virtual hosts automatically:
sudo certbot --apache -d example.com -d www.example.com
Certbot then:
- Verifies domain ownership via an HTTP challenge.
- Obtains the certificate from Let’s Encrypt.
- Updates my Apache virtual host with SSL configuration automatically.
- Offers to set up an HTTP → HTTPS redirect, which I always accept.
Step 3: Verify Automatic Renewal
Let’s Encrypt certificates expire every 90 days. Certbot installs a renewal timer automatically:
sudo systemctl status certbot.timer
sudo certbot renew --dry-run
The dry run confirms renewal works without actually renewing anything.
Manual SSL Configuration (Without Certbot)
If I’m using a certificate from another CA, here’s the configuration underneath what Certbot generates:
<VirtualHost *:443>
ServerName example.com
DocumentRoot /var/www/html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
SSLCertificateChainFile /etc/ssl/certs/example.com-chain.crt
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/ssl-error.log
CustomLog ${APACHE_LOG_DIR}/ssl-access.log combined
</VirtualHost>
On modern Apache/OpenSSL, SSLCertificateFile can often include the full chain, making a separate SSLCertificateChainFile unnecessary — I check my CA’s specific instructions to be sure.
Step 4: Redirect HTTP to HTTPS
I add this to the port 80 virtual host so everything gets forced to HTTPS:
<VirtualHost *:80>
ServerName example.com
Redirect permanent / https://example.com/
</VirtualHost>
Or with mod_rewrite when I want more control:
<VirtualHost *:80>
ServerName example.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</VirtualHost>
Step 5: Test and Reload
sudo apachectl configtest
sudo systemctl reload apache2
I confirm HTTPS is working:
curl -I https://example.com/
Then I run it through SSL Labs (https://www.ssllabs.com/ssltest/) for a full grade on the configuration.
Hardening TLS Configuration
Default mod_ssl settings on older distros can still allow outdated, weak protocol versions. I update /etc/apache2/mods-available/ssl.conf (Debian) or the equivalent httpd-ssl.conf (RHEL):
SSLProtocol -all +TLSv1.2 +TLSv1.3
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
SSLHonorCipherOrder off
SSLSessionTickets off
I add HSTS to tell browsers to always use HTTPS for this domain going forward:
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
For a configuration tailored to my exact Apache/OpenSSL versions, I lean on the Mozilla SSL Configuration Generator, since recommended cipher suites evolve over time and I’d rather not hand-maintain that list myself.
Enabling HTTP/2
HTTP/2 needs TLS in practice, and I’ve seen real performance improvements from multiplexing once it’s on.
sudo a2enmod http2
<VirtualHost *:443>
Protocols h2 http/1.1
...
</VirtualHost>
Real-World Use Cases
- Any site handling logins, payments, or personal data — non-negotiable in my book.
- SEO-sensitive sites, since HTTPS is a confirmed ranking factor.
- API endpoints serving mobile apps or third-party integrations that need encrypted transport.
- Multi-domain hosting using SNI to serve different certificates per virtual host on the same IP.
Mistakes I’ve Made
- Forgetting to open port 443 in the firewall right after installing a certificate.
- Mixing HTTP and HTTPS resources on the same page, which browsers block as “mixed content.”
- Letting a certificate expire because an auto-renewal failed silently and I didn’t notice — I monitor renewal timers now.
- Sticking with outdated TLS versions or weak cipher suites, which fail modern security audits.
- Not redirecting HTTP to HTTPS, leaving both accessible and diluting SEO signals.
Security Best Practices
- I enforce TLS 1.2+ only, disabling TLS 1.0/1.1 and all SSL versions entirely.
- I enable HSTS with a long
max-ageonce I’m confident HTTPS is fully and permanently working. - Private keys stay readable only by root and the Apache process:
sudo chmod 600 /etc/ssl/private/example.com.key - I automate renewal and monitor certificate expiry proactively.
- I re-test TLS configuration with SSL Labs periodically, since recommendations shift over time.
Performance Optimization
- OCSP stapling reduces the latency of certificate revocation checks for clients:
SSLUseStapling onSSLStaplingCache "shmcb:/var/run/ocsp(128000)" - I enable HTTP/2 for multiplexed connections.
- SSL session reuse (
SSLSessionCache) cuts the cost of repeated TLS handshakes for returning visitors. - In my experience, TLS termination itself has modest CPU overhead on modern hardware and is rarely the actual bottleneck for typical traffic levels.
Troubleshooting
“Your connection is not private” browser warning I check certificate validity dates and that the certificate matches the requested domain exactly:
openssl x509 -in /etc/ssl/certs/example.com.crt -noout -dates -subject
Certbot renewal fails I check /var/log/letsencrypt/letsencrypt.log for the specific error; common causes are DNS changes, firewall blocking the HTTP challenge, or an expired DNS record for a subdomain.
Mixed content warnings in the browser console I search the site’s HTML/CSS/JS for hardcoded http:// references and update them to https://.
500 error immediately after enabling SSL apachectl configtest — usually a typo’d certificate/key file path or mismatched cert and key.
FAQs
Do I need a paid certificate, or is Let’s Encrypt sufficient? Let’s Encrypt gives the same encryption strength as paid certificates for standard use cases in my experience. Paid certs sometimes add extended validation branding or warranty coverage, but no meaningful technical security advantage.
How often do I need to renew my certificate? Every 90 days for Let’s Encrypt; Certbot’s automatic renewal handles it, usually attempting around day 60.
Can I run HTTP and HTTPS simultaneously? Yes, and I keep port 80 open specifically to redirect to HTTPS rather than closing it, since some clients still attempt an initial HTTP connection.
Summary and Key Takeaways
mod_sslplus Certbot/Let’s Encrypt gives me a fast, free path to a fully working HTTPS setup.- I always redirect HTTP to HTTPS and enable HSTS once things are stable.
- I harden TLS configuration by disabling outdated protocols and weak ciphers.
- I automate certificate renewal and watch for renewal failures.
- SSL Labs is my go-to check-in to make sure the configuration hasn’t drifted from current best practices.
References
- Apache mod_ssl Documentation: https://httpd.apache.org/docs/current/mod/mod_ssl.html
- Let’s Encrypt / Certbot: https://certbot.eff.org/
- Mozilla SSL Configuration Generator: https://ssl-config.mozilla.org/
- Qualys SSL Labs Server Test: https://www.ssllabs.com/ssltest/