Can't access resources which are served as a reverse proxy

1. Caddy version (caddy version):


2. How I run Caddy:

I use the official Caddy Alpine image and I just copy my Caddyfile to /etc/caddy/

a. System environment:

Docker → caddy:alpine

b. Command:

docker-compose up

c. Service/unit/compose file:

# docker-compose.yml
version: "3.9"

    container_name: elm_frontend
    build: ./frontend
    restart: always
      - 8080:80

    container_name: fastapi_backend
    build: ./backend
    command: bash -c 'while !</dev/tcp/database/5432; do sleep 5; done; uvicorn main:app --workers 2 --host api --port 8000'
    restart: always

    container_name: admin_dashboard
    build: ./admin
    restart: always

    image: postgres
    restart: always
    - ./database:/var/lib/postgresql/data
      - 5432:5432

# Admin dashboard
FROM python:latest


WORKDIR /home/app

COPY . .

RUN pip install --no-cache-dir --upgrade -r requirements.txt

CMD [ "gunicorn", "--bind", "dashboard:8000", "wsgi:app" ]

d. My complete Caddyfile or JSON config:


:80 {
    encode gzip
    root * /srv

    handle /api/* {
        reverse_proxy http://api:8000

    handle /dashboard/* {
        reverse_proxy http://dashboard:8000

    handle {
        try_files {path} /index.html


3. The problem I’m having:

I have three Docker containers. My frontend, api and admin_dashboard. I want that the api and dashboard are only accessible over my frontend. Unfortunately I can’t reach the api or admin_dashboard.

Regarding my logs of the admin dashboard and api, there is no incoming traffic.

To access these endpoints I try localhost:8080/api or localhost:8080/dashboard

API and Dashboard are both Python servers. The frontend is a static Elm frontend.

Expected behavior:

localhost:8080/dashboard → Show login page
localhost:8080/dashboard/login → Show login page

Actual behavior:

localhost:8080/dashboard → Blank page and 404 error
localhost:8080/dashboard/login → 405 Method Not Allowed

The frontend is working as intended.

4. Error messages and/or full log output:

{“level”:“debug”,“ts”:1654020846.3639793,“logger”:“http.handlers.reverse_proxy”,“msg”:“upstream roundtrip”,“upstream”:“dashboard:8000”,“duration”:0.0027134,“request”:{“remote_ip”:“”,“remote_port”:“60860”,“proto”:“HTTP/1.1”,“method”:“GET”,“host”:“localhost:8080”,“uri”:"/dashboard/",“headers”:{“Cookie”:[],“Sec-Fetch-User”:["?1"],“X-Forwarded-For”:[“”],“X-Forwarded-Proto”:[“http”],“Upgrade-Insecure-Requests”:[“1”],“Accept-Language”:[“de,en-US;q=0.7,en;q=0.3”],“Sec-Fetch-Dest”:[“document”],“Sec-Fetch-Mode”:[“navigate”],“User-Agent”:[“Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0”],“Accept”:[“text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8”],“Accept-Encoding”:[“gzip, deflate, br”],“Sec-Fetch-Site”:[“none”],“X-Forwarded-Host”:[“localhost:8080”]}},“headers”:{“Server”:[“gunicorn”],“Content-Type”:[“text/html; charset=utf-8”],“Content-Length”:[“207”],“Date”:[“Tue, 31 May 2022 18:14:06 GMT”]},“status”:404}

5. What I already tried:

I’ve tried different settings in Docker as changing the host and ports. I’ve read the Caddyfile directive docs

6. Links to relevant resources:

I tried to follow:

You configured your matcher as /dashboard/*, but made a request like /dashboard, which doesn’t match. You’d need to use /dashboard* to match that request.

Also, the handle directive doesn’t manipulate the URL path at all, so requests to your backend will still have /dashboard in it. Is that what you intended? If not, you can use handle_path instead of handle to make it strip the path prefix before proxying.

But keep in mind that if your upstream app wasn’t configured to be aware of the subpath, things can break. Read this article to better understand (so I suggest you use subdomains instead of subpaths if possible):

1 Like

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