How to reverse proxy

March 12, 2023

Today i was fighting this problem for a few hours because i didnt know how reverse proxing truly works at its core. So this posts is a notes to myself because i know in the future i will step on the same stone.

I had two containers, one running FreshRSS and the other Caddy as a reverse proxy. Both were working on its own, they could communicate, but whenever i tried entering the url, it returned 404.

This was my initial Caddyfile

domain {
	reverse_proxy /fresh/* container_ip

First i tried looking for Caddy logs, but it didnt show anything, so the problem must be on the FreshRSS side i thought. Looking at its logs, i saw that it was requesting the /freshrss/ path, and not the /.

Looking through forums i came to this post that clarify everything. The main part was this:

When Caddy proxies to a backend, it naturally forwards the entire requested URI along with it.

It makes absolute sense, but being used to router libs, i thought that it stripped the /freshrss part. To do that, it suggested to use the without modifier, but it was v1 only. After hopping through different github issues: 1 and 2, i arrived to the final and correct directive handle_path, which does exactly what i thought it was doing at the begining.

Now my final Caddyfile looks like this:

domain {
	redir /fresh /fresh/
	handle_path /fresh/* {
		reverse_proxy container_ip {
			header_up X-Forwarded-Prefix /fresh

What can i learn from this? That Caddy is simple and beautiful to use, at least for now. And if you want to do something, maybe reading its docs, understanding what you are doing and how it works underneath is essential.

Leave your comment on the github issue, sending me an email or DMing me on twitter