I want to use Caddy as a reverse proxy for a bunch of misc docker containered web-apps

I find the biggest problem with what I’m doing is generally people assume I know what is going on behind the scenes with a web-app.

So for example lets take Jenkins, I have one, it builds software for my developer friends, it runs on java. This is basically the extent of my knowledge here. It likes to reside on port 8080, and it’s ideally served from behind a TLS certificate.

Another example being, I also installed hastebin in a container it’s a node.js app. It has a few frustrating quirks but its a great little web app with self-destructing pastes with a pretty curl’able interface. This runs on another port and needs to have some magic done to let the rest interface be available for curl’ing console output to ( my main usage case for this is dumping sections of logs for debugging. )

The odd time I install another web-app here and there but they all want various port redirects and all kinds of silly fun like this and don’t generally include much support for TLS and I want all of my connections to be encrypted. There’s no particular reason to encrypt except for the fact that I just prefer that the only people who can see what I’m doing are the people I wanted to see.

So far what I have is a Caddyfile that looks like this for Jenkins. My biggest desire here is to have Jenkins sit in a subdirectory and not at an A record. So lets say home.REDACTED.com would point to my home server with this whole setup. Then I want home.REDACTED.com/jenkins/ to point to localhost:8080. Then say home.REDACTED.com/p/ to point at localhost:8181 for Hastebin.

home.REDACTED.com {
    proxy / http://localhost:8080 {
        max_fails 0

Can anyone show me some relevant examples?

The main issue with most apps is that they assume they are at the root directory. It’s a pretty sane decision because it means they use relative internal links (e.g. href="/media/somephoto.jpg"), which is good practice and makes them quite flexible with regards to host (domain).

If you reverse proxy them in a subdirectory, though, you’ll run into issues where links generated from /app/media/ point to /media/blah instead of /app/media/blah, or perhaps /app/index.html requires CSS from /css/style.css instead of the correct /app/css/style.css.

To get around this you only have three options:

  1. Set the app’s “url base” if it allows it. Some apps let you do this and they will automatically prepend their links with it. e.g. urlbase=/app - if not, pester the dev to make it a feature.
  2. Use the Caddy proxy directive without /app and do some serious magic on the stream coming back to edit every instance of an internal link to re-prepend them all with /app. I have not seen a successful implementation of this.
  3. Use a subdomain instead.

It is very easy to configure Caddy to be ignorant these issues and proxy into the subdirectory regardless:

home.REDACTED.com {
    proxy /app localhost:8080 {

But lots of apps will break like this.

To quote, well, me: “Backend apps are awful.”


Very few like to play well with others on the same domain.


Amazing response thanks for the info. It’s basically what I ran into and I appreciate the confirmation. I’ve heard of things like rewrite granted I’ve really not delved into it but from my high-level understanding, it handles modifying requests in both directions on NGINX.

I suppose the main reason I was thinking that I’d want to do this is that I didn’t want to install a bunch of TLS certs but I suppose LetsEncrypt makes that free and Caddy makes it trivial.

It does indeed! Here are the important figures to know in terms of rate limits. The notable limit is 20 subdomains per week, essentially.

Caddy does rewrites too, but they just take a request from the client and modify it before processing. You could take /css/style.css and rewrite it to /app/css/style.css, then proxy it to your app without /app, but then you’re just using the root directory for your app anyway and you might as well put it there so you don’t have to write a lot of special handling.

Proxies and reverse proxies might be able to do some basic magic and modify the request both ways, but you run into serious trouble when we start talking about javascript/ajax/websockets/whatever.


I like caddy and use it where it makes sense.

Consider https://traefik.io/ it is designed for your use case.

I started with nginx, custom container with certs and config setup.
Then my situation got a bit more complex so I shifted to caddy because of auto LetsEncrypt, wunderbar!
Then I got even more containers and started doing CI/CD and redeploying the entire stack over and over again and things kept changing, so I found a way to dynamically update caddy, sweet.

Then I was looking through IRC and someone mentioned https://traefik.io/ so I now I use that…

Another way to deal with backends is to use sub-domains, then apps get to live at the root / of the domain :slight_smile:

1 Like

The automatic dynamic configuration looks really neat for that, actually. In Docker’s case are you able to comment on how it compares to jwilder’s docker-gen project?

It should be updated / improved in the future… But it should work.

I basically just want TLS on everything everywhere I get the chance I encrypt things with best practices but this said. Its got to be easy otherwise no one will use it, also there will be too many potential weaknesses.

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