A successful direct request does not settle the case
An engineer calls the target server with a regular HTTP client and receives a successful response. The same application request sent through a privacy proxy times out, returns an empty response, or fails. The relay, gateway, and origin teams inspect their own logs and all give the same honest answer: everything looks normal here.
The cause is not necessarily poor monitoring. Oblivious HTTP, or OHTTP, deliberately distributes transport knowledge among participants. The relay sees the source of the connection presented to it but forwards encrypted content. The gateway decrypts the request and contacts the origin without directly receiving the original client address through that connection.
This separation depends on the participants not combining their observations. It also does not remove identifiers that the application placed inside the request. Precise timing and message sizes may enable correlation. During an incident, the useful question is therefore not who is at fault. It is which stage was last confirmed with a safe artifact.
Reconstruct the route first
Obtaining a key configuration is a prerequisite for the protected exchange, but it is not necessarily a separate step for every request. A client may retrieve it in advance or use a previously obtained configuration that is valid for the test.
A typical test proceeds as follows:
- The client retrieves or loads the gateway public key configuration.
- It represents the original HTTP message as binary HTTP, or bHTTP.
- It encrypts and encapsulates the message for the gateway.
- It sends an outer HTTP request carrying the protected message to the relay.
- The relay forwards that message to the gateway.
- The gateway decrypts it and constructs a request to the origin.
- The protected response returns through the relay and is decrypted by the client.
Two results must remain distinct. The outer HTTP result belongs to the transport wrapper that carries the OHTTP message. The inner status belongs to the origin response inside the protected exchange. It is available only where that response can be decrypted or safely observed.
A minimal boundary record may include the time, duration, local operation result, next hop, message size, and key configuration identifier. This does not mean that all raw records should be collected in one place. Even metadata needs retention limits, access controls, and an approved correlation method.
The logging and escalation rules below are an operational approach, not protocol requirements. They must be adapted to the deployment’s threat model and privacy policy.
Branch 1: the relay sees no attempt
Start with the client. Confirm that the required gateway configuration is available, that the client supports its parameters, and that local bHTTP construction and protected encapsulation completed without an error.
Then inspect the outer request. Compare the relay address, method, headers, and content type with the configuration for your deployment. Check DNS, TLS, intermediate proxies, and environmental network policy separately. Do not assume that these values are universal. Obtain exact settings from the documentation for the deployment being tested.
The first escalation usually needs only the client version, a narrow time window, the expected address, and the network result category. If no connection to the relay was established, involving the origin team is premature. Do not share the complete encrypted container without a specific need and a risk review.
Branch 2: the relay accepted the request, but the gateway did not complete it
An inbound relay record narrows the search. Use available telemetry to confirm whether the relay attempted to forward the message to the expected gateway and how that attempt ended. The relay does not need to decrypt the content to record the time, next hop, duration, and network result.
If the gateway has no corresponding attempt, investigate routing between the services, TLS, and any deployment-specific access configuration. If the gateway received the message but could not open it, compare the key configuration identifier, supported parameters, and a safe error category.
Do not classify every cryptographic rejection as a network fault. Repeated retries without checking the key configuration may only create more noise. For escalation, show the relay’s last confirmed operation and the gateway’s first failed operation without attaching plaintext request content.
Branch 3: the gateway decrypted the request, but the origin behaves differently
After successful decryption, compare the gateway’s request with the direct test. A call made with curl may use a different path, method, Host header, content type, body, or service credential. The statement that a direct request works is useful only when the known differences are listed.
Confirm that the gateway constructed the expected request, established a connection to the origin, and received an inner HTTP status. At the origin, use a narrow time window or an approved scoped identifier. Do not expect to find the original client address there. Through this route, the origin normally sees a connection from the gateway.
OHTTP does not hide information contained in the decrypted application request. The origin may see an account identifier, token, or other fields if the client sent them. Transport privacy must therefore not be described as complete anonymity.
If the origin responded but the client received no readable response, follow the return path. Check response protection at the gateway, forwarding through the relay, and client-side decryption. A successful outer HTTP exchange does not prove that the inner request succeeded. An origin response does not prove that the client could open it.
A controlled laboratory session with pvcli
Cloudflare published the source code for pvcli, a curl-like tool for end-to-end work with OHTTP and other privacy protocols. According to Cloudflare’s description, it removes the need to reproduce binary HTTP, encryption, and the multi-step exchange manually.
Begin in a controlled test environment. Record the tool version, test endpoint addresses, key configuration identifier, start time, and available outer and inner results. Preserve the exact command only after removing tokens and other secrets. Obtain syntax and options from the documentation for the version you actually run.
A useful laboratory method is to run a known-good baseline through the same relay, gateway, and origin. Then change only one factor: the target path, test data, key configuration, or network route. Replacing the client, address, key, and data together may reproduce the failure, but it will not explain it.
Do not turn temporary diagnostics into permanent excessive logging. Never record private keys, tokens, complete application payloads, or a mapping between the client address and decrypted request. Use a short boundary summary for the shared report instead of centralizing copies of every team’s raw logs.
Avoid other shortcuts as well. Repeated retries may hide a timing-dependent symptom. A successful direct call can create false confidence. Requesting complete logs from every team increases the amount of sensitive data without necessarily identifying the failed transition.
Instead, record a content-free sequence: configuration available, relay accepted the message, gateway received it, decryption succeeded or failed, and origin responded or did not respond. This record reveals the gap between the last confirmed success and the first confirmed failure. That gap determines the next check and the responsible team.
Sources
- Cloudflare, We’re open-sourcing our privacy proxy CLI: https://blog.cloudflare.com/open-sourcing-our-privacy-proxy-cli/
Quick checklist
- reproduce direct and OHTTP requests with the same application data
- verify retrieval and identification of the gateway key configuration
- record status, duration, and data size at every available boundary
- correlate relay, gateway, and origin records with a safe identifier
- prepare a minimal reproducible case without secrets or personal data
- escalate to the team responsible for the gap between the last successful and first failed boundary
Build an OHTTP failure investigation plan
Help me create a safe investigation plan for a failed OHTTP request. Inputs: - symptom and failure time; - environment and client version; - relay, gateway, and origin addresses without secrets; - result of a direct request to the origin; - outer HTTP status or timeout description; - gateway key configuration identifier; - available logs, metrics, and request identifiers; - data that must not be logged or shared. Do not request private keys, tokens, full payloads, or personal data. Do not assume that a successful direct request exactly reproduces the request sent by the gateway. Output format: 1. A short hypothesis about the nearest failure point. 2. Ordered checks from the client to the origin. 3. For every check: required artifact, expected result, and next action. 4. The minimum safe evidence needed to escalate to the responsible team. 5. A separate list of data that must not be included in the report.