I’m a Senior Software Engineer living in Berlin. Shifting limits based on quality and robustness. Cutting-edge software development. Defining durable and flexible interfaces. Creating rich and intuitive user experiences.

Deploying a fully-trusted certificate chain in your LAN

Working with local development environments or home lab servers often means dealing with self-signed certificates and browser warnings. While this might be acceptable for personal use, it becomes tedious when you have multiple devices or want to share services with family members who shouldn’t have to click through security warnings.

This guide walks through setting up a fully-trusted SSL certificate chain for your local area network (LAN) services, giving you the same security guarantees as public websites—all while keeping your services private and local.

The Strategy

The approach combines several technologies to achieve trusted HTTPS in your LAN:

  1. Cloudflare DNS - Manages your domain’s DNS records and provides initial SSL termination
  2. dnsmasq - Routes local DNS queries to your LAN IP addresses
  3. Let’s Encrypt (via certbot) - Issues valid SSL certificates using DNS challenge
  4. Your local services - Use the issued certificates to serve HTTPS traffic

The key insight is that Let’s Encrypt can issue certificates for domains you own by verifying DNS control, even if those domains resolve to private IP addresses in your network.

Step 1: Configure Cloudflare DNS

First, you’ll need a domain name that Cloudflare will manage. This doesn’t have to be expensive—many registrars offer domains for just a few dollars per year.

Setting up your domain:

  1. Purchase a domain from any registrar
  2. Create a free Cloudflare account and add your domain
  3. Configure your domain registrar to use Cloudflare’s nameservers
  4. In Cloudflare’s DNS settings, create an A record for your root domain with proxy enabled (orange cloud icon)
  5. Add A records for each subdomain you plan to use, but set them to “DNS only” (gray cloud icon) rather than proxied

The root domain with Cloudflare proxy enabled ensures that the public domain remains accessible and protected by Cloudflare’s CDN and SSL. The subdomains in DNS-only mode allow you to control where they resolve via your local DNS server, while still enabling Let’s Encrypt to verify domain ownership through DNS challenges.

Step 2: Set Up Local DNS Resolution with dnsmasq

Many consumer routers don’t allow fine-grained DNS configuration, particularly for routing specific domains to local IP addresses. The solution is to run your own DNS server on your network using dnsmasq.

Why you need this:

Modern routers like the Fritz!Box have removed the ability to create custom DNS forwarding rules. By running dnsmasq on a device in your LAN (such as a Raspberry Pi), you gain complete control over DNS resolution for your network.

Installation and configuration:

For this example, I’m using a Raspberry Pi running DietPi (Debian-based), but any Debian/Ubuntu system will work similarly.

apt update
apt install dnsmasq
echo "address=/.lab.example.com/192.168.178.2" >> /etc/dnsmasq.d/local.conf

This configuration tells dnsmasq to resolve any subdomain under lab.example.com to the local IP address 192.168.178.2. Adjust the domain and IP address to match your setup.

Important setup considerations:

  • Assign a static IP address to the machine running dnsmasq
  • Configure your router’s DHCP settings to distribute this machine’s IP as the primary DNS server
  • Ensure the machines hosting your services also have static IP addresses

Verify the setup:

nslookup lab.example.com
# first two lines show DNS server used (the local IP configured earlier and port 53 by default)
# second two lines show how the name was resolved to the local IP configured in `/etc/dnsmasq.d/local.conf`

If configured correctly, you should see the DNS server listed as your dnsmasq machine’s IP, and the resolved address should be your local IP.

Step 3: Obtain SSL Certificates with Certbot

Now comes the clever part: using Let’s Encrypt to issue valid SSL certificates for your local domains. Since you control the DNS records in Cloudflare, you can use the DNS-01 challenge method to prove domain ownership without needing public HTTP access.

Install certbot with Cloudflare plugin:

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

Create a Cloudflare API token:

  1. Log into your Cloudflare dashboard
  2. Navigate to “My Profile” → “API Tokens” → “Create Token”
  3. Use the “Edit zone DNS” template
  4. Restrict the token to only the specific DNS zone you’re using
  5. Under permissions, ensure it has “Zone.DNS” access

Configure the API token for certbot:

# save token in a file and set restricted permissions
echo "dns_cloudflare_api_token = MY_SECRET_TOKEN" > cloudflare.ini
chmod 600 cloudflare.ini

The restrictive permissions (600) ensure only the root user can read the API token.

Issue the certificates:

# issues certificate
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials cloudflare.ini \
-d lab.example.com \
-d '*.lab.example.com'

This command requests a certificate for both the base domain and a wildcard certificate covering all subdomains. Certbot will create a folder at /etc/letsencrypt/live/lab.example.com/ containing your certificates.

Test automatic renewal:

Let’s Encrypt certificates expire after 90 days, but certbot automatically installs a renewal job. Test it with:

certbot renew --dry-run --cert-name lab.example.com

Configure renewal hooks:

You can configure custom actions to run after certificate renewal by editing /etc/letsencrypt/renewal/lab.example.conf:

# …
[renewalparams]
# …
deploy_hook = /usr/local/bin/my-script.sh

This is useful for reloading web servers, sending notifications, or triggering webhooks whenever certificates are renewed.

Step 4: Configure Your Services

With valid certificates in hand, you can now configure your local services to use HTTPS. The certificates you need are:

  • Certificate chain: /etc/letsencrypt/live/lab.example.com/fullchain.pem
  • Private key: /etc/letsencrypt/live/lab.example.com/privkey.pem

Most web servers (nginx, Apache, Caddy, etc.) can be configured to use these files directly. The exact configuration depends on your server, but the principle is the same: point your HTTPS configuration to these two files.

Quick test with Python’s built-in server:

For a simple test or for serving static files, you can use Python’s built-in HTTPS server:

python3 -m http.server 443 --bind 0.0.0.0 \
--directory . \
--tls-cert fullchain.pem \
--tls-key privkey.pem

Once configured, any device on your LAN can access your services using the domain name (e.g., https://lab.example.com or https://service.lab.example.com), and they’ll see a valid, trusted SSL certificate—no browser warnings, no security exceptions needed.

Conclusion

By combining Cloudflare’s DNS management, a local DNS server, and Let’s Encrypt certificates, you can bring the same security and trust of the public web to your private network. This approach is particularly valuable for home labs, development environments, or any scenario where you want legitimate HTTPS without exposing services to the internet.

The setup requires some initial configuration, but once in place, certificate renewal is automatic, and adding new services is as simple as configuring them to use the existing certificates.