Proxy confusion

I’ve been using Caddy successfully for a couple months now but I am a little confused about something. From what I read, “transparent” should pass the client IP to the back end. But that’s not what is happening. Instead my server logs all show the IP of the machine running Caddy instead of the actual Internet host making the request. Am I doing something wrong?

Here’s an example from my caddyfile: {
proxy / {

What’s the code on the backend that is reading the client IP?

Actually I’ve noticed the problem in a couple different places. One is on an IBM Domino server where the web logs show only the Caddy IP. I’m also seeing it on a bunch of Joomla websites with Akeeba Admin Tools running. The web application firewall of Akeeba locks out IP addresses that attempt to log in too many times. Unfortunately, it sees the Caddy server instead of the original IP address and that is what gets locked out, which ends up locking EVERYONE out since they all look like that one IP address.

The transparent preset specifies a few headers, notably X-Real-IP and X-Forwarded-For. It’s up to the back end application to look for those headers and use them instead of the IP address of the connecting front end.

Some apps will do this automatically. Sometimes there will be setting somewhere like “Run behind reverse proxy” or “Get IP from proxy headers”. For Akeeba, for example:

Enable IP workarounds
When this option is disabled (default on new installations) Admin Tools will get the visitor’s IP only from the REMOTE_ADDR environment variable sent by your server to PHP. This is the most secure option but may cause a problem on certain sites which have a load balancer, reverse proxy, cache or CDN in front of the web server. In these cases the REMOTE_ADDR contains the IP address of the load balancer, reverse proxy, cache or CDN in front of the web server, NOT the IP address of the visitor. As a result all attacks will appear to be coming from the same IP address. Automatically or manually blocking this IP will disable your site for everyone. Moreover, features like IP whitelist, IP blacklist and so on will not work properly or at all.

On these setups Admin Tools you can set the Enable IP Workarounds option to Yes. This way Admin Tools can use the X-Forwarded-For HTTP header which is sent by the load balancer, reverse proxy, cache or CDN in front of the web server instead of REMOTE_ADDR. This HTTP header contains the real IP address of the visitor and Admin Tools’ IP-based features will work properly.


1 Like

Oh!! OK, yeah - on the Domino apps I was recording REMOTE_ADDR and REMOTE_HOST. I thought that “transparent” replaced those headers with the data from the visitor. I understand more about what is going on now though. Thanks!

I believe those two are set by PHP directly and would have to be spoofed, which isn’t very good behaviour :stuck_out_tongue:

Shows how much I know about this part of the networking stuff. Wait 'til you see the next dumb question I post…

1 Like

Well, I’m almost there. Fixed Akeeba. Changed my code to record X-Real-IP and X-Forwarded-For. But now both are recording the (correct) IP address but not the host name. Is there a bug in X-Forwarded-For or am I still confused?

OK. Which hostname are you referring to, the clients? Or the requested hostname? And where are you recording it from?

I’m just looking to replace Remote_Host which I was previously recording. I thought I could do that with X-Forwarded-For to get the client’s hostname.

Oh, looks like X_Forwarded_Host should do what I’m looking for but it doesn’t look like Caddy is providing that.

REMOTE_HOST is just the webserver doing a reverse lookup on the IP address given by REMOTE_ADDR. To clarify from my earlier statement, Apache provides these, I believe.

I think Apache’s mod_remoteip would solve this issue by correcting the REMOTE_ADDR internally. Might be a neater solution since you could feasibly allow the app to pretend it’s not behind a proxy at all.

Apache by default identifies the useragent with the connection’s client_ip value, and the connection remote_host and remote_logname are derived from this value. These fields play a role in authentication, authorization and logging and other purposes by other loadable modules.

mod_remoteip overrides the client IP of the connection with the advertised useragent IP as provided by a proxy or load balancer, for the duration of the request.


Eh, I’m over it. IP address is fine anyway. Don’t really need the hostname of the client. Looks like I’m good to go. Thanks for your help.

Yeah, you don’t want Caddy doing a DNS lookup at every request, I promise :wink:

Apart from the principle of the thing (imagine if every webserver did reverse lookups on every client ever, doubling DNS traffic across the globe?), is there any specific factor that would make Caddy especially unsuitable for doing that?

Extra .5-2 second latency on handling every request? But nothing specific to Caddy that I can think of.

I do have the same problem using Odoo as a backend and Caddy sitting on its own server between Cloudflare and the Odoo backend server.
In this thread somebody is reporting the same problem using nginx and and a suggested resolution should provide:

    port_in_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Front-End-Https On;
    proxy_set_header X-Real-IP $remote_addr;

Another guy identified the missing of

proxy_redirect off;

As the culprit. Can anybody help me shed some light on this?

TIA, trevi

proxy_redirect sets a string substitution for the contents of the refresh and location headers as they’re sent to the client (see nginx_http_proxy_module docs). It shouldn’t have any impact on your backend receiving the wrong IP address.

This is likely to be your issue. If you follow the steps above (enable IP workarounds, ensure the transparent preset is being used in Caddy), Odoo will be receiving the correct IP address… for the client that’s connecting to Caddy. With CloudFlare, though, you’ve got a reverse proxy in front of a reverse proxy, so the client connecting to Caddy will always be CloudFlare.

CF “solve” this issue by setting the CF-Connecting-IP header. So you will need to manually set the X-Forwarded-For header:

proxy /odoo {
    header_upstream X-Forwarded-For {>CF-Connecting-IP}

One important caveat: if anyone other than CloudFlare connects to Caddy, Odoo will have no idea who is connecting (because X-Forwarded-For will not have an IP address).

Thanks Matthew for your reply. As I also can’t make TLS work with cloudflare, I assume that I have some serious problem around this all.

I do have virtual hosts/domains, which are served by Cloudflare, one should be served by Route 53 in the future, but comes directly for now. Is your caveat meant for all virtual hosts or per virtual hosts, means can I have virtual hosts connected to via Cloudflare and other virtual hosts, which are connected to directly?

Example vhost with Cloudflare:, { # Your url should go here…
proxy / { # Fill in the correct port…
header_upstream X-Forwarded-For {>CF-Connecting-IP}

realip cloudflare

log /var/log/caddy/gastrosoftware.access.log
errors /var/log/caddy/gastrosoftware.error.log
tls {
dns cloudflare

and another one without cloudflare:,,, {

proxy / {
except /login

log /var/log/caddy/twandaNew.access.log
errors /var/log/caddy/twandaNew.error.log

TLS with CloudFlare is an interesting problem, particularly when combined with domain validation and Automatic HTTPS. You have DNS validation, though, so setting your site in CloudFlare to Full or Full (strict) SSL in the Crypto section should rectify the usual issues, if that’s the case.

Specifically, any client that:

  1. Sends a request to your server for a location that you have configured Caddy to:
    a. Proxy to Odoo, and;
    b. Forward the CF-Connecting-IP header in place of the remote placeholder, AND;
  2. Does not actually supply a CF-Connecting-IP header (so, anyone other than CloudFlare themselves)

will leave Odoo with an empty X-Forwarded-For header, which may have strange results with IP workarounds enabled. This might happen if you “grey-cloud” the A record for (www.) in CloudFlare and begin sending clients directly to the origin, or if someone manually requests that address from your server. With everything correctly configured, it is still possible, but shouldn’t happen.

I have to admit I don’t know all the implications of the realip middleware since I don’t need it for any of my sites (I completely forgot it existed, actually :sweat:). From its description, it might actually make the header_upstream subdirective unnecessary. If it does, it’s a better idea not to bother with using CF-Connecting-IP.