Traditional reverse proxies work well when the proxy can directly reach the upstream service. You put NGINX, Caddy, Traefik, Cloudflare, or another edge in front of an app, terminate TLS, and forward requests to the backend.
That model breaks down when the service lives behind NAT, in a customer site, in a homelab, on an industrial network, or on an edge device where you do not want to open inbound ports.
Cloud-brokered tunnels solve part of the problem. A connector inside the private network dials out to a public service, and users connect through that service. This is convenient, but it changes the trust boundary. The cloud edge often becomes the place where TLS terminates and policy is enforced.
A peer-to-peer reverse proxy takes a different path.
Instead of exposing the private network or forcing the public cloud to sit in the middle of decrypted traffic, the client joins a private overlay, resolves private domains inside that overlay, and sends traffic over a peer connection to a connector at the private network edge. TLS can terminate at the edge you control, close to the protected application.
The goal is simple:
- No inbound ports to the private site
- No public DNS record pointing at the private service
- Valid HTTPS in the browser
- Routing over an authenticated private path
- TLS termination at infrastructure you control
The Problem With Traditional Reverse Proxies
A normal reverse proxy usually assumes three things:
- The proxy is publicly reachable.
- The proxy can directly reach the upstream service.
- TLS terminates at the proxy.
For public web apps, that is fine. For private applications, it can be awkward.
Imagine these examples:
- A password manager hosted in a homelab
- A Grafana dashboard inside a private VPC
- A web UI for a hardware controller at a customer site
- An admin panel for an internal service
- A private app behind CGNAT
You might not want to expose any of these to the public internet, even behind an authentication screen. A login page still reveals that something exists. If the app or proxy has an undiscovered vulnerability, it may still be reachable by scanners.
Tunnels reduce the need for inbound firewall rules, but many cloud tunnel products still make the provider's edge a mandatory middle layer for decrypted traffic. That may be acceptable for many use cases, but it is not ideal when you need stricter data boundaries or want the protected site to remain the place where HTTPS is terminated.
What Is a Peer-to-Peer Reverse Proxy?
A peer-to-peer reverse proxy combines three ideas:
- A private overlay network
- A site connector that makes outbound connections from the private network
- A reverse proxy embedded at the private edge
The request path looks like this:
Browser
|
Local private DNS resolver
|
Client overlay interface
|
Peer connection / tunnel
|
Private site connector
|
Edge reverse proxy
|
Private application
The important difference is where the reverse proxy runs.
In a cloud-terminated tunnel, the public edge usually accepts HTTPS, terminates TLS, and forwards traffic down the tunnel.
In this peer-to-peer model, the tunnel carries the traffic toward the private site first. The reverse proxy runs at the site connector. TLS terminates there, inside the network boundary you control.
Cloud-Terminated vs Peer-Terminated At The Edge
Here is the traditional cloud-tunneled model:
And here is the peer-to-peer model with TLS termination at the edge:
The second model changes the trust boundary. The cloud control plane can still coordinate users, resources, certificates, and health, but private application traffic can stay on the path between the client and the private edge.
Step 1: Capture Private DNS
The first problem is DNS.
If a user types this into a browser:
https://vault.example.internal
you do not want that query leaking to a public resolver like 1.1.1.1 or 8.8.8.8.
The client needs to intercept DNS for private resources and resolve those names inside the overlay network. Normal internet DNS should continue working as usual. Only private resource names should be routed into the private access system.
This is one of the messier parts of the architecture because every operating system handles DNS differently.
On desktop and mobile platforms, a production implementation usually needs platform-native networking hooks:
- macOS network extensions or resolver configuration
- Windows networking APIs
- Linux resolver and network manager integration
- iOS and Android VPN/network extension APIs
The goal is to make the browser think it is resolving a normal domain while ensuring the resolution happens through a trusted local path.
Step 2: Map Domains To Logical Overlay Addresses
Once DNS is captured, the client can run a local resolver inside the private overlay.
Instead of resolving a private domain to a real LAN address, the resolver can map the hostname to a logical overlay address.
For example:
vault.example.internal -> 100.96.128.20
grafana.example.internal -> 100.96.128.21
The browser does not need to know this is special. It receives an IP address and opens a normal TCP connection.
Behind the scenes, the client knows that 100.96.128.20 is not a public internet destination. It is a logical resource address that should be routed over the private overlay to the right site connector.
This keeps the user experience familiar:
- The user opens a normal URL
- The browser performs normal DNS and TCP behavior
- The client routes private destinations into the overlay
- Public internet traffic remains unaffected
Step 3: Choose The Best Site Connector
A private resource may be reachable through more than one site connector.
For example, you might deploy connectors in:
- New York
- London
- Frankfurt
- A customer office
- A backup environment
The client can probe available connectors and choose the best path based on health and latency.
That gives you an anycast-like behavior without requiring the private application to be public:
- A user in New York can route through the New York connector
- A user in Europe can route through a European connector
- If one connector fails, traffic can fail over to another available connector
This is where the architecture starts to feel less like a simple tunnel and more like a private access fabric.
Step 4: Move Certificates To The Edge
Browsers still expect valid HTTPS.
That creates a challenge: how do you get valid certificates to a private connector that has no inbound ports and may not be reachable from the public internet?
A standard HTTP-01 ACME challenge will not work if the private edge cannot receive public HTTP traffic.
There are a few ways to solve this:
- Use DNS-based validation
- Issue certificates from a central control plane
- Push certificates securely to the private edge
- Keep certificates in memory or local protected storage on the connector
In this model, the public control plane can handle certificate issuance for domains it manages, then distribute the relevant certificate material to the connector that needs to terminate TLS.
The private edge does not have to expose itself for ACME challenges. It simply receives the certificate it needs to complete browser-trusted TLS.
Step 5: Terminate TLS At The Private Edge
Once the client selects a connector, traffic travels over the private path to the site.
At that point, an embedded reverse proxy at the connector handles the incoming stream. It can inspect SNI or the HTTP host header, select the right certificate, complete the TLS handshake, and forward the request to the private upstream.
The sequence looks like this:
1. Browser requests https://vault.example.internal
2. Local DNS maps the hostname to a private overlay address
3. Client routes packets over the peer connection
4. Site connector receives the stream
5. Embedded reverse proxy terminates TLS
6. Proxy forwards the request to the private app
The private app might be listening on something simple like:
http://10.0.10.15:8080
But the user still sees a clean HTTPS URL in the browser.
Step 6: Keep Policy And Audit Centralized
Moving the reverse proxy to the edge does not mean giving up centralized policy.
The connector can still enforce:
- Identity checks
- Resource-level authorization
- Session rules
- HTTP routing rules
- Audit logging
- Health reporting
The difference is that enforcement happens close to the protected resource, while metadata and audit events can still be sent back to a central control plane.
That gives you a useful split:
- The control plane manages configuration, identity, policy, certificates, and visibility
- The private edge handles local traffic, TLS termination, and resource forwarding
Tradeoffs
This architecture is powerful, but it is not free.
DNS Gets Complicated
Local DNS interception is brittle because operating systems and browsers do not all behave the same way. Some browsers cache DNS aggressively. If a private domain is queried before the access client is connected, the browser may cache a failed public lookup and ignore the corrected local resolver for a while.
There is no perfect fix for that. Sometimes the user has to wait for the cache to expire, open a new session, or restart the browser.
Certificate Distribution Must Be Secure
If certificates are pushed to the edge, that channel needs to be authenticated and protected. You also need clear behavior for renewal, revocation, connector compromise, and connector rotation.
Routing Needs Good Failure Handling
If multiple connectors can serve a resource, the client needs to make good routing decisions. Health checks, latency probes, failover behavior, and stale state handling all matter.
The Connector Becomes Critical Infrastructure
The private edge connector is now responsible for more than forwarding packets. It may handle TLS, routing, policy, and logging. That makes observability and upgrade safety important.
Where Pangolin Fits
This is the model Pangolin has been moving toward for private application access.
Pangolin combines:
- An open-source control plane
- Lightweight site connectors
- Peer-to-peer connectivity
- Private resource routing
- Automated certificate handling
- Reverse proxy behavior at the network edge
The result is a way to access private applications without opening inbound ports, while still giving users normal browser-based HTTPS access.
The code is available here:
https://github.com/fosrl/pangolin
Final Thoughts
Reverse proxies are not going away. They are still one of the most useful patterns for exposing web applications safely.
But private apps need a different default.
If the application is internal, sensitive, customer-hosted, or sitting behind NAT, it may be better to avoid public exposure entirely. A peer-to-peer reverse proxy with TLS termination at the private edge lets you keep the browser-friendly parts of HTTPS while moving the trust boundary closer to the protected resource.
That is the core idea: public access experience, private network exposure.


Top comments (0)