Method for returning real client IP when using reverse proxy

1. Caddy version (caddy version):

On the reverse proxy:
v2.1.1 h1:X9k1+ehZPYYrSqBvf/ocUgdLSRIuiNiMo7CvyGUQKeA=

On the caddy web server
v2.1.1 h1:X9k1+ehZPYYrSqBvf/ocUgdLSRIuiNiMo7CvyGUQKeA=

2. How I run Caddy:

Caddy Reverse proxy in the cloud, and Caddy on the backend

a. System environment:

Debian 9

b. Command:

sudo systemctl start caddy

c. Service/unit/compose file:

N/A

d. My complete Caddyfile or JSON config:

Caddy Reverse Proxy: (floatboat.ninja)

floatboat.ninja
{
        header {
                # Set server header
                Server "Apache"

                # enable HSTS
                Strict-Transport-Security "max-age=31536000"

                # disable clients from sniffing the media type
                X-Content-Type-Options nosniff

                # clickjacking protection
                X-Frame-Options DENY

                # keep referrer data off of HTTP connections
                Referrer-Policy no-referrer-when-downgrade
        }
        reverse_proxy vpc1029.mkz.com:5656 {
                header_down X-Real-IP {http.request.remote}
        }
        log {
                level DEBUG
                output file /var/log/access.log {
                        roll_size 1gb
                        roll_keep 5
                        roll_keep_for 720h
                }
        }
}

Caddy Webserver (vpc1029.mkz.com):

:5656 {
	root * c:\users\caddy\documents\caddyroot
	encode gzip
	header {
		-Server
		-X-Content-Type-Options
	}
	file_server

	log {
		level DEBUG
		output file c:\users\caddy\documents\access.log {
			roll_size 1gb
			roll_keep 5
			roll_keep_for 720h
		}
	}
	handle_errors {
		@404 {
			expression {http.error.status_code} == 404
		}
		templates
		respond @404 "404"
	}
}

3. The problem I’m having:

I’m trying to get the real IP address of the connecting client from a reverse proxy so that I can return it in the respond of the handle_errors section on the caddy webserver. When I use {remote}, it’s returning the IP address of the proxy server, not the originating client (which is expected). I’ve looked for a way of passing over the IP address in the reverse proxy and while it works, I’m not sure if there’s a directive / template that I can then use that will allow me to specify the custom header and return it in the 404 directive. i.e.

handle_errors {
		@404 {
			expression {http.error.status_code} == 404
		}
		templates
		respond @404 "{header.X-Real-IP}"
}

4. Error messages and/or full log output:

None

5. What I already tried:

I’ve tried to use {http.request.remote.host} {remote} but they both return the proxy ip address, not the client’s ip.

6. Links to relevant resources:

N/A

Hi @angx1ty, welcome to the Caddy community.

I gave this a really quick shot:

{
  debug
}

http://:8080 {
  reverse_proxy http://localhost:8081
}

http://:8081 {
  file_server
  @404 expression {http.error.status_code} == 404
  handle_errors {
    respond @404 "{header.X-Forwarded-For}"
  }
}

And ran wget http://apollo.whitestrake.net:8080 from PowerShell on a nearby Windows computer (that hostname resolves locally to my laptop IP via internal DNS). PowerShell showed a good result:

wget : 192.168.0.162

(That’s the private IP of my Windows computer.)

Debug from the Caddy server looks good:

2020/07/24 00:21:44.781 DEBUG http.handlers.reverse_proxy upstream roundtrip {"upstream": "localhost:8081", "request": {"method": "GET", "uri": "/", "proto": "HTTP/1.1", "remote_addr": "192.168.0.162:60303", "host": "apollo.whitestrake.net:8080", "headers": {"X-Forwarded-For": ["192.168.0.162"], "X-Forwarded-Proto": ["http"], "User-Agent": ["Mozilla/5.0 (Windows NT; Windows NT 10.0; en-AU) WindowsPowerShell/5.1.19041.1"]}}, "duration": 0.001797846, "headers": {"Server": ["Caddy"], "Date": ["Fri, 24 Jul 2020 00:21:44 GMT"], "Content-Length": ["13"]}, "status": 404}

So it really seems like your solution works perfectly.


P.S.: Having a quick gander upon yonder documentation:

By default, Caddy passes thru incoming headers to the backend—including the Host header—without modifications, with two exceptions:

reverse_proxy (Caddyfile directive) — Caddy Documentation

You might therefore be needing naught but the default {header.X-Forwarded-For} field.

3 Likes

Thank you @Whitestrake that was exactly what I was looking for! The {header.X-Forwarded-For} field did the trick.

Cheers.

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