I was reading the docs and assuming the answer is “no” but wanted to doublecheck.
Is there any “or” option for header_up?
My specific use-case: my setup is split in where all local traffic is only local, but all external traffic is through Cloudflare. If I want to do header_up X-Real-IP {http.request.header.CF-Connecting-IP}, that’s great for getting the real IP from Cloudflare, but when that data isn’t present (connection over LAN), then I end up with no (or ‘malformed’ some apps call it) data in that field.
In an ideal world, I would be able to configure Caddy to set that header only when CF-Connecting-IP is actually available, otherwise, just forward on the remote_ip or something.
Cloudflare should be sending along X-Forwarded-For – you can configure Caddy’s reverse_proxy with trusted_proxies to only trust that header if you received the request from one of Cloudflare’s IP ranges.
Use of X-Forwarded-For is more widespread than X-Real-IP, so I suggest you use that instead.
Best I can give you is that you can use the vars directive to set a variable conditionally (use a matcher for the condition) and then later use {vars.*} placeholder to set whatever.
But still, I don’t recommend using other headers for the remote IP, it’s risky. I’d suggest asking those applications to support X-Forwarded-For, either by default or by config
vars * {
realip "{http.request.header.CF-Connecting-IP}"
realip "{http.request.header.X-Forwarded-For}"
realip "{http.request.remote.host}"
}
The above with or without quotes at least feeds data to {vars.realip}, but I’m either getting a blank reply/variable or literally realip as a string…so…progress-ish, but not really sure what to do to get this to work how I want/expect.
Edit 3: A Caddyfile like this seems to work for a local test…so…
http://192.168.1.101:48312 {
vars {
realip {http.request.header.CF-Connecting-IP}
realip {http.request.header.X-Forwarded-For}
realip {http.request.remote.host}
}
respond * 200 {
body "{vars.realip}"
close
}
}
Edit 4: More rogress. Removed the * and quotes from Edit 2. Then in the reverse_proxy section, I have a header_up X-Real-IP "{vars.realip}" and now I can get the IP for LAN connections…but not the ones coming from Cloudflare. So now I’ve flipped the original issue lol
vars {
realip {http.request.header.CF-Connecting-IP}
realip {http.request.header.X-Forwarded-For}
realip {http.request.remote.host}
}
vars doesn’t do try_files-like fallback logic. It’s literally just realip = "value", that’s it.
So you need to use matchers to tell it not to set the value in a certain situation.
Here’s an example checking that the header has some value before setting the var. The first one always runs, but the following ones might run depending on the matcher, to overwrite the value. Also, I’m using placeholder shortcuts as per Caddyfile Concepts — Caddy Documentation
vars realip {remote_host}
@cf header CF-Connecting-IP *
vars @cf realip {header.CF-Connecting-IP}
@xf header X-Forwarded-For *
vars @xf realip {header.X-Forwarded-For}
But either way, this is dangerous, because this doesn’t do anything to prevent spoofing the XFF or CF headers, if some bad actor stuffs in their own value for one of those headers. That’s the point of the changes made in v2.5.0, they prevent spoofing by forcing you to provide a list of IP ranges you trust to have given you correct values for the XFF header, and not just made-up values.
The problem is my split setup, I guess. I’m also utterly behind Cloudflare with no open ports to connect to caddy via my WAN IP. A connection that isn’t internal LAN must and can literally only come from Cloudflare. If some bad actor can spoof whatever the Hell they want and Cloudflare lets them…seems like I have bigger problems.
My caddy is also 2.5.1.
There’s two ways I might access, for example, test.xnaas.info. Let’s say I’m reverse proxying an application on that subdomain that requires X-Real-IP.
If I don’t header_up, obviously X-Real-IP is never set. (duh)
If I do header_up X-Real-IP {remote_host}, I’m going to either get a LAN IP if accessing via LAN or get the Docker IP of my cloudflared container when from WAN, even with trusted_proxies private_ranges <all_cf_ipv4> <all_cf_ipv6> (which should cover the 172.x.x.x space Docker uses, no?).
If I do header_up X-Real-IP {header.CF-Connecting-IP} (or XFF) I will only ever get the X-Real-IP for WAN connections from Cloudflare, LAN connections seem to just get some docker IP set.
Hopefully the above better explains the situation.
Edit:
This does indeed seem to work like a charm. Thank you.