Skip to content

Calendars: Caddy Reverse Proxy

The Calendars pre-built frontend image serves only static files. It doesn't include API reverse proxy rules. You must mount a custom Caddyfile to proxy:

  • /api/v1.0/* → Django backend
  • /caldav/* → Django backend (which adds auth and proxies to CalDAV server)
  • /rsvp/* → Django backend (RSVP page for email invitation links)

Custom Caddyfile

{
    auto_https off
    admin off
}

:8080 {
    root * /srv
    header X-Frame-Options DENY

    route {
        # API reverse proxy to backend
        reverse_proxy /api/v1.0/* 172.29.0.30:8000 {
            header_up Host {host}
            header_up X-Forwarded-Proto https
        }

        # CalDAV proxy through Django (adds X-LS-User + API key)
        reverse_proxy /caldav/* 172.29.0.30:8000 {
            header_up Host {host}
            header_up X-Forwarded-Proto https
        }

        # RSVP endpoint (handles accept/maybe/decline links)
        reverse_proxy /rsvp/* 172.29.0.30:8000 {
            header_up Host {host}
            header_up X-Forwarded-Proto https
        }

        try_files {path} {path}.html /index.html
        file_server
    }

    handle_errors {
        rewrite * /404.html
        file_server
    }
}

Why Three Proxies?

  1. /api/v1.0/* — standard REST API calls from the frontend JS
  2. /caldav/* — CalDAV protocol requests (PROPFIND, MKCALENDAR, REPORT). MUST go through Django, not directly to CalDAV. Django adds X-LS-User header and API key authentication
  3. /rsvp/* — RSVP confirmation pages linked from invitation emails. Django serves the HTML page and processes accept/decline actions

Critical: CalDAV Must Go Through Django

The CalDAV PHP server uses ApiKeyAuthBackend which requires an API key header. Direct browser requests don't have this — they only have session cookies. Proxying through Django solves this:

Browser → Caddy → /caldav/* → Django backend → CalDAV server
                                 (adds auth headers)

If you proxy /caldav/* directly to the CalDAV server: 1. CalDAV returns 401 (no API key) 2. Frontend JS sees 401 → triggers full re-authentication 3. Infinite redirect loop

Port

Caddy listens on 8080, not 3000. This is what the original Caddyfile in the image uses. The Traefik label must match.