Issues with Caddy + DuckDNS Setup for Serving Local Directory Over HTTPS

1. The problem I’m having:

I am trying to set up a Caddy server to serve a local directory on my Raspberry Pi over HTTPS using a DuckDNS domain (kev1.duckdns.org). The goal is to allow access to the contents of a directory located at /mnt/external/data/media/newspapers on my external drive via HTTPS.

The Caddy server is running in a Docker container, and I am using the DuckDNS module for DNS challenge verification to obtain an SSL/TLS certificate from Let’s Encrypt.

However, I am facing two issues:

1.	SSL certificate verification error when accessing https://kev1.duckdns.org.
2.	Directory contents are not visible when visiting https://kev1.duckdns.org.

CLI outputs from my Raspberry:

curl -v http://kev1.duckdns.org
*   Trying 115.70.40.36:80...
* Connected to kev1.duckdns.org (115.70.40.36) port 80 (#0)
> GET / HTTP/1.1
> Host: kev1.duckdns.org
> User-Agent: curl/7.88.1
> Accept: */*
>
< HTTP/1.1 302 Found
< Location: https://kev1.duckdns.org/webpages/login.html
< Connection: close
<
* Closing connection 0

and

*   Trying 115.70.40.36:443...
* Connected to kev1.duckdns.org (115.70.40.36) port 443 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/certs/ca-certificates.crt
*  CApath: /etc/ssl/certs
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, unknown CA (560):
* SSL certificate problem: unable to get local issuer certificate
* Closing connection 0
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html

2. Error messages and/or full log output:

caddy  | {"level":"info","ts":1724716025.5288093,"msg":"using config from file","file":"/etc/caddy/Caddyfile"}
caddy  | {"level":"info","ts":1724716025.5318816,"msg":"adapted config to JSON","adapter":"caddyfile"}
caddy  | {"level":"warn","ts":1724716025.5319395,"msg":"Caddyfile input is not formatted; run 'caddy fmt --overwrite' to fix inconsistencies","adapter":"caddyfile","file":"/etc/caddy/Caddyfile","line":2}
caddy  | {"level":"info","ts":1724716025.5346239,"logger":"admin","msg":"admin endpoint started","address":"localhost:2019","enforce_origin":false,"origins":["//localhost:2019","//[::1]:2019","//127.0.0.1:2019"]}
caddy  | {"level":"info","ts":1724716025.5352125,"logger":"tls.cache.maintenance","msg":"started background certificate maintenance","cache":"0x40004b6a80"}
caddy  | {"level":"info","ts":1724716025.5354228,"logger":"http.auto_https","msg":"server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS","server_name":"srv0","https_port":443}
caddy  | {"level":"info","ts":1724716025.535471,"logger":"http.auto_https","msg":"enabling automatic HTTP->HTTPS redirects","server_name":"srv0"}
caddy  | {"level":"info","ts":1724716025.5362732,"logger":"http","msg":"enabling HTTP/3 listener","addr":":443"}
caddy  | {"level":"info","ts":1724716025.5366695,"msg":"failed to sufficiently increase receive buffer size (was: 208 kiB, wanted: 7168 kiB, got: 416 kiB). See https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes for details."}
caddy  | {"level":"info","ts":1724716025.5372162,"logger":"http.log","msg":"server running","name":"srv0","protocols":["h1","h2","h3"]}
caddy  | {"level":"info","ts":1724716025.5375028,"logger":"http.log","msg":"server running","name":"remaining_auto_https_redirects","protocols":["h1","h2","h3"]}
caddy  | {"level":"info","ts":1724716025.5375445,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["kev1.duckdns.org"]}
caddy  | {"level":"info","ts":1724716025.5381725,"msg":"autosaved config (load with --resume flag)","file":"/config/caddy/autosave.json"}
caddy  | {"level":"info","ts":1724716025.5382175,"msg":"serving initial configuration"}
caddy  | {"level":"info","ts":1724716025.5389495,"logger":"tls.obtain","msg":"acquiring lock","identifier":"kev1.duckdns.org"}
caddy  | {"level":"info","ts":1724716025.5463822,"logger":"tls","msg":"cleaning storage unit","storage":"FileStorage:/data/caddy"}
caddy  | {"level":"info","ts":1724716025.5479794,"logger":"tls","msg":"finished cleaning storage units"}
caddy  | {"level":"info","ts":1724716025.5489483,"logger":"tls.obtain","msg":"lock acquired","identifier":"kev1.duckdns.org"}
caddy  | {"level":"info","ts":1724716025.5492709,"logger":"tls.obtain","msg":"obtaining certificate","identifier":"kev1.duckdns.org"}
caddy  | {"level":"info","ts":1724716026.4610503,"logger":"tls.issuance.acme","msg":"waiting on internal rate limiter","identifiers":["kev1.duckdns.org"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
caddy  | {"level":"info","ts":1724716026.4611108,"logger":"tls.issuance.acme","msg":"done waiting on internal rate limiter","identifiers":["kev1.duckdns.org"],"ca":"https://acme-v02.api.letsencrypt.org/directory","account":""}
caddy  | {"level":"info","ts":1724716026.4611936,"logger":"tls.issuance.acme","msg":"using ACME account","account_id":"https://acme-v02.api.letsencrypt.org/acme/acct/1912538936","account_contact":[]}
caddy  | {"level":"info","ts":1724716026.8499134,"logger":"tls.issuance.acme.acme_client","msg":"trying to solve challenge","identifier":"kev1.duckdns.org","challenge_type":"dns-01","ca":"https://acme-v02.api.letsencrypt.org/directory"}
caddy  | {"level":"info","ts":1724716032.8179328,"logger":"tls.issuance.acme.acme_client","msg":"authorization finalized","identifier":"kev1.duckdns.org","authz_status":"valid"}
caddy  | {"level":"info","ts":1724716032.8179865,"logger":"tls.issuance.acme.acme_client","msg":"validations succeeded; finalizing order","order":"https://acme-v02.api.letsencrypt.org/acme/order/1912538936/299765463756"}
caddy  | {"level":"info","ts":1724716033.594002,"logger":"tls.issuance.acme.acme_client","msg":"got renewal info","names":["kev1.duckdns.org"],"window_start":1729811291.3333333,"window_end":1729984091.3333333,"selected_time":1729879771,"recheck_after":1724737633.5939832,"explanation_url":""}
caddy  | {"level":"info","ts":1724716033.9575677,"logger":"tls.issuance.acme.acme_client","msg":"got renewal info","names":["kev1.duckdns.org"],"window_start":1729811291.3333333,"window_end":1729984091.3333333,"selected_time":1729895772,"recheck_after":1724737633.9575586,"explanation_url":""}
caddy  | {"level":"info","ts":1724716033.9576702,"logger":"tls.issuance.acme.acme_client","msg":"successfully downloaded available certificate chains","count":2,"first_url":"https://acme-v02.api.letsencrypt.org/acme/cert/03fa331ddd7bfde15890e327d053c531445f"}
caddy  | {"level":"info","ts":1724716033.959345,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"kev1.duckdns.org","issuer":"acme-v02.api.letsencrypt.org-directory"}
caddy  | {"level":"info","ts":1724716033.9600403,"logger":"tls.obtain","msg":"releasing lock","identifier":"kev1.duckdns.org"}

3. Caddy version:

4. How I installed and ran Caddy:

With Docker Compose and a custom DuckDNS module:

  caddy:
    image: serfriz/caddy-duckdns:latest
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"   # HTTP port
      - "443:443" # HTTPS port
    volumes:
      - ./config/caddy:/config  # Mount config directory for Caddy
      - ./Caddyfile:/etc/caddy/Caddyfile  # Mount your custom Caddyfile
      - /mnt/external/data/media/newspapers:/srv  # Mount your newspapers folder
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Australia/Sydney
      - DUCKDNS_API_TOKEN=b3xxx-xxx-xxx-8e

volumes:
  caddy_data:
    external: true
  caddy_config:
    external: true

a. System environment:

PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
Architecture:
aarch64
Docker Version:
Docker version 27.1.2, build d01f264
Docker Compose Version:
Docker Compose version v2.20.2
Caddy Version:
v2.8.4 h1:q3pe0wpBj1OcHFZ3n/1nl4V4bxBrYoSoab7rL9BMYNk=
Docker Managed by systemd:
Yes, Docker is managed by systemd.

b. Command:

sudo docker compose up -d 

c. Service/unit/compose file:

See above please

d. My complete Caddy config:

kev1.duckdns.org {
    tls {
        dns duckdns {env.DUCKDNS_API_TOKEN}
    }

    root * /srv

    # Enable file server with directory browsing
    file_server browse

    # Log access to a specific file
    log {
        output file /var/log/caddy/access.log
    }
}

5. Links to relevant resources:

I’d greatly appreciate if someone could point me in the right direction!

Howdy @Koalamanx, welcome to the Caddy community.

This reply did not come from Caddy. I can state this confidently for a few reasons:

  1. Caddy’s default HTTP->S redirect issues status 308 Permanent Redirect, not status 302 Found. It also preserves the original URI; it’s a straight scheme upgrade, i.e. repeat your exact request but send it to https:// instead. So, this isn’t Caddy’s redirect.
  2. Nothing in your configuration would create a redirect to /webpages/login.html, either. You have not configured redir in your Caddyfile, and you are not reverse proxying any upstream which could issue a redirect. You have only configured a file server.
  3. There is no Server: Caddy header.
  4. There is no access log entry in your posted logs, nor an error log for a TLS handshake issue.

I can only assume that DNS is not properly configured, or port forwarding does not properly pass packets onwards to your Caddy host and some other server is responding instead.

2 Likes

Okay, firstly thank you for your thorough reply!

I got it working. I had my router set to remote management on port 443. Somehow this interfered with the HTTPS request I reckon. After disabling it it all works fine.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.