Newbe Configuring Caddy as Reverse Proxy

1. The problem I’m having:


I am new to caddy. Previously I always used Apache2 as my webserver of choice. I am trying to use caddy as reverse proxy. I am using LXD container with Ubuntu 22.14. have installed node server and caddy successfully. I am using node v22.14. All of this is in an LXD container. When I browse to the container IP (192.168.1.47)
Node: WebTransport server running on https://localhost:4433 When I browse to 192.168.1.47, I get a HTTP/2 502 error? →

2. Error messages and/or full log output:

$  journalctl -u caddy --no-pager | less +G
"name":"srv1","protocols":["h1","h2","h3"]}
Mar 02 13:41:33 CaddyWebtransport caddy[1226]: {"level":"info","ts":1740922893.4507356,"logger":"http","msg":"enabling automatic TLS certificate management","domains":["192.168.1.47"]}
Mar 02 13:41:33 CaddyWebtransport caddy[1226]: {"level":"info","ts":1740922893.4749036,"msg":"autosaved config (load with --resume flag)","file":"/var/lib/caddy/.config/caddy/autosave.json"}
Mar 02 13:41:33 CaddyWebtransport caddy[1226]: {"level":"info","ts":1740922893.4751632,"msg":"serving initial configuration"}
Mar 02 13:41:33 CaddyWebtransport systemd[1]: Started caddy.service - Caddy.
Mar 02 15:14:27 CaddyWebtransport caddy[1226]: {"level":"error","ts":1740928467.7561662,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"192.168.1.38","remote_port":"58394","client_ip":"192.168.1.38","proto":"HTTP/2.0","method":"GET","host":"192.168.1.47","uri":"/","headers":{"Sec-Fetch-Mode":["navigate"],"Sec-Fetch-User":["?1"],"Te":["trailers"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"],"Accept-Language":["en-US,en;q=0.5"],"Sec-Fetch-Dest":["document"],"Sec-Fetch-Site":["none"],"Priority":["u=0, i"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Upgrade-Insecure-Requests":["1"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":""}},"duration":0.020323073,"status":502,"err_id":"c7df0m7jz","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}
Mar 02 15:14:44 CaddyWebtransport caddy[1226]: {"level":"error","ts":1740928484.481648,"logger":"http.log.error","msg":"EOF","request":{"remote_ip":"192.168.1.38","remote_port":"58394","client_ip":"192.168.1.38","proto":"HTTP/2.0","method":"GET","host":"192.168.1.47","uri":"/","headers":{"Sec-Fetch-Dest":["document"],"Accept-Language":["en-US,en;q=0.5"],"Sec-Fetch-User":["?1"],"Sec-Fetch-Site":["none"],"Te":["trailers"],"User-Agent":["Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0"],"Accept-Encoding":["gzip, deflate, br, zstd"],"Upgrade-Insecure-Requests":["1"],"Sec-Fetch-Mode":["navigate"],"Priority":["u=0, i"],"Accept":["text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"]},"tls":{"resumed":false,"version":772,"cipher_suite":4865,"proto":"h2","server_name":""}},"duration":0.002250698,"status":502,"err_id":"4x5bhjk8g","err_trace":"reverseproxy.statusError (reverseproxy.go:1373)"}

3. Caddy version: v2.9.1 h1:OEYiZ7DbCzAWVb6TNEkjRcSCRGHVoZsJinoDR/n9oaY=

4. How I installed and ran Caddy:

sudo apt update
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -fsSL https://dl.cloudsmith.io/public/caddy/stable/gpg.key | sudo tee /usr/share/keyrings/caddy-keyring.asc >/dev/null
echo "deb [signed-by=/usr/share/keyrings/caddy-keyring.asc] https://dl.cloudsmith.io/public/caddy/stable/deb/debian any-version main" | sudo tee /etc/apt/sources.list.d/caddy.list
sudo apt update
sudo apt install -y caddy
sudo systemctl start caddy

a. System environment:

b. Command: sudo systemctl start caddy


### c. Service/unit/compose file:
### d. My complete Caddy config:
<!--
vboxuser@CaddyWebtransport:/etc/caddy$ caddy fmt
# The Caddyfile is an easy way to configure your Caddy web server.
#
# Unless the file starts with a global options block, the first
# uncommented line is always the address of your site.
#
# To use your own domain name (with automatic HTTPS), first make
# sure your domain's A/AAAA DNS records are properly pointed to
# this machine's public IP, then replace ":80" below with your
# domain name.

:80 {
# Set this path to your site’s directory.
root * /usr/share/caddy

    # Enable the static file server.
    file_server

    # Another common task is to set up a reverse proxy:
    # reverse_proxy localhost:8080

    # Or serve a PHP site through php-fpm:
    # php_fastcgi localhost:9000

}

Remove the servers block and the experimental_http3 option completely,

as Caddy 2 automatically supports HTTP/3 when using https.

localhost {
reverse_proxy localhost:4433
# tls internal # Optional: Use self-signed cert if you don’t have a domain
}

Refer to the Caddy docs for more information:

The Caddyfile — Caddy Documentation


### 5. Links to relevant resources:
<!-- Optional, but can help get us on the same page quickly. -->

Your upstream uses https, but you didn’t tell Caddy it requires HTTPS. You need to review the documentation of reverse_proxy.

So, I got caddy to serve up it’s default index page, but for http only.
How do I get an https working? Remember, I am using Oracle Vbox and
the VM is 192.168.1.47 so I can’t get a Let’s Encrypt cert.

You can still get Let’s Encrypt certificates if you use the DNS challenge with your DNS provider, if you have a domain name to use.

If you don’t have a domain name, it’s a longer story. You can use tls internal, but that requires knowing what you’re doing.

Is WebTransport server on the same VM? Is it in a container or a VM (you used both words)? Are you accessing Caddy from a different machine? You have to share more details

No domain. I have a Vbox configured with Ubuntu 22.04 locally on my Windows 10 PC. And webtransport server on the same VM which uses nodejs and on port 4433.

Here is my caddyfile:

The Caddyfile is an easy way to configure your Caddy web server.

Unless the file starts with a global options block, the first

uncommented line is always the address of your site.

To use your own domain name (with automatic HTTPS), first make

sure your domain’s A/AAAA DNS records are properly pointed to

this machine’s public IP, then replace “:80” below with your

domain name.

:80, :443 {
#reverse_proxy localhost:4433
# Set this path to your site’s directory.
root * /usr/share/caddy

    # Enable the static file server.
    file_server

    # Another common task is to set up a reverse proxy:
    # reverse_proxy localhost:8080

    # Or serve a PHP site through php-fpm:
    # php_fastcgi localhost:9000

}

Remove the servers block and the experimental_http3 option completely,

as Caddy 2 automatically supports HTTP/3 when using https.

https://localhost:443 {
#tls internal # Optional: Use self-signed cert if you don’t have a domain
reverse_proxy localhost:4433
tls internal
}

Refer to the Caddy docs for more information:

The Caddyfile — Caddy Documentation

Is there any other information you need, let me know.

Thanks for your time.

Ray

Please mind your formatting. Code blocks need to be between ```.

You seem to have 2 config blocks:

:80, :443 {
    #reverse_proxy localhost:4433
    # Set this path to your site’s directory.
    root * /usr/share/caddy

    # Enable the static file server.
    file_server

    # Another common task is to set up a reverse proxy:
    # reverse_proxy localhost:8080

    # Or serve a PHP site through php-fpm:
    # php_fastcgi localhost:9000
}

https://localhost:443 {
    #tls internal # Optional: Use self-signed cert if you don’t have a domain
    reverse_proxy localhost:4433
    tls internal
}

I don’t think you need the first block anymore. The second block can be:

localhost {
	reverse_proxy https://localhost:4433 {
		tls_insecure_skip_verify
	}
}

Caddy automatically generates a certificate for localhost. Your WebTransport server seems to run with HTTPS, so you’ll need to tell Caddy that the upstream address needs HTTPS. I’ve added tls_insecure_skip_verify to get things moving for now, but it’s best if you get the root certificate WebTransport uses, and tell Caddy to trust it by using tls_trust_pool. You need to read the reverse_proxy docs.

I’m getting this error:
Error: adapting config using caddyfile: /etc/caddy/Caddyfile:30: unrecognized directive: tls_insecure_skip_verify

I missed something

localhost {
	reverse_proxy https://localhost:4433 {
		transport http {
			tls_insecure_skip_verify
		}
	}
}

Nonetheless, you can always refer to the documentation.

Thanks Mohammed, that fixed it. Now on to Webtransport.

So actually caddy starts fine. The default index.html come up with http.
Https is still failing. I get:

Secure Connection Failed

An error occurred during a connection to 192.168.1.47. Peer reports it experienced an internal error.

Error code: SSL_ERROR_INTERNAL_ERROR_ALERT

You configured localhost but not 192.168.1.47.

Since I’m struggling to get even simple things to work, I decided it might be a good idea to run the caddy tutorials at

Well, even the very first step is not working?

‘’’
Create a new text file named Caddyfile (no extension).

The first thing you should type is your site’s [address]

localhost

If the HTTP and HTTPS ports (80 and 443, respectively) are privileged ports on your OS, you will either need to run with elevated privileges or use a higher port. To use a higher port, just change the address to something like localhost:2015 and change the HTTP port using the http_port Caddyfile option.

Then hit enter and type what you want it to do. For this tutorial, make your Caddyfile look like this:

localhost

respond "Hello, world!"

Save that and run Caddy (since this is a training tutorial, we’ll use the --watch flag so changes to our Caddyfile are applied automatically):

caddy run --watch

And here’s the response I get:

2025/03/09 18:11:45.270 INFO    using adjacent Caddyfile
2025/03/09 18:11:45.275 INFO    adapted config to JSON  {"adapter": "caddyfile"}
2025/03/09 18:11:45.277 INFO    admin   admin endpoint started  {"address": "localhost:2019", "enforce_origin": false, "origins": ["//localhost:2019", "//[::1]:2019", "//127.0.0.1:2019"]}
2025/03/09 18:11:45.278 INFO    http.auto_https server is listening only on the HTTPS port but has no TLS connection policies; adding one to enable TLS {"server_name": "srv0", "https_port": 443}
2025/03/09 18:11:45.278 INFO    http.auto_https enabling automatic HTTP->HTTPS redirects        {"server_name": "srv0"}
2025/03/09 18:11:45.278 INFO    tls.cache.maintenance   started background certificate maintenance      {"cache": "0xc00032f900"}
2025/03/09 18:11:45.279 INFO    pki.ca.local    root certificate is already trusted by system   {"path": "storage:pki/authorities/local/root.crt"}
2025/03/09 18:11:45.280 INFO    http    enabling HTTP/3 listener        {"addr": ":443"}
2025/03/09 18:11:45.281 INFO    http.log        server running  {"name": "srv0", "protocols": ["h1", "h2", "h3"]}
2025/03/09 18:11:45.282 WARN    http    HTTP/2 skipped because it requires TLS  {"network": "tcp", "addr": ":80"}
2025/03/09 18:11:45.282 WARN    http    HTTP/3 skipped because it requires TLS  {"network": "tcp", "addr": ":80"}
2025/03/09 18:11:45.283 INFO    http.log        server running  {"name": "remaining_auto_https_redirects", "protocols": ["h1", "h2", "h3"]}
2025/03/09 18:11:45.283 INFO    http    enabling automatic TLS certificate management   {"domains": ["localhost"]}
2025/03/09 18:11:45.285 INFO    autosaved config (load with --resume flag)      {"file": "/root/.config/caddy/autosave.json"}
2025/03/09 18:11:45.286 INFO    serving initial configuration
2025/03/09 18:11:45.287 INFO    watcher watching config file for changes        {"config_file": "Caddyfile"}
2025/03/09 18:11:45.335 INFO    tls     storage cleaning happened too recently; skipping for now        {"storage": "FileStorage:/root/.local/share/caddy", "instance": "dadd446a-c4d9-42f1-b2c8-6fd3f5d712d2", "try_again": "2025/03/10 18:11:45.335", "try_again_in": 86399.999998415}
2025/03/09 18:11:45.335 INFO    tls     finished cleaning storage units

What am I doing wrong?

Ray

I don’t understand what you’re seeing wrong. Are you testing Caddy by checking localhost or some other address on the browser?

I am following the tutorial. New Caddyfile like this:

localhost

respond "Hello, world!"

…then

caddy run--watch