How to Verify a WACZ Web Archive Independently (Step-by-Step)

Somebody hands you a web archive file and says it proves what a page looked like. Maybe it arrived in discovery, maybe a source sent it to your newsroom, maybe it is your own capture and opposing counsel wants to check it. The question is always the same: can you verify this thing without trusting whoever made it?
If the file is a WACZ, the answer is yes, with free tools, in a few minutes, and offline for all but the last two steps. This guide shows how. It is written for the moderately technical reader, using standard free tools.
What a WACZ actually is
WACZ stands for Web Archive Collection Zipped, an open specification maintained by Webrecorder. In the spec’s words, it is “a media type that allows web archive collections to be packaged and shared on the web as a discrete file”: a ZIP that packages WARC data (the raw recorded web traffic) and metadata in a defined directory structure. The current spec, version 1.1.1, is stable and in active use.
Two design choices make WACZ verifiable rather than merely portable. First, the package must contain a manifest, datapackage.json, listing every file inside with its size and fixity, in practice a SHA-256 hash per file. Second, an optional companion spec, WACZ Signing and Verification (currently a working draft, version 0.1.0), adds datapackage-digest.json, “which contains a hash and signature of the datapackage.json.” Sign the manifest and you have sealed the whole package: any change to any file breaks its hash in the manifest, and any change to the manifest breaks the signature.
The format has serious institutional users. Webrecorder’s own Browsertrix crawler generates WACZ natively, and Scoop, the “high-fidelity, browser-based, web archiving capture engine” from Harvard’s Library Innovation Lab, outputs WACZ with what its README calls “accurate and complete provenance information.”
Step 1: Look at it (nothing to install)
Go to replayweb.page in a browser, press Choose File, and pick the WACZ. The page replays locally; as Webrecorder’s docs put it, “the archived item is loaded directly in the browser and is not uploaded anywhere.” You are now browsing the capture as it rendered, offline if you like.
This step verifies nothing cryptographic. It answers the prior question: is the content in this archive actually the page in dispute?
Step 2: Check the file hashes against the manifest
Unzip the WACZ (it is a plain ZIP; rename to .zip if your tool insists) and open datapackage.json. Each entry lists a path and a hash like sha256:8a7fc0d3.... Recompute any file’s hash and compare:
shasum -a 256 archive/data.warc.gz # macOS
sha256sum archive/data.warc.gz # Linux
If the computed value matches the manifest, that file is byte-for-byte what the manifest describes. Do this for every file. Webrecorder’s own Python package (pip install wacz) also validates a package with wacz validate myfile.wacz.
At this point you know the package is internally consistent. What you do not yet know is who sealed it and when.
Step 3: Verify the signature
A signed WACZ carries datapackage-digest.json, with the hash of the manifest and a signature over it. The signing spec supports two modes: a signature backed by a domain’s TLS certificate (verify the certificate chain and that the domain matches), or a bare public-key signature, where verification means, in the spec’s words, “validate the signature of the specified hash with the publicKey.”
For an Ed25519 key, OpenSSL 3 or later does this directly. On a Mac, install a current OpenSSL first with brew install openssl, because the openssl a Mac ships with is LibreSSL, which cannot run this check. In a signed WACZ, the hash and the base64-encoded signature both live inside datapackage-digest.json; extract each to its own file first. One practical detail: Ed25519 does not use OpenSSL’s streaming digest commands, so use pkeyutl with -rawin:
openssl pkeyutl -verify -rawin -pubin -inkey signer-public.pem \
-in datapackage-hash.txt -sigfile signature.bin
A valid signature ties the sealed package to the signer’s key. If the signer also anchored that signature in time, a timestamp you can independently check, you now have the full chain: these exact bytes existed, sealed by this key, no later than this moment.
Step 4: Check the independent copy
A verified package on your disk still has a single-point-of-failure problem: your disk. If the capture was also stored on Arweave, a decentralized permanent storage network, fetch the stored copy from a gateway by its transaction ID (the URL pattern is https://<gateway>/<transaction-id>, per the AR.IO gateway docs, whose gateways “verify that retrieved and cached data matches what was committed to Arweave” using Merkle-proof data roots). Hash what you download and compare it to the manifest from Step 2. Same hashes, same evidence, now confirmed to exist on a public permanent network that no single party, including the vendor, can alter.
Step 5: Check when it was sealed
A signature says who sealed the package. A timestamp says when, and the strongest kind does not depend on the signer’s clock. If the package carries an OpenTimestamps proof, a small .ots file covering a manifest, the free OpenTimestamps client checks it against the Bitcoin blockchain. In a Permavault package the proof is manifest.ots and covers manifest.json:
pip install opentimestamps-client
ots verify -f manifest.json manifest.ots
A valid result shows the manifest existed no later than the Bitcoin block the proof points to, a record no single party can backdate. This step needs a network connection, a full check may need a Bitcoin node (a pruned one works), and a freshly made proof can take a few hours to reach a confirmed block. If the package also carries an RFC 3161 timestamp (a .tsr file from a timestamp authority), openssl ts -verify checks it against that authority’s certificate chain.
What this adds up to
Run all five steps and you have verified, without trusting the capture vendor at any point: the content is the page in dispute; every file matches the sealed manifest; the manifest signature is valid; an unalterable public copy matches too; and the manifest existed by a date anchored in Bitcoin. That verification path is what makes an archive evidence-grade rather than just a saved file, and it is what a certification under FRE 902(13) and 902(14) can then describe on paper, a topic we cover in our plain-English guide to those rules.
It is also the link that holds the rest together: being able to check a capture without its vendor is what keeps a chain of custody for digital evidence intact for the years a matter can run. If you would rather check a capture than read about checking one, our verification page walks a real record through it in the browser.
How Permavault packages this
Every Permavault capture is built for this checklist, with one difference worth knowing: the signature sits in the evidence package’s own manifest.json, signed with Ed25519 and listing the hash of every file including the web archive, rather than inside the WACZ. The package carries the sealed web archive, that signed manifest, the signing public key, a Bitcoin timestamp proof, and the permanent Arweave copy on roughly 300 independent nodes funded by a long-term storage endowment, plus a verify script that runs steps 2 to 5 against the manifest with bash verify.sh. The hash and signature checks work offline with free tools; the Bitcoin check uses the OpenTimestamps client. You can test-drive the whole flow on the sample evidence package from our homepage, no account needed.
To show someone a regular capture without sending files, share its verification link. It opens the capture’s public record on our verification page, with its hashes, its permanent copy, and its Bitcoin anchor, and the evidence package stays the way to check every byte yourself.
Each capture is $4.99, down to $2.98 with a pack, and carries an independent Bitcoin-anchored timestamp. An optional $9 Certificate of Authenticity documents the capture process. The Legal certificate ($39) adds an RFC 3161 timestamp on the signed capture manifest, a chain-of-custody appendix, and a declaration template designed to support authentication under FRE 902(13) and 902(14). The Legal evidence package states whether the timestamp qualifies under eIDAS.
Do not take anyone’s word for what a capture proves. The whole point of the format is that you do not have to.
This article is general information, not legal advice. Specifications and tool behaviors reflect the cited project documentation as of July 2026; the WACZ signing specification is a working draft and may change.
Need a web page preserved exactly as it exists right now?
Capture it with Permavault