OpenAI-Proxy-PHP: The tiny PHP gatekeeper that keeps OpenAI keys off the client
A single-file middleware layer signs requests, freezes the model and prompt server-side, and turns image uploads into temporary URLs. It is less a proxy than a programmable security fence for AI apps.
- OpenAI-Proxy-PHP turns a tiny PHP script into a trust boundary, so the client can send intent without ever seeing the key, the prompt, or the model choice.
- The shared-secret HMAC check is the real gate, because tampered requests die before any OpenAI call happens.
- Its image pipeline is clever because it converts base64 uploads into temporary public URLs, and brittle because that creates cleanup and exposure concerns.
- The repo is elegant because it is tiny and dependency-free, but that same simplicity makes it best for narrow gateways, not sprawling backend policy.
The dangerous part of AI apps is the key, not the model
If an app ships with an OpenAI API key inside it, the key is already public the moment someone inspects the binary or watches traffic closely enough. The model can be swapped later. The leaked key can be abused immediately.
OpenAI-Proxy-PHP attacks that problem with a boring toolchain and a sharp boundary. The client talks to PHP. PHP decides whether the request is authentic, what prompt it gets, and which model it can reach.
A single file becomes a trust boundary
The whole repo lives in one file, openai_proxy.php. No framework, no build step, no package graph. That is not just minimalism. It is a way to make every policy decision easy to audit.
Once you read it that way, the code stops looking like a pass-through and starts looking like policy. The proxy is where trust is granted, narrowed, or denied. Everything else is just transport.
How the shared-secret check actually blocks abuse
The client sends the messages plus a hash. The server recomputes the expected hash with a shared secret, then compares them with hash_equals. If the values do not match, the request dies before it can spend tokens.
That tiny move matters. hash_equals closes the timing leak that a naive string compare can open, so the proxy is not just checking identity. It is checking it in constant time.
$expected_hash = hash_hmac('sha256', $_POST['messages'], $shared_secret_key);
if (!hash_equals($expected_hash, $_POST['hash'])) {
http_response_code(403);
exit('Invalid hash');
}
The server owns the prompt, the model, and the temperature
This is where the repo becomes opinionated. The client can supply messages, but the server injects the system prompt, hardcodes the model, and controls sampling settings. The proxy is not neutral middleware. It is policy enforcement.
That matters when you are shipping a frontend or mobile app. Once prompt and model move server-side, you can change behavior without shipping a new client, and users cannot quietly steer the model around your guardrails.
The image trick is the most interesting part
The repo’s strangest move is also its most pragmatic. Base64 image data is validated, written to /tmp, then exposed as a temporary URL that OpenAI can fetch as if it were a normal hosted asset.
That lets a lightweight PHP script support multimodal requests without building a media pipeline. It also creates new responsibilities: cleanup, public URL exposure, filesystem permissions, and the possibility that temporary files outlive their welcome.
Why this is elegant, and where it hurts
The elegance is obvious once you compare it with a fuller backend. There is very little to deploy, very little to update, and very little surface area to audit. For a narrow AI gateway, that is a feature.
The trade-offs are just as clear. Requests are synchronous, temp files need lifecycle management, the server needs write access, and the public file path is part of the security story whether you want it to be or not.
| Criterion | Direct client key | OpenAI-Proxy-PHP | Full backend or gateway |
|---|---|---|---|
| Secret exposure | The key ships with the app. | The key stays on the server. | The key stays on the server. |
| Prompt control | The client can alter behavior. | The server fixes the prompt and model. | The server can fix prompt, policy, and routing. |
| Image handling | Awkward or unsafe. | Base64 becomes a temporary URL. | Usually handled by storage, queues, or a media service. |
| Complexity | Low code, high risk. | Low code, moderate ops. | Higher code, higher ops. |
| Operational burden | Almost none until the key leaks. | Cleanup and file permissions matter. | More infrastructure, more moving parts. |
| Best fit | Never for secrets. | Small frontends and mobile apps that need a narrow gate. | Teams that need broader policy, logging, and scale. |
What this repo is really for
This is for builders who want the smallest possible server shim between a client and OpenAI. Not a platform. Not a framework. A controlled doorway.
If your problem is, "I need the app to call AI without shipping the key," this repo is enough to start. If your problem is, "I need full governance, observability, retries, and queues," it is the wrong tool on purpose.