Caddy as Internal CA and Reverse Proxy

I’m using Caddy v2 as my internal Certificate Authority (CA) and as a reverse proxy for my self-hosted services. In this post, I’ll show you how I’ve set up the CA in my HomeLab and rolled out the root CA certificate to my clients.

General

There are a few reasons why Caddy is a great choice for me when it comes to managing my internal web services:

  • Automatic HTTPS with Internal CA
  • Centralized reverse proxy
  • Load balancing and failover support
  • Lightweight and resource efficient
  • Low maintenance overhead
  • HTTP/2 and HTTP/3 support
  • Zero runtime dependencies
  • Memory safety guarantees

See the full list of Caddy features here

Installation and Configuration

Installing Caddy is straightforward. Just follow the official instructions:

Caddy internal CA

One of its key features is that Caddy automatically handles TLS certificates, including renewal and management. By default, Caddy v2 creates a Let’s Encrypt cert automatically. Since I only need it for internal purposes, I’ll stick with Caddy’s internal CA, which is powered by the open source libraries Smallstep. The trust chain consists of a root certificate and an intermediate certificate. Leaf certificates are signed by the intermediate. They are stored in Caddy’s data directory at pki/authorities/local.

After configuring the PKI in the Caddyfile, all we need to do is install the root CA certificate on the hosts so that the generated website certificates will be trusted. Once the certificate is installed, the services can be accessed securely without any SSL warnings.

Internal CA configuration

Just define a general pki module in the Caddyfile /etc/caddy/Caddyfile. This example shows an Internal CA called homelab_ca and a host using the reverse proxy module:

{
        # Enable caddy internal CA
        pki {
                ca internal {
                        name homelab_ca
                }
        }
}

# Example service
example.internal {
        tls internal
        reverse_proxy 192.168.0.99:8080
}

The configuration can be checked, and if there are any syntax errors, Caddy can simply format it correctly. Then the Caddy service needs to be restarted:

caddy validate --config /etc/caddy/Caddyfile
caddy fmt --overwrite /etc/caddy/Caddyfile
systemctl restart caddy

Verify certificate

We can quickly check the website’s certificate using openssl: openssl s_client -connect example.internal:443

Root CA Certificate Rollout

Then we need to deploy the root CA certificate to the clients’ trust stores so that the hosts know that they can trust our newly created CA.

Caddy stores the root certificate of its internal CA in a default location: /var/lib/caddy/.local/share/caddy/pki/authorities/local/root.crt

Note: Web browsers may use their own trust store instead of the system’s one. I tested it on Ubuntu 22.04, 24.04 and Windows 10 using Firefox, Chrome, Vivaldi and Brave. And on Android using Firefox and Chrome:

  • On Windows, I simply installed the certificate in the system trust store and all the browsers I tested worked out of the box
  • On Linux, I had to add the certificate to each browser’s own trust store
  • On Android, Chrome used the system trust store, but Firefox did not
Copy root CA certification from your server

To copy a CA certificate to another system, you can use a secure file transfer protocol, such as scp. You can also simply copy and paste it from the command line to another system, as the certificate is just a text file. Or you can use your preferred and available file sharing system, such as a NAS, SMB, etc.

Linux

Example with scp:

  • Caddy server:
      cp /var/lib/caddy/.local/share/caddy/pki/authorities/internal/root.crt /
      /home/user/caddy_homelab_ca.crt
      chown user:user /home/user/caddy_homelab_ca.crt
    
  • Debian-based workstation:
    • Copy certificate to local system and CA directory
        scp caddy-server:caddy_homelab_ca.crt ~/Downloads/caddy_homelab_ca.crt
        sudo mv ~/Downloads/caddy_homelab_ca.crt /usr/local/share/ca-certificates
      
    • Update the system’s list of trusted certificates
      sudo update-ca-certificates
    • You can check that the certificate has been installed by checking the output of the update-ca-certificates command, which should show that a new certificate has been added.
    • You can also check to see if the file exists:
      ls /etc/ssl/certs/caddy_homelab_ca.pem
    • And, of course, with OpenSSL to see the details.
      openssl x509 -in /etc/ssl/certs/caddy_homelab_ca.pem -text -noout
  • Caddy server, clean up
    rm /home/user/caddy_homelab_ca.crt

Depending on your distribution or desktop environment, you may be able to install the CA by double-clicking the caddy_homelab_ca.crt file and clicking Import.

Linux - Firefox
  • In the menu, go to:
    Preferences > Privacy & Security > Certificates > View Certificates > Authorities > Import, and select the caddy_homelab_ca.crt file
  • Mozilla KB - Setting CA
Windows
  • Double-click the caddy_homelab_ca.crt file and follow the prompts:
    Install Certificat -> Local Machine -> Trusted Root Certification Authorities
  • You can also use the command line:
    certutil -addstore -f "ROOT" caddy_homelab_ca.crt
Android

I’m using a Google Pixel 6 as an example:

  • Go to the preferences:
    Settings > Security > More security settings >  Encryption & credentials > Install a certificate > CA certificate
  • Then, select the file: caddy_homelab_ca.crt 
  • Verify Installation:
    • Under Encryption & credentials go to Trusted Credentials
    • Look under the User tab to see the installed certificate
  • Firefox, there is a setting in the Firefox developer mode so that the browser uses the phone trust store:
    • Open Firefox Settings -> About Firefox
    • Click on the logo 5 times (until “Debug menu enabled” popup appears)
    • Go back to Settings - there should be a Secret Settings option now
    • In the new appeared setting, enable Use third party CA certificates
Automation

At a later stage, I will be creating an Ansible playbook for the rollout of the root CA certificate.

Resources