Verification & Trust Headers
Ar.io gateways attach HTTP response headers to every data response that communicate whether the data has been verified against the Arweave base layer, whether it has reached finality, and whether the source is trusted. These headers enable clients to make informed trust decisions without relying solely on the gateway operator's reputation.
Trust & Verification Headers
Every data response from an ar.io gateway includes the following headers:
| Header | Values | Description |
|---|---|---|
X-AR-IO-Verified | true / false | true only when data is served from local cache and has been verified against the Arweave base layer. Data streamed from network sources is marked false even if the gateway's database indicates prior verification, because the hash cannot be confirmed in-flight during streaming. |
X-AR-IO-Stable | true / false | true when the data exists beyond Arweave's maximum fork depth, indicating finality. Once stable, the data cannot be reorganized out of the chain. |
X-AR-IO-Trusted | true / false | true when the data was retrieved from a source the operator has configured as trusted (via TRUSTED_GATEWAY_URL or TRUSTED_GATEWAYS_URLS). |
Content-Digest | sha-256=:base64: | SHA-256 hash of the response body in RFC 9530 format. Only set on cache hits and HEAD requests, since the hash cannot be computed during network streaming. Enables direct body integrity verification by clients. |
X-AR-IO-Digest | base64url string | The raw SHA-256 hash of the content in base64url encoding. Set alongside Content-Digest. |
ETag | "hash" | Content hash for HTTP conditional requests (If-None-Match). Enables 304 Not Modified responses for efficient caching. |
X-AR-IO-Data-Id | base64url string | The Arweave transaction ID or data item ID of the content being served. |
X-AR-IO-Hops | number | The number of inter-gateway hops the request traversed before reaching this gateway. |
X-Cache | HIT / MISS | Whether the data was served from the gateway's local cache or fetched from the network. |
Why X-AR-IO-Verified Can Be False for Verified Data
A common source of confusion: data may be verified in the gateway's database but still return X-AR-IO-Verified: false. This happens when the data is streamed from a network source rather than served from local cache. During streaming, the gateway cannot compute a hash of the data in-flight to confirm it matches the verified hash. Only when the data is served from local cache can the gateway guarantee that the bytes being sent match what was previously verified.
# Cache hit - verified data served from local storage
X-AR-IO-Verified: true
X-Cache: HIT
Content-Digest: sha-256=:4ROTs2lTPAKbr8Y41WrjHu+2q+7S+m+yTuO7fAUzZI4=:
# Network fetch - same data, but hash can't be confirmed in-flight
X-AR-IO-Verified: false
X-Cache: MISS
# Content-Digest is NOT set (can't hash during streaming)ANS-104 Bundle Headers
For data items served from ANS-104 bundles, gateways include additional headers in two categories.
Position Headers
These headers describe the data item's location within its parent bundle, allowing clients to independently verify inclusion:
| Header | Description |
|---|---|
X-AR-IO-Root-Transaction-Id | The root Arweave transaction containing the bundle |
X-AR-IO-Data-Item-Offset | Byte offset of this data item within the bundle |
X-AR-IO-Data-Item-Size | Size of the data item in bytes |
X-AR-IO-Data-Item-Data-Offset | Byte offset of the data payload within the data item |
X-AR-IO-Root-Item-Offset | Offset of the root item in the bundle |
X-AR-IO-Root-Item-Size | Size of the root item |
Metadata Headers
ANS-104 fields are exposed at the HTTP layer so clients can read data item tags and owner information directly from response headers without parsing the binary format:
| Header | Description |
|---|---|
X-Arweave-Owner | The raw owner public key of the data item |
X-Arweave-Owner-Address | The data item signer's wallet address (derived from the owner key) |
X-Arweave-Tag-* | One header per tag, with the tag name as the suffix (e.g., X-Arweave-Tag-Content-Type) |
X-Arweave-Tags-Truncated | Set when the tag set exceeds a configurable byte budget, indicating partial tag exposure |
Client-Side Verification
Clients can verify gateway responses at multiple levels, from lightweight header checks to full cryptographic verification:
Body Integrity
Compare the Content-Digest header (when present) against a locally computed SHA-256 hash of the response body. For Arweave content, the X-AR-IO-Data-Id is itself a content hash - clients can walk the Arweave or ANS-104 signature chain from this ID to verify the content.
// Verify Content-Digest in a browser
const response = await fetch('https://gateway.example/raw/TX_ID');
const body = await response.arrayBuffer();
const hash = await crypto.subtle.digest('SHA-256', body);
const base64Hash = btoa(String.fromCharCode(...new Uint8Array(hash)));
const digestHeader = response.headers.get('Content-Digest');
// Header format: sha-256=:base64hash=:
// Extract the hash between the colons
const match = digestHeader?.match(/sha-256=:(.+?):/);
const verified = match?.[1] === base64Hash;
console.log('Integrity check:', verified);Cross-Gateway Comparison
Query the same data from multiple gateways and compare their response headers. If X-AR-IO-Verified: true and Content-Digest values match across independent gateways, the data is authentic with high confidence.
Wayfinder Verification
The Wayfinder protocol provides automated client-side routing and verification across the gateway network. It supports configurable strategies including balanced (random), fastest ping, and static gateway selection, with built-in response verification.
HTTP Message Signatures (RFC 9421)
Gateways can opt in to signing response headers using RFC 9421 HTTP Message Signatures. When enabled, an Ed25519 sub-key signs all trust-relevant headers at response time, producing a Signature and Signature-Input header pair. This provides cryptographic proof that a specific gateway operator produced a given response.
Enabling HTTP Signatures
Add the following to your gateway's .env file:
# Enable response signing
HTTPSIG_ENABLED=trueSigning Key Selection
When OBSERVER_KEYPAIR_PATH or OBSERVER_PRIVATE_KEY is set, the gateway uses the observer's Solana keypair directly as the HTTPSIG signing key. Verifiers derive the Solana address from the public key in the keyId and look it up in the on-chain Gateway Registry — no separate attestation is needed.
When neither observer env is set, the gateway auto-generates a standalone Ed25519 key at data/keys/httpsig.pem. Responses are still signed, but the signer can't be tied back to the on-chain registry.
If you use a single Solana key for both operator and observer (Pattern 1 in the migration guide), HTTPSIG falls back to the auto-generated key because the gateway only piggybacks on OBSERVER_KEYPAIR_PATH/OBSERVER_PRIVATE_KEY when they're explicitly set. To get on-chain-verifiable HTTPSIG with a single key, explicitly set OBSERVER_KEYPAIR_PATH (or OBSERVER_PRIVATE_KEY) to the same value as your operator key.
Configuration Reference
| Variable | Default | Description |
|---|---|---|
HTTPSIG_ENABLED | false | Enable RFC 9421 response signing |
HTTPSIG_KEY_FILE | data/keys/httpsig.pem | Path to standalone Ed25519 private key (auto-generated if missing). Ignored when OBSERVER_KEYPAIR_PATH or OBSERVER_PRIVATE_KEY is set |
HTTPSIG_BIND_REQUEST | true | Include request method and path in signature (prevents replay) |
OBSERVER_KEYPAIR_PATH | - | Path to a 64-byte Solana keypair JSON. When set, used as the HTTPSIG signing key — verifiable against the on-chain GAR |
OBSERVER_PRIVATE_KEY | - | Alternative: base58-encoded 64-byte secret. Mutually exclusive with the file form |
Verifying It's Working
Once enabled, check the /ar-io/info endpoint to confirm:
curl -s https://your-gateway.example/ar-io/info | jq '.httpsig'The response includes the public key, key ID, Solana address, and attestation details (if configured). Signed responses will include Signature and Signature-Input headers on all trust-relevant data responses.
What Gets Signed
The gateway signs all trust-relevant response headers but not the response body. Body integrity is achieved through the signed X-AR-IO-Data-Id (which is a content hash) and the Content-Digest header. Header-only signing preserves streaming performance for large responses.
Signed headers (when present on the response):
- Trust headers:
X-AR-IO-Data-Id,X-AR-IO-Verified,X-AR-IO-Stable,X-AR-IO-Trusted - Bundle headers:
X-AR-IO-Root-Transaction-Id,X-Arweave-Owner-Address,X-Arweave-Tags-Truncated - ArNS headers:
X-ArNS-Name,X-ArNS-Resolved-Id,X-ArNS-TTL-Seconds,X-ArNS-Process-Id - Chunk headers:
X-Arweave-Chunk-Data-Root,X-Arweave-Chunk-Tx-Id,X-AR-IO-Chunk-Source-Type - Content headers:
Content-Type,Content-Digest - All
X-Arweave-Tag-*headers (dynamically matched) - Request components (when
HTTPSIG_BIND_REQUEST=true):@method,@path,@status
Only responses containing at least one trust-relevant header are signed. Non-data responses (health checks, info endpoint, errors) are not signed.
Verification Chain
Clients can verify signed responses through the following chain:
- Signature verification — The public key is embedded in the
Signature-Inputheader'skeyidparameter and is verifiable via the Web Crypto API in modern browsers. - Identity verification — When the observer Solana key is used for signing, derive the Solana address from the public key and look it up in the on-chain Gateway Registry (GAR). A match confirms the signer is a registered gateway operator.
- Body integrity — Compare the
Content-Digestheader against a locally computed hash, or walk the Arweave signature chain from the signedX-AR-IO-Data-Id.
Related
Data Verification
How gateways verify data integrity using Merkle tree cryptography
Wayfinder
Client-side routing and verification across the gateway network
Gateway API Reference
Full API reference including data retrieval endpoints
Environment Variables
Configure trusted sources, verification, and gateway behavior
How is this guide?