guest@6admin.io:~$

A reverse proxy in front of Perforce, or not: the pros and cons

Before showing how to put nginx in front of Perforce, which I did in the dedicated article , the real question has to be asked, the one usually skipped: whether to do it at all. Because p4d can terminate its own TLS and adding an intermediary is free nowhere. Four approaches and what they cost.

The common trap: the client IP

One point runs through everything else, so it may as well come first. As soon as an intermediary terminates or relays the connection, p4d risks seeing that intermediary’s address and not the client’s. This breaks IP-based logging, the server-side protections that rely on the address and the triggers that read it. Each of the options below is judged partly on this, so keep it in mind.

Option A: TLS termination by nginx at layer 4

nginx terminates TLS in a stream block and relays the TCP in clear to p4d.

The pro: the certificates live in the same place as Jenkins’ or the rest of the fleet’s, renewed by the same ACME tool, which is precisely the point when you already host other services behind the same nginx. You centralise the TLS policy, the ciphers, the versions, once and for all. The con: it is the option where p4d loses the client IP the most, since it only ever sees the local proxy. And nginx understands nothing of the P4 protocol, so it filters, logs and routes nothing at the application level, it just moves the TLS termination point.

Option B: p4d native TLS

p4d terminates its own TLS, with a P4PORT in ssl: and a P4SSLDIR, with no intermediary.

The pro: simplicity and p4d sees the real client IP, so logging, protections and triggers work as intended. Encryption is end to end, with no extra machine to maintain. The con: the certificates are managed separately, outside the centralised ACME machinery, often self-signed with a p4 trust to distribute to clients. If the whole rest of the fleet is on automated Let’s Encrypt, Perforce becomes the exception nobody remembers to renew.

Option C: SNI passthrough by nginx

nginx does not decrypt, it reads the SNI with ssl_preread and relays the TLS connection intact to p4d, which terminates it.

The pro: you keep end-to-end TLS and the certificate on p4d, while still going through a single entry point, useful for routing several TLS services on one address by the requested name. The con: it does not restore the client IP, which stays masked at the TCP level and it still does not pool the certificates since p4d carries them. You somewhat combine the drawbacks of the first two options without their benefits, unless SNI routing is the actual need.

Option D: the in-house tools, P4Broker and P4P

Perforce ships its own front ends, the P4Broker and P4P , which do understand the protocol.

The pro: the broker speaks P4, so it filters, redirects, logs and applies rules at the application level, which no generic proxy can do. The P4P is a cache for remote sites, which avoids re-transferring the same large binaries to every client. These are the right tools when the need is Perforce, not TLS. The con: they are not TLS terminators meant for pooling. If the sole goal is to serve the certificate in the same place as the rest, the broker is a heavy brick for a light task and you still have to handle TLS on top.

The call I make

If Perforce is the only service to expose, p4d’s native TLS is the most honest: nothing to insert, the client IP preserved, a p4 trust to distribute once. If Perforce lives next to a Jenkins and other services already behind nginx and the real value is having a single place for the certificates and the TLS policy, layer-4 termination holds up, provided you accept the loss of the client IP or recover it some other way. And if the need is Perforce itself, filtering, redirection, remote-site caching, then it is the broker and the in-house proxy to look at, not a generic reverse proxy. The how of the option chosen here is in nginx in front of Perforce .

[edit: 2026] The Ansible implementation of all this: the nginx role .