Back

Blog details

How IPFS Verifies Content Without Trusting the Gateway

AIOZ Network
7 min readAugust 29, 2026
aioz-pin
AIOZ Pin header image with the headline How IPFS Verifies Content You Receive

IPFS content verification means your client hashes every block it receives and compares the result with the CID it asked for. A match proves the bytes are right, no matter who sent them. The Trustless Gateway spec brings that same check to plain HTTP.

TL;DR:

  • A CID carries the hash of its block. Your client recomputes that hash on arrival and rejects the block if the two differ.
  • To check one file inside a big directory, you only need the blocks on the path from the root CID down to that file, plus the file's own blocks.
  • A normal gateway URL is a trusted request: the gateway decodes the file and your browser shows whatever comes back.
  • A trustless request asks for verifiable data instead, either one raw block (?format=raw) or a CAR stream (?format=car), and your client does the checking.
  • Verification proves the bytes match the CID. It does not prove the CID is the one you wanted, and it does nothing for availability or privacy.

Updated Oct 28, 2026: shorter answer-first opening, a verification diagram, a runnable hash check with its real output, the Trustless Gateway request formats from the spec, the verified-fetch library, and a closing AIOZ Pin section.

What Does Trustless Mean in IPFS?

In IPFS, "trustless" has a narrow meaning. Anyone can still send you bad data, but you can always tell, because the check runs on your side and only needs the CID you started with.

Compare that with a normal URL. When you download https://example.com/report.pdf, you trust the server, its TLS certificate, and every cache in between to hand you the right file. With a CID, the address itself describes the content, so the sender's reputation stops mattering. A random peer, a public gateway, and your own server are all equally acceptable sources once you can verify what they return.

How Does IPFS Verify a Block?

A CID has three parts: a version, a codec that says how to read the block, and a multihash. The multihash names the hash function, its length, and the digest. When a block arrives, your client runs the same hash function over the bytes and compares its digest with the one inside the CID.

You can see the whole idea in a few lines of standard-library Python. This script builds a CIDv1 for a raw block, the same way an IPFS node names a raw leaf, then checks the original bytes and a changed copy. We ran it on our machine. The CID it printed matches what the multiformats JavaScript library (9.9.0) produces for the same bytes.

import base64, hashlib

def cid_v1_raw(block: bytes) -> str:
    # CIDv1 = version 0x01, codec raw 0x55, multihash sha2-256 (0x12, length 0x20, digest)
    digest = hashlib.sha256(block).digest()
    raw = bytes([0x01, 0x55, 0x12, 0x20]) + digest
    return "b" + base64.b32encode(raw).decode().lower().rstrip("=")

block = b"hello from aioz pin\n"
cid = cid_v1_raw(block)
print("CID of the block:      ", cid)
print("same bytes verify:     ", cid_v1_raw(block) == cid)
print("one changed byte verify:", cid_v1_raw(b"hello from aioz pin!") == cid)
CID of the block:       bafkreiciuqus57wedvnzisfyydrze2ffnymhsgqafkwqx7kfh7cz72izbq
same bytes verify:      True
one changed byte verify: False

Changing one character gives a different digest, so the check fails. A real client does the same thing for every block, and it throws away any block that fails before the data reaches your app.

Diagram of trustless retrieval: a gateway or peer sends bytes, your client hashes them with sha2-256 and compares the result with the digest in the requested CID, then accepts a match or rejects a mismatch
The check runs on your side. The sender can be any peer or gateway.

How Verification Scales to a Whole File or Directory

Most files are more than one block. IPFS stores them as a Merkle DAG, where each parent block lists the CIDs of its children. Once you have verified a parent, you also trust every child CID it lists, so each block can be checked as it streams in.

The same rule keeps directory checks cheap. To verify /photos/2026/cat.jpg inside a pinned folder of ten thousand files, you need the root block, the directory blocks along that path, and the blocks of cat.jpg. The other files never have to be downloaded. Unlike a textbook Merkle proof, which only carries sibling hashes, IPFS needs the full parent blocks on that path, but those are usually small.

What Is a Trustless Gateway?

The Trustless Gateway spec defines a small subset of the normal HTTP gateway. Its own summary says it lets light clients retrieve data behind a CID and verify its integrity "without delegating any trust to the gateway itself". Every response type is verifiable, and the gateway never decodes UnixFS files for you.

You choose between two response types with a query parameter or an Accept header:

  • Raw block: ?format=raw or Accept: application/vnd.ipld.raw. Only /ipfs/{cid} works here, with no path after the CID.
  • CAR stream: ?format=car or Accept: application/vnd.ipld.car. Paths are allowed, and the CAR includes the blocks you need to verify each path segment.

CAR requests take a dag-scope parameter: block for just the final block, entity for everything needed to read one file or list one directory, and all, the default, for the whole DAG below the path. An entity-bytes=from:to parameter works like a verifiable HTTP range request. The two requests below follow the spec's format. We did not run them, so no output is shown.

curl -H "Accept: application/vnd.ipld.raw" "https://<gateway>/ipfs/<CID>" -o block.bin
curl "https://<gateway>/ipfs/<CID>/photos/cat.jpg?format=car&dag-scope=entity" -o cat.car

After the download, you hash block.bin exactly as the script above does. For the CAR file, a library walks the blocks, checks each one, and rebuilds the file. Our guide to CAR files explains that format in detail.

Trusted vs Trustless Requests in Practice

When you paste https://<gateway>/ipfs/<CID> into a browser, the gateway fetches the blocks, checks them on its side, rebuilds the file, and sends you ordinary bytes. Your browser can't tell whether those bytes match the CID. That is fine for a quick look and for most web pages, as long as you trust the gateway operator.

If you want the check on your side without writing CAR parsing yourself, the Helia project ships @helia/verified-fetch. Its README describes a fetch-like API where "the integrity of all bytes are verified by comparing hashes of the data". It first tries providers that expose a trustless gateway and falls back to recursive gateways such as trustless-gateway.link. This is the README's getting-started example, which we did not run:

import { verifiedFetch } from '@helia/verified-fetch'

const resp = await verifiedFetch('ipfs://bafy...')

Our post on Helia, the JavaScript IPFS implementation covers the rest of that toolkit.

What Verification Does Not Protect Against

A hash check answers one question: do these bytes match this CID? Three other questions stay open, and you should plan for each one.

  • Is it the right CID? If someone hands you a wrong CID, verification passes for the wrong file. Get CIDs from a source you trust, such as your own upload response or a smart contract.
  • Is it available? A host that serves nothing gives you nothing to check. Staying online is a pinning and routing job, covered in how IPFS finds and retrieves files.
  • Is the request private? The gateway or peer still sees which CID you asked for. Our post on privacy on IPFS goes through what that reveals.

How AIOZ Pin Handles It

AIOZ Pin's gateway docs describe the standard path format, https://gateway-url/ipfs/<CID>, with https://public.aiozpin.network/ipfs/{cid} as the public gateway and premium gateways for production traffic. Those are normal, trusted gateway requests. Our docs don't say whether AIOZ Pin's gateways answer ?format=raw or ?format=car requests.

You can still verify anything you serve from AIOZ Pin, because the CID comes from a source you trust: your own pin. The pin response and the pin details call both return the cid. Store it, then hash what your app downloads against it, or fetch through a verifying client. This is the documented call for one pin's details (not run here, no credentials on this machine):

curl --location --request GET 'https://api.aiozpin.network/api/pinning/<pinId>' \
  --header 'pinning_api_key: KEY' \
  --header 'pinning_secret_key: SECRET'

Already have a CID from somewhere else? You can pin it by CID without re-uploading. The full request and response shapes are in the AIOZ Pin pinning API docs.

Frequently Asked Questions

What does trustless mean in IPFS? It means your client checks each block by recomputing its hash and comparing it with the CID, so you don't have to trust the peer or gateway that sent it.

How does IPFS know a block was tampered with? The digest of the bytes you received won't match the digest inside the CID you requested, and the client rejects the block.

Do you need a whole directory to verify one file in it? No. You need the blocks on the path from the root CID to the file, plus the file's own blocks.

What is the difference between format=raw and format=car? format=raw returns one block for a bare CID. format=car returns a stream of blocks, allows a path, and lets you limit the scope with dag-scope.

Is a normal gateway URL verified? The gateway checks the blocks on its side, but your browser only receives the rebuilt file and can't check it against the CID.

Can I verify files served by AIOZ Pin? Yes. Keep the CID returned when you pin the file, then hash what you download against it or use a verifying client such as verified-fetch.

References

We only send updates when meaningful changes ship, and you can unsubscribe anytime

Related Content

blog thumbnail

IPFS Dedicated Gateway Free vs Paid Plans on AIOZ Pin

An IPFS dedicated gateway on AIOZ Pin is called a premium gateway. See what the free plan limits, what a paid plan adds, and what happens at zero balance.

6 min readOctober 28, 2026
blog thumbnail

Pin by CID on IPFS Without Re-Uploading Your Files

Pin by CID asks a pinning service to fetch content already on IPFS and keep it. Covers the AIOZ Pin pinByHash call, statuses, and why a pin stalls on Pinning.

6 min readOctober 26, 2026
blog thumbnail

IPFS Gateway Not Working? Diagnose It in This Order

IPFS gateway not working? Read the status code first, then check the CID, your request rate, and whether anyone is providing the content. Here is the order.

6 min readOctober 23, 2026
blog thumbnail

ERC-1155 Metadata on IPFS: The ID Substitution Rule

ERC-1155 metadata on IPFS breaks when filenames ignore the {id} rule. Here is the exact 64-character format, a working folder layout, and the per-token option.

6 min readOctober 21, 2026
blog thumbnail

AIOZ Pin Rate Limits: API, Public, and Premium Gateways

AIOZ Pin rate limits in one place: 180 requests per minute for the API, 15 per CID on the public gateway, and no predefined limit on premium gateways.

6 min readOctober 19, 2026
blog thumbnail

IPFS Pin Stuck on Pinning? What Each Pin Status Means

IPFS pin stuck on Pinning? Here is what Pinning, Pinned, and Failed mean on AIOZ Pin, why big directories can sit there, and how to read a pin's real state.

6 min readOctober 16, 2026