How can I simplify this Caddyfile?

1. Output of caddy version:

2.5.2

2. How I run Caddy:

Ansible configures docker and runs it on the server

a. System environment:

docker

b. Command:

Not relevant

c. Service/unit/compose file:

Not relevant

d. My complete Caddy config:

NOTE: Caddyfile.j2 ansible template file

*.{{ domain }} {
  log {
    level INFO
    output file /logs/caddy.log {
      roll_size 10MB
      roll_keep 10
    }
  }

  tls {
    dns cloudflare {{ cloudflare_dns_token }}
  }

  encode zstd gzip

  header {
    # Enable HTTP Strict Transport Security (HSTS)
    Strict-Transport-Security "max-age=31536000;"
    # Enable cross-site filter (XSS) and tell browser to block detected attacks
    X-XSS-Protection "1; mode=block"
    # Disallow the site to be rendered within a frame (clickjacking protection)
    X-Frame-Options "DENY"
    # Prevent search engines from indexing (optional)
    X-Robots-Tag "none"
    # Server name removing
    -Server
  }

  @vault host vault.{{ domain }}
  handle @vault {
    # allow access to the admin interface only from local networks
    @insecureadmin {
      not remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
      path /admin*
    }
    redir @insecureadmin /

    # The negotiation endpoint is also proxied to Rocket
    reverse_proxy /notifications/hub/negotiate 10.10.40.10:{{ vaultwarden_http_port }}

    # notifications redirected to the websockets server
    reverse_proxy /notifications/hub 10.10.40.10:3012

    # proxy the Root directory to Rocket
    reverse_proxy 10.10.40.10:{{ vaultwarden_http_port }} {
      # Send the true remote IP to Rocket, so that vaultwarden can put this in the
      # log, so that fail2ban can ban the correct IP.
      header_up X-Real-IP {remote_host}
    }
  }

  @portainer {
    host portainer.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @portainer {
    reverse_proxy 10.10.40.10:9000
  }

  @home {
    host home.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @home {
    reverse_proxy 10.10.40.10:{{ homer_http_port }}
  }

  @overseerr {
    host overseerr.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @overseerr {
    reverse_proxy 10.10.40.10:5055
  }

  @bazarr {
    host bazarr.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @bazarr {
    reverse_proxy 10.10.40.2:6767
  }

  @radarr {
    host radarr.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @radarr {
    reverse_proxy 10.10.40.2:7878
  }

  @sonarr {
    host sonarr.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @sonarr {
    reverse_proxy 10.10.40.2:8989
  }

  @prowlarr {
    host prowlarr.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @prowlarr {
    reverse_proxy 10.10.40.2:9696
  }

  @torrent {
    host torrent.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @torrent {
    reverse_proxy 10.10.40.2:{{ bittorrent_http_port }}
  }

  @tautulli {
    host tautulli.{{ domain }}
    remote_ip 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8
  }
  handle @tautulli {
    reverse_proxy 10.10.40.10:8181
  }

  # Fallback for otherwise unhandled domains
  handle {
    abort
  }
}

3. The problem I’m having:

Not really a problem, but I’m wondering if there’s a way to remove duplicate code from the Caddyfile above. If you notice, a lot of entries are repeats to make sure they are only accessible on the local network. All websites except for vault.{{ domain }} are accessible only from the LAN

Is it possible to put remote_ip in a variable and reference it instead of repeating it over and over? I suppose since I’m using ansible anyways I could throw those @handle sections in an ansible variable and loop over the j2 template, but I’m wondering if Caddy itself has a better way to write this without the repeated copy-paste

4. Error messages and/or full log output:

N/A

5. What I already tried:

N/A

6. Links to relevant resources:

There’s no benefit to removing this header. It doesn’t hide any information that anyone could otherwise glean from your server. If it caused some kind of vulnerability, we would never have added the header in the first place.

You can probably just use remote_ip private_ranges which is a shortcut for all the CIDRs for private IP ranges. Bit easier to read.

These two go to the same place, so you can just remove the first one; it’s redundant.

Are you sure you need this header? Caddy sends along X-Forwarded-For already with the incoming client’s IP address.

Also, if you have Cloudflare in front of your server, the {remote_host} would be Cloudflare’s IP addresses, not the real client’s.


Other than that, your config looks fine to me :+1:

You could play with Caddyfile snippets, which could let you deduplicate some bits of config, but it might not be worth the effort.

1 Like

hey @francislavoie I appreciate the time you took to review these. I’ll fixup my Caddyfile :slight_smile:

In regards to that fail2ban entry, I’ll remove it since I don’t even use fail2ban. I might implement either fail2ban or crowdsec in the future, but that’s another topic. Most of these configs were taken from the respective upstream projects so I appreciate an expert opinion. Example

2 Likes