ACME without port 80 — DNS-01 challenges for blocked ports and wildcards
The standard Let's Encrypt flow uses HTTP-01: certbot serves a challenge on port 80, the CA fetches it, cert issued. This fails if port 80 is blocked, busy, or unreachable — and it can't issue wildcard certificates. DNS-01 challenges work through DNS records instead, sidestepping both problems. This article walks the setup.
When DNS-01 is the right choice
- Port 80 is blocked. Your firewall, your ISP, or your hosting provider doesn't let inbound HTTP through.
- Port 80 is in use by something that can't be moved. Some PBX or appliance binds to 80 and won't share.
- You need a wildcard certificate.
*.example.comcan only be issued via DNS-01. - Your service isn't reachable from the public internet. An internal app on a LAN-only IP can still get a real cert via DNS-01.
How DNS-01 works
- You request a cert from Let's Encrypt.
- The CA gives you a challenge token.
- You add a TXT record at
_acme-challenge.<your-domain>with the token. - The CA queries DNS for the TXT record.
- If it matches, the cert is issued.
The challenge is that step 3 has to be automated for cert renewal to be hands-off — which means certbot needs API access to your DNS provider.
Picking a DNS provider
Whatever DNS host runs your zone, you need a way for certbot to add TXT records programmatically. Most major hosts offer an API:
- Cloudflare — first-class, well-documented. Most certbot plugins target Cloudflare first. Generate a scoped API token.
- DigitalOcean, Linode, Hetzner — all have certbot plugins.
- AWS Route 53 — built-in certbot support.
- Google Cloud DNS — built-in certbot support.
- Generic / no API — you can delegate the
_acme-challengesubdomain to a separate DNS host that does have API access (acme-dns is a tiny purpose-built DNS server for exactly this).
Install certbot with the DNS plugin
For Cloudflare:
# Debian/Ubuntu
apt install certbot python3-certbot-dns-cloudflare
# AlmaLinux/Rocky
dnf install certbot python3-certbot-dns-cloudflare
Different plugins for different DNS providers — substitute the relevant one.
Configure the Cloudflare credentials
/etc/letsencrypt/cloudflare.ini:
dns_cloudflare_api_token = <your-scoped-cloudflare-token>
Lock it down:
chmod 600 /etc/letsencrypt/cloudflare.ini
The token should have:
- Zone → DNS → Edit (the only permission needed).
- Zone Resources → Include → Specific zone → your domain.
Don't use your Global API Key. Use a scoped token.
Request a certificate
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d example.com \
-d www.example.com
Wildcard:
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
-d example.com \
-d "*.example.com"
Certbot creates the TXT record, waits for propagation, asks Let's Encrypt to verify, deletes the TXT record. Cert lands at /etc/letsencrypt/live/example.com/.
Renewal
Certbot installs a systemd timer (or cron job on older distros) that checks for renewals twice a day. With the DNS plugin and credentials in place, renewal is automatic.
Test it without actually renewing:
certbot renew --dry-run
The dry-run hits the staging environment so you don't burn your rate limit while testing.
Using the cert
Same as HTTP-01 issued certs — the files are in the same place:
fullchain.pem— the certificate plus intermediate.privkey.pem— the private key.
For nginx, Apache, Caddy: point them at these files. For services that don't auto-reload on cert renewal, add a renewal hook:
# /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
#!/bin/bash
systemctl reload nginx
systemctl reload postfix
# whatever else needs the new cert
Make it executable:
chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
Delegated _acme-challenge with acme-dns
If your DNS host doesn't have an API, but does support CNAME records, set this up once:
- Run acme-dns on a server with a public IP (a small VPS works fine).
- Register a subdomain with acme-dns — it gives you a long random token to use as your credential.
- In your DNS, CNAME
_acme-challenge.example.comto<your-acme-dns-subdomain>. - Use certbot's acme-dns plugin with the credential.
Effectively you delegate the ACME challenge handling to a small DNS server you control, regardless of your main DNS host's API capabilities. acme-dns runs in tiny RAM and is the kind of service that runs for years untouched.
Common issues
- "DNS problem: NXDOMAIN looking up TXT for _acme-challenge.example.com." Propagation delay — DNS hasn't updated yet. Use the plugin's
--dns-cloudflare-propagation-seconds 30flag to make certbot wait longer. - "Unable to authenticate." Token is wrong or doesn't have the right permissions. Generate a fresh scoped token.
- Wildcard cert issued but doesn't cover the apex.
*.example.comdoesn't coverexample.comitself. Always include both when requesting wildcards.
Also Read
Powered by WHMCompleteSolution