Need help with reverse proxy

I have always used caddy for very simple 2/3 liner reverse proxy use cases like accessing my jellyfin & other services on my domain which has worked flawlessly. Currently, i need to use reverse proxy for a very simple but different usecase and i am unable to get it working the way i want.
I have 2 VPS, lets call them VPS A & VPS B. I want to send a request to this API on VPS A - curl -v https://api.telegra.ph/getPageList?... but this api domain (api.telegra.ph) is inaccessible from there and works totally fine on VPS B. So i want to host a reverse proxy server on VPS B pointing to this api so that i can access it on VPS A by sending a request to this proxy server which forwards request to this api.

I have deployed Caddy on VPS B with the following Caddyfile

:8001 {
        tls internal {
                on_demand
        }
        # Basic Authentication
        basic_auth /* {
                user HASHED_PASS
        }

        # Proxy requests to the Telegraph API
        reverse_proxy https://api.telegra.ph {
                header_up Host {host}
        }
}

On VPS B itself, when i directly send a request to this api, it returns the i requested info
curl -v https://api.telegra.ph/getPageList?access_token=d3b25feccb89e508a9114afb82aa421fe2a9712b963b387cc5ad71e58722&limit=3

But sending a request to the proxy server doesn’t return any info at all
curl -k -u $USER:$PASS -v https://localhost:8001/getPageList?access_token=d3b25feccb89e508a9114afb82aa421fe2a9712b963b387cc5ad71e58722&limit=3

*   Trying 127.0.0.1:8001...
* Connected to localhost (127.0.0.1) port 8001 (#0)
* ALPN: offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: [NONE]
*  start date: Feb  6 06:44:39 2025 GMT
*  expire date: Feb  6 18:44:39 2025 GMT
*  issuer: CN=Caddy Local Authority - ECC Intermediate
*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* Server auth using Basic with user '$USER'
* h2h3 [:method: GET]
* h2h3 [:path: /getPageList?access_token=d3b25feccb89e508a9114afb82aa421fe2a9712b963b387cc5ad71e58722]
* h2h3 [:scheme: https]
* h2h3 [:authority: localhost:8001]
* h2h3 [authorization: Basic cHpwOjEyMzQ=]
* h2h3 [user-agent: curl/7.88.1]
* h2h3 [accept: */*]
* Using Stream ID: 1 (easy handle 0xaaab00f5caf0)
> GET /getPageList?access_token=d3b25feccb89e508a9114afb82aa421fe2a9712b963b387cc5ad71e58722 HTTP/2
> Host: localhost:8001
> authorization: Basic cHpwOjEyMzQ=
> user-agent: curl/7.88.1
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 302
< alt-svc: h3=":8001"; ma=2592000
< cache-control: no-store
< content-type: text/html; charset=UTF-8
< date: Thu, 06 Feb 2025 12:24:32 GMT
< location: https://core.telegram.org
< pragma: no-cache
< server: Caddy
< server: nginx/1.20.1
< strict-transport-security: max-age=31536000; includeSubDomains; preload
< x-redirect-host: localhost
<
* Connection #0 to host localhost left intact

Its not returning anything on VPS B (localhost) itself so there’s no point testing it anywhere else.
I am not even sure if this is the correct way to do it so would appreciate some help.

CADDY_VERSION=v2.9.1 (Installed using Docker)
OS - Debian 12 (bookworm)
PS / That access_token is public so not a problem in sharing it.

Replace this:

with this:

        # Proxy requests to the Telegraph API
        reverse_proxy https://api.telegra.ph {
                header_up Host "api.telegra.ph"
        }

or just this:

        # Proxy requests to the Telegraph API
        reverse_proxy https://api.telegra.ph

Your Caddy is working, and you’re even getting a response from Caddy:

< location: https://core.telegram.org

That is coming from the api.telegra.ph back-end because you keep asking it for Host: locahost

2 Likes

Thanks, that working properly. Just so i have understood it correctly. The reverse_proxy https://api.telegra.ph line tells caddy to forward incoming requests on Port 8001 to telegraph api which in itself is sufficient to fulfil my usecase. What exactly is the usecase for header_up Host {host}. I saw it being used in some other similar questions which i why i had it in here.

Sometimes, you need to communicate with a remote back-end using its IP address or one of its hostnames, but the website on that server only responds when accessed via a specific name. In such cases, you just need to specify the Host header for that particular site:

header_up Host "SITE_FQDN"

or whatever Host HTTP request header value you want to send. The remote back-end then uses that Host header value to pick the correct website from all the other sites it serves.

The result is the same as running:

curl -v http://IP_ADDRESS:PORT -H 'Host: SITE_FQDN'

For example:

curl -kv https://149.154.164.13 -H 'Host: api.telegra.ph'

Your other option, with your previous Caddy config, was:

curl -k -u $USER:$PASS -v `https://localhost:8001/getPageList?access_token=d3b25feccb89e508a9114afb82aa421fe2a9712b963b387cc5ad71e58722&limit=3` -H 'Host: api.telegra.ph'

With your old Caddy config, which had header_up Host {host}, Caddy would have taken api.telegra.ph from -H 'Host: api.telegra.ph' curl command and used it as {host} in header_up Host {host}.

1 Like

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