Transport Layer Security (TLS 1.3) shields HTTP payload confidentiality against eavesdropping ISPs and rogue transit nodes. Yet beneath encryption lies an intractable metadata vulnerability: the destination server always observes the client IP address.
In standard client-server exchanges, this revelation is treated as routine networking. However, for sensitive interactions—such as prompt submissions to generative AI models, telemetry crash reports, biometric verification queries, or location lookups—the source IP acts as a durable digital identifier. By aggregating request timestamps and client IP addresses, destination backends can assemble persistent user dossiers even when users never log into an account.
Standard proxying (such as classic HTTP forward proxies or SOCKS5 relays) simply moves the point of observation: the proxy operator now sees both the client IP address and the plain destination. To break this linkage without requiring mutually trusted intermediaries, the Internet Engineering Task Force (IETF) standardized RFC 9458 (Oblivious HTTP), accompanied by RFC 9180 (Hybrid Public Key Encryption) and RFC 9292 (Binary HTTP).
+--------------------------------------------------------------------------+
| RFC 9458 OBLIVIOUS HTTP (OHTTP) ARCHITECTURE |
+--------------------------------------------------------------------------+
| |
| +------------+ 1. Encrypted Request (HPKE) +------------+ |
| | | --------------------------------------> | | |
| | Client | <-------------------------------------- | Relay | |
| | (Browser / | 4. Encrypted Response (HPKE) | Server | |
| | AI Agent) | +------------+ |
| +------------+ | |
| | 2. Forward| 3. Send|
| | (Client IP visible to Relay ONLY) Encrypted | Encrypt|
| | (Payload encrypted with Target Key) Blob | Response
| | v Blob |
| | +------------+ |
| +----------------------------------------------> | Target | |
| (End-to-End Cryptographic Boundary) | Server | |
| Target decrypts payload via HPKE | (API / AI) | |
| Target sees RELAY IP, NOT Client IP +------------+ |
| |
+--------------------------------------------------------------------------+
The Cryptographic Foundation: HPKE and Binary HTTP Framing
RFC 9458 operates on a strict non-collusion principle between two distinct infrastructure entities: the Relay and the Target. The Relay forwards encapsulated blobs without ability to decrypt them; the Target processes decrypted payloads without ability to observe client network addresses.
This operational guarantee rests upon two foundational protocol components:
1. Binary HTTP Framing (RFC 9292)
Traditional HTTP requests consist of textual headers and arbitrary bodies formatted for TCP or QUIC streams. In OHTTP, the entire request—method, path, headers, and payload—is encoded into a deterministic, compact binary byte array conforming to RFC 9292 (BHTTP):
+--------------------+--------------------------------+--------------------+
| Framing / Control | Known Headers (Indexed) | Content Body |
| (GET /api/v1/query)| (Host: target.example.com ...) | (Binary payload) |
+--------------------+--------------------------------+--------------------+
Encoding the transaction into Binary HTTP eliminates transport-level framing ambiguities before cryptographic operations commence.
2. Hybrid Public Key Encryption (HPKE, RFC 9180)
To protect the Binary HTTP request against the Relay, the client encrypts the message using the Target public key configuration. The Target distributes an OHTTP Key Configuration containing:
key_id: A 1-byte identifier distinguishing active key rotations.kem_id: The Key Encapsulation Mechanism (such as0x0020for DHKEM over Curve25519).kdf_id: The Key Derivation Function (such as0x0001for HKDF-SHA256).aead_id: The Authenticated Encryption with Associated Data cipher (such as0x0001for AES-128-GCM or0x0003for ChaCha20-Poly1305).public_key: The raw public key bytes of the Target.
Using HPKE in Single-Shot Base Mode, the client creates an ephemeral key pair $(sk_E, pk_E)$ on the selected curve. It derives a shared secret with the Target public key $pk_T$ and encapsulates the secret into an encapsulated key enc:
$$ ext{SharedSecret} = ext{DH}(sk_E, pk_T)$$
Using HKDF, the client derives an AEAD encryption key $K_{ ext{req}}$ and nonce $N_{ ext{req}}$:
$$(K_{ ext{req}}, N_{ ext{req}}) = ext{KeyDerivation}( ext{SharedSecret}, ext{info})$$
The client seals the Binary HTTP request byte array into ciphertext $C_{ ext{req}}$:
$$C_{ ext{req}} = ext{Seal}(K_{ ext{req}}, N_{ ext{req}}, ext{aad}, ext{BinaryHTTPRequest})$$
The encapsulated request message transmitted to the Relay carries the MIME type message/ohttp-req, containing:
[KeyID (1 byte)] || [KEM ID (2 bytes)] || [KDF ID (2 bytes)] || [AEAD ID (2 bytes)] || [enc (32 bytes)] || [Ciphertext]
Because only the Target holds the private key matching $pk_T$, the Relay cannot inspect or tamper with the payload without triggering an AEAD authentication failure.
+--------------------------------------------------------------------------+
| WHAT EACH ENTITY OBSERVES IN RFC 9458 |
+--------------------------------------------------------------------------+
| Entity | Observes Client IP? | Observes Request Data?| Reads Response? |
+-------------+---------------------+-----------------------+-----------------+
| ISP / Wire | Yes | NO (Encrypted TLS) | NO |
| Relay Host | Yes (or VPN IP) | NO (HPKE Ciphertext) | NO (Ciphertext) |
| Target Host | NO (Relay IP only) | YES (Decrypted BHTTP) | YES (Plaintext) |
+--------------------------------------------------------------------------+
The Oblivious HTTP Transaction Lifecycle
The full request-response cycle executes across five deterministic steps:
- Key Fetching: The client obtains the Target current OHTTP key configuration out-of-band (via DNS HTTPS records, authenticated key directories, or bootstrapping endpoints).
- Client Encapsulation: The client encodes its HTTP request to BHTTP format, generates an ephemeral HPKE key pair, encrypts the request, and yields a
message/ohttp-reqblob. - Relay Forwarding: The client transmits the blob to the Relay via standard HTTPS
POST /relay. The Relay terminates the incoming TLS connection, strips the client IP headers, and re-transmits the encapsulated payload to the Target via a fresh HTTPPOSTconnection. - Target Decapsulation and Execution: The Target matches
key_idto its private key, decapsulatesencto reconstruct the shared secret, decrypts the ciphertext, and unpacks the Binary HTTP request. The Target executes the business logic (e.g. AI inference prompt processing) and encodes the result into a Binary HTTP response. - Response Encapsulation: The Target derives a separate response key $K_{ ext{res}}$ from the established HPKE context, seals the Binary HTTP response into a
message/ohttp-respayload, and returns it to the Relay. The Relay passes the blob back to the client, where the client decrypts it using its saved context.
Synergies with RiceGuard and Layer 3 VPN Tunnels
While Oblivious HTTP functions as an application-layer (L7) cryptographic construct, coupling OHTTP with Layer 3 VPN tunneling (such as the RiceGuard datapath) establishes multi-tier defense-in-depth:
- Elimination of Relay Profile Correlation: When routing through RiceGuard, the connection from the client to the Relay originates from an aggregated, shared VPN egress node rather than a residential IP. Even if a Relay operator were compromised, it cannot correlate connections back to physical households or cellular subscribers.
- DNS Leak Prevention: Key discovery and relay domain lookups are resolved strictly inside the isolated kernel network namespace of RiceGuard, thwarting local network adversaries from inferring when OHTTP sessions start.
- Protection for Generative AI and Local Inference Workflows: Autonomous AI agents making tool-calling requests and cloud inference lookups can route their external egress through OHTTP endpoints over RiceGuard. The AI vendor evaluates prompt tokens without retaining persistent IP tracking vectors.
Practical Implementation: Minimal OHTTP Client in Go
Below is a working implementation illustrating how a client serializes a request into Binary HTTP, encapsulates it using HPKE, and prepares the payload for transmission to an RFC 9458 Relay:
package main
import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"io"
"net/http"
"github.com/cloudflare/circl/hpke"
)
// OHTTPEncapsulator handles request encapsulation for RFC 9458.
type OHTTPEncapsulator struct {
suite hpke.Suite
targetPK hpke.PublicKey
keyID uint8
}
func NewOHTTPEncapsulator(keyID uint8, kemID hpke.KEM, kdfID hpke.KDF, aeadID hpke.AEAD, rawPubKey []byte) (*OHTTPEncapsulator, error) {
suite := hpke.NewSuite(kemID, kdfID, aeadID)
pk, err := suite.KEM().Scheme().UnmarshalBinaryPublicKey(rawPubKey)
if err != nil {
return nil, fmt.Errorf("failed to parse target public key: %w", err)
}
return &OHTTPEncapsulator{
suite: suite,
targetPK: pk,
keyID: keyID,
}, nil
}
// EncapsulateRequest transforms plaintext Binary HTTP data into an RFC 9458 message/ohttp-req blob.
func (oe *OHTTPEncapsulator) EncapsulateRequest(bhttpReq []byte) ([]byte, hpke.Context, error) {
sender, err := oe.suite.NewSender(oe.targetPK, []byte("message/bhttp request"))
if err != nil {
return nil, nil, fmt.Errorf("failed creating HPKE sender: %w", err)
}
enc, ctx, err := sender.Setup(rand.Reader)
if err != nil {
return nil, nil, fmt.Errorf("HPKE setup failed: %w", err)
}
// Encrypt the binary HTTP message with context
ciphertext, err := ctx.Seal(nil, bhttpReq)
if err != nil {
return nil, nil, fmt.Errorf("HPKE seal failed: %w", err)
}
// Wire layout: [KeyID (1)] [KEM (2)] [KDF (2)] [AEAD (2)] [enc (variable)] [ciphertext]
var buf bytes.Buffer
buf.WriteByte(oe.keyID)
buf.Write([]byte{0x00, 0x20}) // DHKEM(X25519, HKDF-SHA256)
buf.Write([]byte{0x00, 0x01}) // HKDF-SHA256
buf.Write([]byte{0x00, 0x01}) // AES-128-GCM
buf.Write(enc)
buf.Write(ciphertext)
return buf.Bytes(), ctx, nil
}
// SendToRelay delivers encapsulated payload to the intermediary relay node.
func SendToRelay(relayURL string, targetURL string, ohttpPayload []byte) ([]byte, error) {
req, err := http.NewRequest("POST", relayURL, bytes.NewReader(ohttpPayload))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "message/ohttp-req")
req.Header.Set("Target", targetURL)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("relay request failure: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("relay rejected transaction: HTTP %d", resp.StatusCode)
}
return io.ReadAll(resp.Body)
}
Architectural Trade-offs and Deployment Considerations
Deploying RFC 9458 at scale requires engineering trade-offs across three key dimensions:
- Latency Overhead: Because transactions transit two physical server hops (Client $ o$ Relay $ o$ Target) plus cryptographic encapsulation, round-trip latency increases by roughly 20–45 ms depending on physical proximity. For interactive web browsing, full OHTTP tunneling can introduce lag, making it ideal for discrete telemetry, AI inference, and API calls.
- Cache Invalidation: Traditional CDN caching at edge servers relies on inspecting request URLs. Because OHTTP payloads are opaque ciphertexts, intermediate caches cannot cache responses unless the Relay coordinates application-specific cache tags.
- Relay-Target Operational Independence: The architectural guarantee collapses if the Relay and Target belong to the same business entity or share log pipelines. Production deployments require clear jurisdictional separation (e.g. Apple routing through Cloudflare/Fastly relays).
RFC 9458 Oblivious HTTP demonstrates that privacy on the modern Internet does not require abandoning HTTP or relying on centralized trust brokers. By enforcing architectural separation through mathematics, OHTTP ensures that identity and data remain permanently decoupled.



