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.

1 Like

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.

1 Like

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