Skip to content

Instantly share code, notes, and snippets.

@bathivinod
Created July 8, 2025 03:58
Show Gist options
  • Select an option

  • Save bathivinod/51c6f82a9f69b5755acdc4138cea4196 to your computer and use it in GitHub Desktop.

Select an option

Save bathivinod/51c6f82a9f69b5755acdc4138cea4196 to your computer and use it in GitHub Desktop.
Let's Encrypt SSL with Cloudflare DNS Validation Guide

Let's Encrypt SSL with Cloudflare DNS Validation

Requirements Recap

βœ… Your Requirements:

  • πŸ”’ Ports 80/443 are firewalled to Cloudflare IPs only
  • ☁️ You're behind Cloudflare, so Let's Encrypt (Certbot) cannot reach port 80 directly
  • 🌐 You're using Apache, not Nginx
  • You already have a self-signed or manually managed cert in use

Best Approach: DNS-01 Challenge (via Cloudflare API)

Since HTTP-01 won't work (because Let's Encrypt can't reach port 80 through Cloudflare's proxy), you must use the DNS-01 challenge, which updates DNS records temporarily during verification.

Step-by-Step Setup with DNS-01 (Cloudflare API)

πŸ” 1. Install Certbot + DNS Plugin for Cloudflare

sudo apt update
sudo apt install certbot python3-certbot-dns-cloudflare

πŸ” 2. Create Cloudflare API Token

  • Go to Cloudflare API Tokens
  • Click "Create Token"
  • Use the "Edit zone DNS" template
  • Assign it only to the domain example.com
  • Save the API Token

πŸ“ 3. Store the API Token Securely

Create a file to hold the API token:

sudo nano /etc/letsencrypt/cloudflare.ini

Paste:

dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN

Set correct permissions:

sudo chmod 600 /etc/letsencrypt/cloudflare.ini

πŸ“œ 4. Run Certbot with DNS Challenge

Example command (non-interactive):

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  --dns-cloudflare-propagation-seconds 30 \
  --preferred-challenges dns-01 \
  --agree-tos \
  --no-eff-email \
  --email your@email.com \
  -d dev.example.com \
  -d dev-api.example.com
Option Description
--dns-cloudflare Use Cloudflare DNS for challenge
--dns-cloudflare-credentials Path to your Cloudflare API token file
--dns-cloudflare-propagation-seconds 30 Wait 30s for DNS to propagate (Cloudflare is fast)
--preferred-challenges dns-01 Force DNS-based validation
--agree-tos Agree to Let's Encrypt terms automatically
--no-eff-email Don't subscribe to EFF mailing list
--email your@email.com Required by Let's Encrypt
-d domain Domains to issue cert for

Example Output

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Account registered.
Requesting a certificate for dev.example.com
Waiting 30 seconds for DNS changes to propagate

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/dev.example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/dev.example.com/privkey.pem
This certificate expires on 2025-10-06.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Debug Log Example

root@dev:/var/log/apache2# tail /var/log/letsencrypt/letsencrypt.log
2025-07-08 08:42:49,788:DEBUG:certbot._internal.display.obj:Notifying user:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/dev.example.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/dev.example.com/privkey.pem
This certificate expires on 2025-10-06.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
2025-07-08 08:42:49,790:DEBUG:certbot._internal.display.obj:Notifying user: If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le

πŸ› οΈ 5. Update Apache SSL Config

Now update your <VirtualHost *:443> to use Certbot's live certs:

SSLCertificateFile      /etc/letsencrypt/live/dev.example.com/fullchain.pem
SSLCertificateKeyFile   /etc/letsencrypt/live/dev.example.com/privkey.pem
# (No need for SSLCertificateChainFile with fullchain.pem)

Save and reload Apache:

sudo systemctl reload apache2

πŸ”„ 6. Set up Auto-Renewal (with Apache reload)

Certbot renews automatically via cron. To ensure Apache reloads after renewal, add this hook:

sudo crontab -e

Add:

0 2 * * * certbot renew --quiet --post-hook "systemctl reload apache2"

This runs daily at 2 AM.

Verification Steps

βœ… 1. Check Certificate Files

List the directory:

sudo ls -l /etc/letsencrypt/live/dev.example.com/

You should see:

cert.pem
chain.pem
fullchain.pem
privkey.pem

βœ… 2. Inspect Certificate Details

Use openssl to inspect the certificate:

sudo openssl x509 -in /etc/letsencrypt/live/dev.example.com/fullchain.pem -text -noout

Look for:

  • Issuer β€” should say Let's Encrypt
  • Not Before and Not After β€” issue and expiry dates (valid for 90 days)
  • DNS: β€” make sure all domains (e.g. dev.example.com, dev-api.example.com, etc.) are listed under Subject Alternative Name (SAN)

βœ… 3. Check Certbot Renewal Status

This shows expiry info for all managed certs:

sudo certbot certificates

Example output:

Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: dev.example.com
    Serial Number: 55c926cd11842f24f0c6ed069581a25e262
    Key Type: ECDSA
    Domains: dev.example.com
    Expiry Date: 2025-10-06 02:14:10+00:00 (VALID: 89 days)
    Certificate Path: /etc/letsencrypt/live/dev.example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/dev.example.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

βœ… 4. Test Auto-Renewal

Manually run the renewal command:

sudo certbot renew --dry-run

If all is well, you should see:

Renewing an existing certificate
Performing the following challenges:
dns-01 challenge for dev.example.com
Waiting for verification...
Cleaning up challenges
Cert not yet due for renewal

βœ… 5. Test SSL from Browser or CLI

You can test with:

curl -Iv https://dev.example.com

You should see something like:

*  SSL certificate verify ok.
*  subject: CN=dev.example.com
*  start date: Jul 8 08:00:00 2025 GMT
*  expire date: Oct 6 08:00:00 2025 GMT
*  issuer: C=US; O=Let's Encrypt...

Summary

Step Description
βœ… 1 Install Certbot with Cloudflare DNS plugin
βœ… 2 Generate Cloudflare API token (Edit DNS only)
βœ… 3 Save token to /etc/letsencrypt/cloudflare.ini
βœ… 4 Use certbot certonly --dns-cloudflare ...
βœ… 5 Update Apache to use live certs
βœ… 6 Add auto-renew with --post-hook reload
@clorteau
Copy link

clorteau commented Oct 1, 2025

Hey thanks, I was having a hard time with cloudflare's and letsencrypt's documentation but these instructions worked like a breeze.

@bathivinod
Copy link
Author

Hi @clorteau ,
Great to hear the instructions worked smoothly!

@kleo
Copy link

kleo commented Dec 6, 2025

thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment