Help Migrating NGINX to Caddy

1. The problem I’m having:

I’m trying to convert a rather basic NGINX proxy to Caddy for the sake of memory safety, but the proxy seems to fail with my current Caddy configuration.

2. Error messages and/or full log output:

Below are logs of caddy-relay when trying to connect through the Signal app.

{"level":"error","ts":1724048911.1883106,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"174.72.24.251","remote_port":"33150","client_ip":"174.72.24.251","proto":"HTTP/1.1","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"],"Accept-Language":["en-US,en;q=0.9"],"Connection":["close"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"connect.netrunner.academy"}},"duration":0.003698411,"status":502,"err_id":"x1z67qgrv","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724050340.4996786,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"5.164.29.116","remote_port":"33636","client_ip":"5.164.29.116","proto":"HTTP/1.1","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 (scanner.ducks.party)"],"Accept-Encoding":["gzip"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"connect.netrunner.academy"}},"duration":0.013764111,"status":502,"err_id":"ra4kmkf4j","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724050340.9463775,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"5.164.29.116","remote_port":"33636","client_ip":"5.164.29.116","proto":"HTTP/1.1","method":"GET","host":"connect.netrunner.academy","uri":"/favicon.ico","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 (scanner.ducks.party)"],"Accept-Encoding":["gzip"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"connect.netrunner.academy"}},"duration":0.002413049,"status":502,"err_id":"rfxgds6y8","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}

The expected result would be for caddy-proxy to match one of the defined hosts and act as a transparent proxy between the Signal server and the end user, but instead I’m seeing error 502 which would indicate my caddy config is not working as intended.

When trying to visit the proxy from Chrome, the expected result should be a basic error 404, but once again the logs are instead showing error 502. Below are the logs for desktop client requests:

{"level":"error","ts":1724048782.0440547,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"172.18.0.1","remote_port":"40698","client_ip":"172.18.0.1","proto":"HTTP/2.0","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"Sec-Ch-Ua-Platform":["\"Chrome OS\""],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-User":["?1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"User-Agent":["Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"],"Sec-Fetch-Site":["none"],"Sec-Fetch-Mode":["navigate"],"Sec-Ch-Ua":["\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\""],"Dnt":["1"],"Priority":["u=0, i"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"],"Accept-Language":["en-US,en;q=0.9"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"connect.netrunner.academy"}},"duration":0.018127019,"status":502,"err_id":"nepxej5ud","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724048910.7399435,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"172.18.0.1","remote_port":"40698","client_ip":"172.18.0.1","proto":"HTTP/2.0","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"Sec-Fetch-Dest":["document"],"Accept-Language":["en-US,en;q=0.9"],"Sec-Ch-Ua":["\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\""],"Sec-Ch-Ua-Platform":["\"Chrome OS\""],"Upgrade-Insecure-Requests":["1"],"Dnt":["1"],"User-Agent":["Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"],"Priority":["u=0, i"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["none"],"Accept-Encoding":["gzip, deflate, br, zstd"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"connect.netrunner.academy"}},"duration":0.017589707,"status":502,"err_id":"iuinam6d2","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724048922.9016356,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"172.18.0.1","remote_port":"40698","client_ip":"172.18.0.1","proto":"HTTP/2.0","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"Dnt":["1"],"Sec-Fetch-Site":["none"],"Upgrade-Insecure-Requests":["1"],"Sec-Purpose":["prefetch;prerender"],"Purpose":["prefetch"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"],"Sec-Fetch-Dest":["document"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-User":["?1"],"Priority":["u=0, i"],"Sec-Ch-Ua":["\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Chrome OS\""],"User-Agent":["Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["navigate"],"Accept-Language":["en-US,en;q=0.9"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"connect.netrunner.academy"}},"duration":0.013143114,"status":502,"err_id":"fdx61i17d","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724056955.229329,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"172.18.0.1","remote_port":"53574","client_ip":"172.18.0.1","proto":"HTTP/2.0","method":"GET","host":"connect.netrunner.academy","uri":"/favicon.ico","headers":{"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15"],"Referer":["https://connect.netrunner.academy/"],"Accept-Encoding":["gzip, deflate, br"],"Accept":["*/*"],"Sec-Fetch-Site":["same-origin"],"Sec-Fetch-Dest":["image"],"Accept-Language":["en-CA,en-US;q=0.9,en;q=0.8"],"Sec-Fetch-Mode":["no-cors"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"connect.netrunner.academy"}},"duration":0.00295089,"status":502,"err_id":"3jp1qdm08","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}

3. Caddy version:

2.8.4-r2

4. How I installed and ran Caddy:

Caddy was installed via the Chainguard Caddy image labeled as latest (Chainguard Images - caddy - versions).

a. System environment:

Docker compose file running inside of Ubuntu 24.04.

services:
  caddy-terminate:
    image: cgr.dev/chainguard/caddy:latest
    ports:
      - "80:80"
      - "443:443"
    restart: unless-stopped
    volumes:
      - ./data/caddy-terminate/Caddyfile:/etc/caddy/Caddyfile:ro,Z
      - caddy_data:/data:Z
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - SETUID
      - SETGID
    command: run --config /etc/caddy/Caddyfile
  caddy-relay:
    image: cgr.dev/chainguard/caddy:latest
    restart: unless-stopped
    volumes:
      - ./data/caddy-relay/Caddyfile:/etc/caddy/Caddyfile:ro,Z
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - SETUID
      - SETGID
    command: run --config /etc/caddy/Caddyfile

volumes:
  caddy_data:
    external:  true

The Caddy configuration file is trying to be converted directly from NGINX and is split into two containers:

  • caddy-terminate which handles all incoming traffic on ports 80 and 443. All traffic on port 80 should return 404, while all traffic on port 443 should be forwarded to the caddy-relay container on port 4433.
  • caddy-relay which is supposed to check the incoming ClientHello request information and match it to the corresponding backend service. If there is no match, it should immediately terminate the connection.

b. My complete Caddy config:

caddy-terminate:

connect.netrunner.academy:443 {
    tls {
        protocols tls1.2 tls1.3
    }
    
    reverse_proxy caddy-relay:4433
}

connect.netrunner.academy:80 {    
    route / {
        respond 404
    }
}

caddy-relay:

:4433 {
	@signal_service host chat.signal.org ud-chat.signal.org
	handle @signal_service {
		reverse_proxy https://chat.signal.org:443
	}

	@storage_service host storage.signal.org
	handle @storage_service {
		reverse_proxy https://storage.signal.org:443
	}

	@signal_cdn host cdn.signal.org
	handle @signal_cdn {
		reverse_proxy https://cdn.signal.org:443
	}

	@signal_cdn2 host cdn2.signal.org
	handle @signal_cdn2 {
		reverse_proxy https://cdn2.signal.org:443
	}

	@signal_cdn3 host cdn3.signal.org
	handle @signal_cdn3 {
		reverse_proxy https://cdn3.signal.org:443
	}

	@cdsi host cdsi.signal.org
	handle @cdsi {
		reverse_proxy https://cdsi.signal.org:443
	}

	@content_proxy host contentproxy.signal.org
	handle @content_proxy {
		reverse_proxy https://contentproxy.signal.org:443
	}

	@uptime host uptime.signal.org
	handle @uptime {
		reverse_proxy https://uptime.signal.org:443
	}

	@sfu host sfu.voip.signal.org
	handle @sfu {
		reverse_proxy https://sfu.voip.signal.org:443
	}

	@svr2 host svr2.signal.org
	handle @svr2 {
		reverse_proxy https://svr2.signal.org:443
	}

	@updates host updates.signal.org
	handle @updates {
		reverse_proxy https://updates.signal.org:443
	}

	@updates2 host updates2.signal.org
	handle @updates2 {
		reverse_proxy https://updates2.signal.org:443
	}

	@svr31 host backend1.svr3.signal.org
	handle @svr31 {
		reverse_proxy https://backend1.svr3.signal.org:443
	}

	@svr32 host backend2.svr3.signal.org
	handle @svr32 {
		reverse_proxy https://backend2.svr3.signal.org:443
	}

	@svr33 host backend3.svr3.signal.org
	handle @svr33 {
		reverse_proxy https://backend3.svr3.signal.org:443
	}

	handle {
		abort
	}
}

5. Links to relevant resources:

All of my configurations are intended to be a direct mirror of the official Signal-TLS-Proxy repository which uses NGINX, but migrated to Caddy. The relevant files I am attempting to convert are listed below:

nginx-terminate (used as a base for caddy-terminate):

user  nginx;
worker_processes  auto;

events {
    worker_connections  1024;
}

http {
    server {
        listen 80;

        location /.well-known/acme-challenge/ {
            # init-certificate.sh uses --standalone, so we must proxy renewals to the certbot server
            proxy_pass http://certbot:80;
        }

        location / {
            return 404;
        }
    }
}

stream {

    upstream relay {
         server nginx-relay:4433;
    }

    server {
        listen                443 ssl;
        proxy_pass            relay;

        access_log            off;
        error_log             /dev/null;

        ssl_certificate /etc/letsencrypt/active/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/active/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf;
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
     }

}

nginx-relay (used as a base for caddy-relay):

user  nginx;
worker_processes  auto;


events {
    worker_connections  1024;
}

stream {
    map $ssl_preread_server_name $name {
        chat.signal.org                         signal-service;
        ud-chat.signal.org                      signal-service;
        storage.signal.org                      storage-service;
        cdn.signal.org                          signal-cdn;
        cdn2.signal.org                         signal-cdn2;
        cdn3.signal.org                         signal-cdn3;
        cdsi.signal.org                         cdsi;
        contentproxy.signal.org                 content-proxy;
        sfu.voip.signal.org                     sfu;
        svr2.signal.org                         svr2;
        updates.signal.org                      updates;
        updates2.signal.org                     updates2;
        backend1.svr3.signal.org                svr31;
        backend2.svr3.signal.org                svr32;
        backend3.svr3.signal.org                svr33;
        default                                 deny;
    }

    upstream signal-service {
         server chat.signal.org:443;
    }

    upstream storage-service {
        server storage.signal.org:443;
    }

    upstream signal-cdn {
        server cdn.signal.org:443;
    }

    upstream signal-cdn2 {
        server cdn2.signal.org:443;
    }

    upstream signal-cdn3 {
        server cdn3.signal.org:443;
    }

    upstream cdsi {
        server cdsi.signal.org:443;
    }

    upstream content-proxy {
        server contentproxy.signal.org:443;
    }

    upstream sfu {
        server sfu.voip.signal.org:443;
    }

    upstream svr2 {
        server svr2.signal.org:443;
    }

    upstream svr31 {
        server backend1.svr3.signal.org:443;
    }

    upstream svr32 {
        server backend2.svr3.signal.org:443;
    }

    upstream svr33 {
        server backend3.svr3.signal.org:443;
    }

    upstream updates {
        server updates.signal.org:443;
    }

    upstream updates2 {
        server updates2.signal.org:443;
    }

    upstream deny {
        server 127.0.0.1:9;
    }

    server {
        listen                4433;
        proxy_pass            $name;
        ssl_preread           on;
        error_log             /dev/null;
        access_log            off;
     }
}

If it makes it easier for context switching, below are the two git repositories:

Official Signal-TLS-Proxy repository: GitHub - signalapp/Signal-TLS-Proxy
My fork which tries to migrate it to Caddy: GitHub - MiahaCybersec/Signal-TLS-Proxy: An experimental port of signal's TLS proxy from NGINX to Caddy. Not currently recommended for production.

You haven’t actually said what’s not working. Show the problem. Show example requests, show your logs.

2 Likes

My apologies! Below are logs of caddy-relay when trying to connect through the Signal app.

{"level":"error","ts":1724048911.1883106,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"174.72.24.251","remote_port":"33150","client_ip":"174.72.24.251","proto":"HTTP/1.1","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"],"Accept-Language":["en-US,en;q=0.9"],"Connection":["close"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"connect.netrunner.academy"}},"duration":0.003698411,"status":502,"err_id":"x1z67qgrv","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724050340.4996786,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"5.164.29.116","remote_port":"33636","client_ip":"5.164.29.116","proto":"HTTP/1.1","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 (scanner.ducks.party)"],"Accept-Encoding":["gzip"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"connect.netrunner.academy"}},"duration":0.013764111,"status":502,"err_id":"ra4kmkf4j","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724050340.9463775,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"5.164.29.116","remote_port":"33636","client_ip":"5.164.29.116","proto":"HTTP/1.1","method":"GET","host":"connect.netrunner.academy","uri":"/favicon.ico","headers":{"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36 (scanner.ducks.party)"],"Accept-Encoding":["gzip"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"","server_name":"connect.netrunner.academy"}},"duration":0.002413049,"status":502,"err_id":"rfxgds6y8","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}

The expected result would be for caddy-proxy to match one of the defined hosts and act as a transparent proxy between the Signal server and the end user, but instead I’m seeing error 502 which would indicate my caddy config is not working as intended.

When trying to visit the proxy from Chrome, the expected result should be a basic error 404, but once again the logs are instead showing error 502. Below are the logs for desktop client requests:

{"level":"error","ts":1724048782.0440547,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"172.18.0.1","remote_port":"40698","client_ip":"172.18.0.1","proto":"HTTP/2.0","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"Sec-Ch-Ua-Platform":["\"Chrome OS\""],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-User":["?1"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Ch-Ua-Mobile":["?0"],"User-Agent":["Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"],"Sec-Fetch-Site":["none"],"Sec-Fetch-Mode":["navigate"],"Sec-Ch-Ua":["\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\""],"Dnt":["1"],"Priority":["u=0, i"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"],"Accept-Language":["en-US,en;q=0.9"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"connect.netrunner.academy"}},"duration":0.018127019,"status":502,"err_id":"nepxej5ud","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724048910.7399435,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"172.18.0.1","remote_port":"40698","client_ip":"172.18.0.1","proto":"HTTP/2.0","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"Sec-Fetch-Dest":["document"],"Accept-Language":["en-US,en;q=0.9"],"Sec-Ch-Ua":["\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\""],"Sec-Ch-Ua-Platform":["\"Chrome OS\""],"Upgrade-Insecure-Requests":["1"],"Dnt":["1"],"User-Agent":["Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"],"Priority":["u=0, i"],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Fetch-Site":["none"],"Accept-Encoding":["gzip, deflate, br, zstd"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"connect.netrunner.academy"}},"duration":0.017589707,"status":502,"err_id":"iuinam6d2","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724048922.9016356,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"172.18.0.1","remote_port":"40698","client_ip":"172.18.0.1","proto":"HTTP/2.0","method":"GET","host":"connect.netrunner.academy","uri":"/","headers":{"Dnt":["1"],"Sec-Fetch-Site":["none"],"Upgrade-Insecure-Requests":["1"],"Sec-Purpose":["prefetch;prerender"],"Purpose":["prefetch"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"],"Sec-Fetch-Dest":["document"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Sec-Fetch-User":["?1"],"Priority":["u=0, i"],"Sec-Ch-Ua":["\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\""],"Sec-Ch-Ua-Mobile":["?0"],"Sec-Ch-Ua-Platform":["\"Chrome OS\""],"User-Agent":["Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36"],"Sec-Fetch-Mode":["navigate"],"Accept-Language":["en-US,en;q=0.9"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"connect.netrunner.academy"}},"duration":0.013143114,"status":502,"err_id":"fdx61i17d","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}
{"level":"error","ts":1724056955.229329,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"172.18.0.1","remote_port":"53574","client_ip":"172.18.0.1","proto":"HTTP/2.0","method":"GET","host":"connect.netrunner.academy","uri":"/favicon.ico","headers":{"User-Agent":["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15"],"Referer":["https://connect.netrunner.academy/"],"Accept-Encoding":["gzip, deflate, br"],"Accept":["*/*"],"Sec-Fetch-Site":["same-origin"],"Sec-Fetch-Dest":["image"],"Accept-Language":["en-CA,en-US;q=0.9,en;q=0.8"],"Sec-Fetch-Mode":["no-cors"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":"connect.netrunner.academy"}},"duration":0.00295089,"status":502,"err_id":"3jp1qdm08","err_trace":"reverseproxy.statusError (reverseproxy.go:1269)"}

I think it’s because you’re doing abort – the connection is closed, so the front Caddy sees an EOF cause it has nothing left to read from the connection because it was aborted by the back Caddy.

Your request has Host: connect.netrunner.academy but none of your matchers on your back Caddy handles that domain, so it falls through to the abort handle.

1 Like

You’re trying to run Signal proxy, which requires TLS-in-TLS, so it’s not plain HTTP(S). For that you need the layer4 app.

Anyways, translating the nginx config to Caddy layer4 was already done by another member. See the repo:

3 Likes

Thanks Mohammed! I did take a look at layer 4, but it seems like I overlooked some of the finer details of the Signal proxy implementation. I’ll fork their repo and mark this issue as closed :slightly_smiling_face:

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