On this page
01 What content integrity means on the wire
Content integrity is the property that a response body reaches the receiver exactly as the sender produced it. It is a narrower claim than authenticity (who produced it) and a different claim from confidentiality (who could read it). A network can break integrity by accident (a truncated transfer, a proxy that recompresses, an optimiser that rewrites a script) or on purpose (a compromised cache, an injected advert). The receiver's only defence is a value, computed by the publisher, that the receiver can recompute and compare.
That value is a cryptographic hash, and every mechanism on this page in the network-layer provenance section is a different way of moving the publisher's hash to the receiver: inside the page (Subresource Integrity), inside the response headers (RFC 9530 digest fields), beside the file (checksums), or under a signature (RFC 9421). The differences are what each covers, who performs the check and what each misses.
02 What TLS guarantees and where it stops
TLS 1.3 (RFC 8446) provides integrity for every record it carries: the AEAD cipher authenticates each record, and a modified record fails decryption. Within one connection, between one client and one server, the body cannot be altered without detection. The limit is the word connection. A CDN terminates the client's TLS session at its edge, holds the response in a cache, and opens a separate session to the origin. The client's integrity guarantee ends at the edge; the origin's ends at the same edge from the other side. Whatever happens inside that box, caching, compression, image optimisation, script injection, is invisible to both.
The same break happens at any layer 7 balancer, corporate proxy or interception appliance. The mutual TLS between edge and origin page shows how to authenticate the hop behind the CDN; it does not extend the client's integrity check across it. End-to-end integrity needs something the intermediary cannot alter without detection, which means a value the client can check independently of TLS.
03 Subresource Integrity for scripts and styles
Subresource Integrity (SRI) is the W3C mechanism for the case that hurts most: a page on your domain loading a script or stylesheet from somewhere else. The author computes a hash of the exact file, encodes it in base64, and places it in an integrity attribute on the script or link element as sha384- followed by the digest. The browser fetches the resource, hashes the bytes it received, and refuses to execute or apply them on a mismatch. Conformant browsers must support SHA-256, SHA-384 and SHA-512; when several are listed the strongest is used.
Two details decide whether it works. The resource must be served with CORS headers and the element must carry crossorigin="anonymous", otherwise the browser cannot read the bytes to hash them for a cross-origin fetch. And the hash binds to one exact byte sequence, so any change to the file, a new build, a different minifier, a CDN that rewrites, produces a mismatch and a blocked resource. That is the intended behaviour. The specification also defines an Integrity-Policy header that lets a site require integrity metadata on every script it loads.
$ openssl dgst -sha384 -binary vendor.min.js | openssl base64 -A
H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO
$ cat snippet.html
<script src="https://static.example.net/vendor.min.js"
integrity="sha384-H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO"
crossorigin="anonymous"></script>
$ curl -s https://static.example.net/vendor.min.js | openssl dgst -sha384 -binary | openssl base64 -A
H8BRh8j48O9oYatfu5AZzq6A9RINhZO5H16dQZngK7T62em8MUt1FLm52t+eX6xO # match: served bytes equal the published fileThe last command pair is the operator's own check: fetch the file through the path a browser would use and compare. A mismatch here is the earliest signal that something between origin and edge has altered the file.
04 RFC 9530: Content-Digest and Repr-Digest
For responses that are not scripts, and for requests, the hash travels in a header. RFC 9530 (2024) defines two integrity fields and obsoletes the older Digest header of RFC 3230, whose meaning implementations had read inconsistently. Content-Digest is a hash of the message content, the bytes actually in this message after any content encoding. Repr-Digest is a hash of the selected representation, the whole resource before transfer manipulation, which is what a client wants when it receives a range or a compressed encoding and needs to know whether the underlying file is intact.
Both are Structured Field dictionaries keyed by algorithm, so a sender can offer sha-256 and sha-512 together and a receiver may ignore either. The matching Want-Content-Digest and Want-Repr-Digest fields let a client ask for one, with a preference weight per algorithm. RFC 9530 registers the algorithms in an IANA registry with an Active or Deprecated status; md5 and sha are deprecated, sha-256 and sha-512 active. Section 6.1 of the RFC is blunt that these fields protect content and representation only, never the headers or the message as a whole, which is what makes signing them necessary.
05 Checksums for downloads, and signed responses
For firmware images, installers and archives the oldest method still applies: publish a checksum file beside the download, ideally on a different host, and a detached signature over the checksum file made with a key the publisher has announced. The receiver hashes the downloaded file, compares, and verifies the signature. This is the model most operating system distributions and hardware vendors use for firmware, and it is the right one for any file an engineer will load onto a switch or router; the archived Foundry security advisories are a reminder of what a wrong image can do.
For HTTP itself, the digest fields become end-to-end proof only when signed. RFC 9421 HTTP Message Signatures lets a server cover Content-Digest or Repr-Digest as a signed component, together with the status, content type and a date, and carry the signature in Signature-Input and Signature response headers. A client that holds the publisher's public key can then verify the signature, recompute the digest and know that both the body and its metadata are what the origin sent, regardless of how many caches lay between. The request side of the same standard is covered in signature headers on bot requests.
06 Comparing the content integrity mechanisms
| Mechanism | What it covers | Who checks it | What it misses |
|---|---|---|---|
| TLS 1.3 record integrity | Every byte on one hop | Both TLS endpoints, automatically | Anything done inside a terminating proxy or cache |
| Subresource Integrity | One script or stylesheet, as fetched by the browser | The browser, before execution | Non-subresource content; the HTML page itself; resources without CORS |
| Content-Digest (RFC 9530) | Message content bytes as encoded | Any client that recomputes | Headers; a different value per content encoding |
| Repr-Digest (RFC 9530) | The whole representation, independent of range or encoding | Any client that recomputes | Headers; nothing without a signature to stop replacement of the digest |
| Published checksum plus detached signature | A downloaded file | The operator, after download | Anything not downloaded as a file |
| RFC 9421 signed response | Chosen headers and the digest field | A client with the publisher's key | Content not covered by the digest; key distribution |
07 What an operator should log
Integrity failures are silent unless something records them. The browser reports SRI mismatches to the console and, where a reporting endpoint is configured through Integrity-Policy, to the server; collect those reports. The origin should log the digest it sent with each response, so a later dispute can compare what left the origin with what a client received. And the edge should log the digest of what it served, which turns a customer report of a corrupt file into a three-way comparison rather than a guess. The evidential logging page covers how to keep those records trustworthy.
08 Pitfalls: rewriting CDNs, gzip and digest scope
- A CDN that rewrites. Minification, image optimisation and script injection at the edge change bytes and break SRI and Content-Digest by design. Either disable rewriting for integrity-protected paths or compute hashes from what the edge serves, accepting that you are then trusting the edge.
- gzip and digest scope. Content-Digest covers the encoded bytes, so the same file served with and without gzip has two valid digests, and RFC 9530 notes that even one encoding can vary by compression level. Use Repr-Digest when the check should be about the file rather than this transfer.
- SRI without CORS. A cross-origin script with an integrity attribute but no crossorigin attribute, or served without an Access-Control-Allow-Origin header, is blocked outright. Test in a browser, not only with a hash tool.
- Range requests. A 206 response carries part of the representation; Content-Digest hashes the part, Repr-Digest the whole. Verifying the wrong one always fails.
- Hash beside the file on the same host. A checksum published next to the download proves only that both were replaced together. Publish it elsewhere or sign it.
- Trusting the digest without a signature. Anything that can replace the body can replace an unsigned Content-Digest header. The header detects accidents; only a signature detects intent.
Integrity sits between two other checks in the lane on authenticity at the network layer: certificate-based identity for the origin connection establishes who sent the bytes, and carrying a C2PA manifest through a CDN intact extends the same promise to the provenance record inside a media file. Retaining the digests in a signed log is what turns a check into evidence.
09 Questions
Does HTTPS guarantee content integrity?
Only for one connection. TLS 1.3 authenticates every record between its two endpoints, but a CDN or proxy terminates that connection, and whatever it does to the body before opening the next connection is outside TLS. End-to-end integrity needs a hash or signature the client can check independently.
What is Subresource Integrity?
A W3C mechanism where the page carries a base64 SHA-256, SHA-384 or SHA-512 hash of a script or stylesheet in an integrity attribute. The browser hashes the fetched bytes and refuses to run or apply the resource if the hash differs. It requires CORS for cross-origin resources.
How do I generate an SRI hash?
Hash the exact file with openssl dgst using sha384 in binary mode, base64-encode the output on one line, and prefix it with sha384- in the integrity attribute. Any change to the file, including a rebuild, requires a new hash; generate it as part of the build, not by hand.
What is the difference between Content-Digest and Repr-Digest?
Content-Digest (RFC 9530) hashes the bytes in this message after content encoding, so a gzip response and a plain response differ. Repr-Digest hashes the whole underlying representation regardless of encoding or range, so it describes the resource rather than the transfer.
Is a Content-Digest header proof of integrity on its own?
No. An intermediary that alters the body can recompute the header to match. The field detects accidental corruption and truncation. To detect deliberate change the digest field must be covered by an RFC 9421 signature made with a key the client can verify.
Should I verify checksums on firmware downloads?
Always. Hash the downloaded image, compare with the checksum the vendor publishes, and verify the vendor's signature on that checksum file where one exists. A corrupt or substituted image loaded onto a switch costs far more to recover from than a thirty-second check.
Content integrity in transit is not one control but a small set, each covering a different object, and the practical order is SRI on every third-party script today, Repr-Digest on API and download responses next, and signatures over those digests once key distribution is solved. The record of what was served, and the proof that the record itself is intact, is the next page in this lane.