Reverse proxy with multiple different upstreams, with paths

I’ve been trying to figure this one out, but either I have found solutions that only worked with v1, or I have found stuff that straight up does not work.

I have example.com facing publicly.

Caddy is acting as a reverse proxy that sends the traffic to my backend-server example.local
I want to be able to serve example.com/abc that I want to point to example2.local:8888/abc and example.com/def that I want to point to example2.local:8888/def

It seems like I can’t use rewrite on the request to the upstream server?

Answered here:

https://github.com/caddyserver/caddy/issues/4019

Great!

But I must take this opportunity to, well, criticize the documentation.

It’s really easy to get caddy up and running, but as soon as you need to do stuff like above, you’re pretty much lost.

Nowhere in the documentation about reverse_proxy was there a mention of handle, and when I look at the section on handle, there is very limited information. It says that handle “Evaluates a group of directives”. Ok great… which directives? It mentions “handler directives or directive block”. What is a directive block? handle is listed among the directives, does that mean I can do nested handle-statements?

I understand that no one is better off with just a huge document-dump when they are getting started, but instead of having to hunt around various forums and trying to weed out obsolete Caddy v1 info it would be great with more technical, exhaustive info. More “cookbooks” and real-world examples. How do I set up a CSP for example?

1 Like

Thanks for the feedback. Thing is, most of what you want to know is already in the docs.

Nonetheless, I have some answers:

Most of them, the ones that make sense when defining HTTP routes, I guess: Caddyfile Directives — Caddy Documentation

This is explained in the Concepts page. There’s a whole section about blocks. Caddyfile Concepts — Caddy Documentation

Yep!

I am writing a bunch of articles about Caddy (and related topics) for sponsors here: Chapters - Expert Caddy

Of course, we welcome anyone else to contribute examples and tutorials to our wiki, linked at the top here. Lots of people complain but almost nobody does anything about it. It’s exhausting for us, the maintainers, to keep up with it all, so for our own mental health we back down before we can satisfy everyone’s requests.

To do what though? That’s a security decision you’ll have to make. No one can decide what your CSP is, except you. Copying and pasting one is a bad idea. As you know, CSP is just a header. So, as you would expect, use the header directive: header (Caddyfile directive) — Caddy Documentation

Might be useful to add a “suggested directives” at the end. One documentation which roes it right in this asoect is Matlab.

What do you mean? The docs can’t guess what the user might want to do. All directives are “suggested”. They wouldn’t exist if they didn’t have a reason to exist.

For example in the reverse_proxy the suggested directives could be route, handle, and named matchers. At a minimum the commonly used parent directives, alternative siblings e.g. in handle also have handle_path linked, and directives commonly used together. These relationship might need to be done manually or kickstarted from existing examples.

So… you mean like the examples at the bottom of each directive pages, that already exist?

That’s already a thing.

If there’s any specific examples you feel are missing, feel free to open a pull request with your proposed changes on the website repo:

Not exactly. Check the design of this matlab doc, particularly the see also part. It’s more of a quick navigation and exploration rather than detailed examples.

Thing is, most of what you want to know is already in the docs.

I’m sure it is, but take my situation for example. I wanted to do a reverse proxy, with a path, a not too uncommon situation I would figure. When you read the documentation for reverse-proxy it says “Additionally, schemes cannot contain paths or query strings, as that would imply simultaneous rewriting the request while proxying, which behavior is not defined or supported”

That reads a lot like “No, you can not use paths in upstreams. End of discussion”.

But you can, sort of… if you use the handle-directive. The documentation may be technically correct, but from a user-perspective it becomes confusing.

It’s like trying to learn a language from just a dictionary. You can see how a word is spelled, but the lack of real-world examples makes it extremely difficult.
With nginx and apache there is such a long history that all these fringe cases has already been dealt with and googling for your specific situation is very much easier.

As you know, CSP is just a header.

I know, but does Caddy have a special directive to handle a CSP, or does one do it manually? I don’t have to add the X-Forwarded-For header when using reverse_proxy, so maybe there is something similar in Caddy if you want to use a CSP or HSTS.

Of course, we welcome anyone else to contribute examples and tutorials to our wiki

I’m sorry, I actually didn’t realize that was a wiki, I just thought it was a redirect to the userforum.

There’s examples at the bottom of the page for every directive. The information is all there.

Like I said, if you think something is missing, please propose the change on the GitHub - caddyserver/website: The Caddy website repo.

There is not. Again, the reason is that CSP is completely user-specific, there’s nothing Caddy can do to assume what you need. For X-Forwarded-For, it’s always going to work the same way (Caddy grabs the remote address of the request, prepends it to the existing X-Forwarded-For header if already set, passes it along). It’s a bit apples and oranges here.

There’s examples on the header directive page, including one for HSTS: header (Caddyfile directive) — Caddy Documentation

Thanks to your feedback, I just realized there’s a typo that makes this not make any sense. It should read:

Additionally, upstream addresses cannot contain paths or query strings, as that would imply simultaneous rewriting the request while proxying, which behavior is not defined or supported.

Okay, well, I think that would be a little ridiculous for any reverse proxy, but just to be extra clear how about we change it to:

Additionally, upstream addresses specified in configuration cannot contain paths or query strings, as that would imply simultaneous rewriting the request while proxying, which behavior is not defined or supported.

(But to be honest, I do think that it’s pretty obvious that we’re already talking about upstream addresses defined in configuration, since that’s literally what the whole section is about.)

To be clear, the handle directive has nothing to do with proxying. The reverse proxy will always (by default) pass through pretty much every aspect of the request except the few things that are noted (a few hop-by-hop proxy headers, for example). The request URI components, all other headers, and the body go through unchanged.

Yep! Our wiki category lets anyone share their expertise and discuss improvements to their articles. It doesn’t require any pull requests or code review, and anyone can easily edit and improve on contributions.

Okay, well, I think that would be a little ridiculous for any reverse proxy

Both Nginx and Apache allows for a URL with paths to be used as a upstream, so Caddy not allowing it would actually be the outlier…

Yep! Our wiki category lets anyone share their expertise and discuss improvements to their articles. It doesn’t require any pull requests or code review, and anyone can easily edit and improve on contributions.

It doesn’t seem to be indexed by google, maybe that was why I couldn’t find anything…

Of course Caddy sends paths to upstreams as part of the request. The behavior you’re talking about is prepending paths with something, which Caddy can do; just not in the upstream definition. There’s no value in having it done where the upstream is defined, it only adds confusion and complexity, because it’s a rewriting rule/task, not something that has anything to do with proxying.

1 Like

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