Reverse Proxy to HTTP backend fails with 502 Bad Gateway

I couldn’t find a answer to this problem and i’m sorry if i created a dublicate but i’m really struggeling to see the problem. I have a production server that handles this just fine with the reverse proxy but the dev env seems to need some extra configuration.

1. Caddy version (caddy version):

2.3.0

2. How I run Caddy:

In a docker container

a. System environment:

MacOS, Docker

b. Command:

docker-compose up

c. Service/unit/compose file:

version: "3"
services:
  node_stream_server:
    build: ./server/
    volumes:
      - ./server/videos:/app/videos 
      - ./server/thumbnails:/app/thumbnails 
    environment: 
      - ADMIN_USERNAME=${ADMIN_USERNAME}
      - ADMIN_PASSWORD=${ADMIN_PASSWORD}
      - MONGODB_USERNAME=${DB_USERNAME}
      - MONGODB_PASSWORD=${DB_PASSWORD}
      - SYNCHRONOUS_SECRET=${SYNCHRONOUS_SECRET}
  caddy_http_server:
    image: caddy:alpine
    environment: 
      - HOSTNAME=${HOSTNAME}
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - ./caddy/caddydata:/root/.caddy
      - ./caddy/www:/var/www/html
    ports:
      - "80:80"
      - "443:443"  
  mongo_database:
    image: mongo:3.6.1
    restart: always
    volumes:
      - ./db/data:/data/db

d. My complete Caddyfile or JSON config:

{$HOSTNAME}
    root * /var/www/html
    file_server
    basicauth /* {
        User Password (just placeholders)
    }
    reverse_proxy /api/* node_stream_server:3000 {
        transport http {
            tls_insecure_skip_verify
        }
    }
    log {
        output stdout
        }

3. The problem I’m having:

I’m trying to reverse proxy requests from https://localhost/api/* to http://localhost:3000/api/* and the request fail (502 Bad Gateway)

4. Error messages and/or full log output:

{"level":"error","ts":1613480905.9548938,"logger":"http.log.error.log0","msg":"tls: first record does not look like a TLS handshake","request":{"remote_addr":"172.18.0.1:59160","proto":"HTTP/2.0","method":"GET","host":"localhost","uri":"/api/stream/status","headers":{"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10.16; rv:85.0) Gecko/20100101 Firefox/85.0"],"Accept":["*/*"],"Accept-Language":["de,en-US;q=0.7,en;q=0.3"],"Accept-Encoding":["gzip, deflate, br"],"Referer":["https://localhost/"],"Dnt":["1"],"Authorization":["Basic Password"],"Te":["trailers"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","proto_mutual":true,"server_name":"localhost"}},"duration":0.0047244,"status":502,"err_id":"ftmam2whz","err_trace":"reverseproxy.statusError (reverseproxy.go:783)"}

5. What I already tried:

I tried to bypass the HTTPS with the tls_insecure_skip_verify flag in the reverse_proxytransport directive

Hmm strange seems like i fixed it with a change to the caddyfile

{$HOSTNAME}
    root * /var/www/html
    file_server
    basicauth /* {
        User Password
    }
    reverse_proxy /api/* node_stream_server:3000 
    log {
        output stdout
        }

strange thing is, when i add port mapping to the node server i can’t reverse proxy to it with:

{$HOSTNAME}
    root * /var/www/html
    file_server
    basicauth /* {
        User Password
    }
    reverse_proxy /api/* localhost:3000 
    log {
        output stdout
        }
1 Like

Yeah - node isn’t serving HTTPS, it’s serving HTTP. Caddy terminates HTTPS.

And also using localhost means trying to connect to 127.0.0.1 which is the loopback address, meaning Caddy would try to connect to its own container, not the other container, but that’ll never work because the only thing running in that container is Caddy.

Glad you found the fix!

Tiny thing, you can remove the /* on the basicauth directive. Not specifying a matcher means “any request”. You’re specifying a path matcher for any path starting with /* which will always be true (all request’s paths start with /) but this is very slightly less efficient because it means Caddy needs to make a path comparison every time. Imperceptible difference in performance, but still worth fixing :stuck_out_tongue:

2 Likes

Thank you for the insights! I didn’t realized that localhost pointed to the container but it’s totally comprehensible. I will fix my auth matcher. Thanks for the help !

1 Like

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