ar.io Logoar.io Documentation

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:

HeaderValuesDescription
X-AR-IO-Verifiedtrue / falsetrue 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-Stabletrue / falsetrue 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-Trustedtrue / falsetrue when the data was retrieved from a source the operator has configured as trusted (via TRUSTED_GATEWAY_URL or TRUSTED_GATEWAYS_URLS).
Content-Digestsha-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-Digestbase64url stringThe 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-Idbase64url stringThe Arweave transaction ID or data item ID of the content being served.
X-AR-IO-HopsnumberThe number of inter-gateway hops the request traversed before reaching this gateway.
X-CacheHIT / MISSWhether 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:

HeaderDescription
X-AR-IO-Root-Transaction-IdThe root Arweave transaction containing the bundle
X-AR-IO-Data-Item-OffsetByte offset of this data item within the bundle
X-AR-IO-Data-Item-SizeSize of the data item in bytes
X-AR-IO-Data-Item-Data-OffsetByte offset of the data payload within the data item
X-AR-IO-Root-Item-OffsetOffset of the root item in the bundle
X-AR-IO-Root-Item-SizeSize 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:

HeaderDescription
X-Arweave-OwnerThe raw owner public key of the data item
X-Arweave-Owner-AddressThe 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-TruncatedSet 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=true

Signing 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

VariableDefaultDescription
HTTPSIG_ENABLEDfalseEnable RFC 9421 response signing
HTTPSIG_KEY_FILEdata/keys/httpsig.pemPath to standalone Ed25519 private key (auto-generated if missing). Ignored when OBSERVER_KEYPAIR_PATH or OBSERVER_PRIVATE_KEY is set
HTTPSIG_BIND_REQUESTtrueInclude 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:

  1. Signature verification — The public key is embedded in the Signature-Input header's keyid parameter and is verifiable via the Web Crypto API in modern browsers.
  2. 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.
  3. Body integrity — Compare the Content-Digest header against a locally computed hash, or walk the Arweave signature chain from the signed X-AR-IO-Data-Id.

How is this guide?