
A tunnel can solve reachability and still leave the important question unanswered: which request metadata should the service trust?
I ran into that question while thinking through secure remote access to a home control UI.
At first, the problem looked familiar: how do I put a safe front door in front of a thing I can reach at home?
Then I got to the part that matters more than the front door. Once a request has passed through a proxy, what does the service on the other side actually know about the person making it?
That is where a lot of “I put it behind a tunnel” thinking gets a little too cozy.
A proxy changes the shape of a request. It does not magically turn every bit of metadata attached to that request into truth.
The IP address stopped meaning what I thought it meant
When a reverse proxy sits between a visitor and an origin, the origin normally sees the proxy as the network peer. In Cloudflare’s ordinary proxied setup, that means the origin sees a Cloudflare IP by default rather than the visitor’s transport-layer IP.
That is not a bug. It is the job.
The proxy receives the request, then makes a new request to the origin. From the origin’s perspective, the proxy is the thing knocking on the door. If I only looked at the direct connection IP, I would be learning something real but not very useful: yes, the request came from the proxy I deliberately put there.
So proxies pass along extra context. For ordinary Cloudflare-proxied requests, Cloudflare sends CF-Connecting-IP to the origin. Its documentation describes that as the client IP that connected to Cloudflare, and its restoration guide calls it the original visitor IP.
Useful? Absolutely.
A magic identity stamp? Nope.
That distinction is the whole post.
A header is a claim, not a credential
It is tempting to see a client-IP header and treat it as the answer. The request says who it came from. Job done.
But a forwarded IP value is still a value arriving in an HTTP request. Its meaning depends on who was allowed to provide it and whether the path to the application has been verified.
If an application accepts a forwarding header from anywhere, a client can potentially provide a flattering little fiction about where the request came from. That can make IP allowlists, rate limits, audit logs, or any “only let me in from here” rule built on top of the address misleading.
The safe mental model is simpler than the implementation details:
- A proxy can provide client-address information.
- The application should accept that information only from proxy addresses it explicitly trusts.
- The trusted path matters as much as the header name.
- A direct path to the origin, or a trust boundary that is too broad, changes the security story.
I like this model because it refuses to turn a convenient header into a personality test. The header is useful evidence. It is not proof by itself.
Why this matters more at home
Home-scale projects have a particular way of making security feel smaller than it is.
I am not running a giant public platform. I am thinking about one control screen. One service. One thing I want to reach without making my home network into a terrible idea.
That small scope can make the setup look obvious: place a proxy in front, add an authentication layer, call it a night.
But the origin still has to make decisions. Maybe it logs client addresses. Maybe it limits requests. Maybe some route has an extra rule. Maybe it is only trying to tell a useful access log from a pile of proxy traffic.
Every one of those decisions depends on what the service trusts.
The tunnel or proxy handles reachability. The trust boundary handles meaning. Those are related, but they are not the same job.
And honestly, the second job is easier to forget because nothing dramatic happens while I am setting it up. The page loads. The proxy works. The lock icon looks reassuring. Meanwhile, a tiny assumption about forwarded headers can quietly decide whether the origin is reading a trusted signal or accepting user-supplied noise.
The boring question I want to ask earlier
The question I am taking away from this is not “which header has the real IP?”
That wording smuggles in an answer before the work starts.
A better question is: “Which component is allowed to tell this service about the client, and how does the service know the request actually came through that component?”
That leads to less glamorous checks:
- Is the origin receiving traffic only through the intended proxy path?
- Does the application have a narrow, correct set of trusted proxy addresses?
- What should happen if a forwarded-IP header appears on a request that did not come from that path?
- Are logs and access rules using an address with the right scope for the decision being made?
None of this is exciting. It is more like labeling the breakers before something trips.
But it is the kind of boring that saves a future evening.
Trust is the actual configuration
I went into this thinking about remote access. I came out thinking about provenance.
The important thing was not merely that an address showed up in a header. The important thing was whether the service had a defensible reason to believe that header came from the proxy it was supposed to trust.
Cloudflare’s documentation is clear about the moving pieces: a proxied origin sees the proxy by default, CF-Connecting-IP carries the client address for ordinary proxied traffic, and restoring that information requires correct trusted-proxy handling. RFC 7239 makes the broader point too: conclusions about a forwarding chain depend on trusted proxies.
So my rule for home tinkering is now boringly strict: every useful piece of request metadata needs a story about where it came from.
If I cannot explain that story, I should not build a security decision on it.



