# QR code checksum and integrity

> A QR code carries no payload checksum. Reed–Solomon protects codewords against damage, and BCH codes protect the 15-bit format and 18-bit version fields, but a code that decodes cleanly proves nothing about who made it. Integrity and authenticity must live in the payload — a CRC, a token or a signature.

Source: https://useqr.app/docs/spec/qr-code-checksum-and-integrity · Last reviewed 2026-08-21 · UseQR is free forever, MIT licensed, no signup.

---

## What protection a QR code actually contains

Three error-control mechanisms exist in the symbol, protecting three different things:

| Mechanism | Protects | Size |
|---|---|---|
| [Reed–Solomon](/docs/spec/reed-solomon-error-correction) | data + EC codewords | 7–30% of codewords, by level |
| BCH (15,5) | [format information](/docs/spec/format-information) — EC level and mask | 10 check bits on 5 data bits, stored twice |
| BCH (18,6) | [version information](/docs/spec/version-information) (versions 7+) | 12 check bits on 6 data bits, stored twice |

Notice what is missing: **there is no checksum, CRC or hash over the payload itself**.
The bit stream is mode indicators, character counts, data and padding — nothing that
summarises or fingerprints the content. QR's error control exists to survive *transport
damage* (dirt, blur, missing modules), not to vouch for the data.

## What Reed–Solomon does and does not guarantee

What it does: if the damaged codewords are within the level's budget, the decoder
recovers the original codewords *exactly* — error correction is reconstruction, not
approximation. As a side effect it provides strong error *detection*: random corruption
beyond the correctable budget almost always fails to decode rather than producing wrong
text, because a fabricated-but-consistent codeword set is astronomically unlikely to
arise by accident. Silent misdecodes are possible in principle when damage grossly
exceeds the budget, which is one reason critical systems re-verify decoded content rather
than trusting a lone scan.

What it does not do:

- **Authenticate.** Reed–Solomon math is public and runs in every generator. Anyone can
  produce a flawlessly valid QR code saying anything — that is precisely what a
  [quishing sticker](/docs/security/qr-code-stickers-placed-over-real-ones) is. "It
  scanned cleanly" carries zero evidence about *who created it*.
- **Validate meaning.** A decoder happily returns a malformed URL, a wrong account
  number or an expired link. Correct transport of wrong content is still wrong content.
- **Protect the payload's semantics.** A typo in the URL you *encoded* is preserved with
  perfect fidelity.

## If you need integrity, put it in the payload

Since the symbol layer will not carry it, the payload layer must. Three escalating
patterns, all in production use:

1. **Checksums for typos and truncation.** EMVCo payment payloads — the format behind
   [PIX](/docs/payments/pix-qr-code-format) — end in a mandatory CRC-16 over the whole
   string, so a mis-scanned or hand-mangled payload fails validation in the payment app
   (details: [PIX CRC16 checksum](/docs/payments/pix-crc16-checksum)). Same idea:
   check digits in [GS1 GTINs](/docs/spec/gs1-digital-link-in-qr).
2. **Server-side validation for guessing resistance.** Ticketing and voucher codes embed
   a random token that the backend validates — no offline forgery value, revocable, but
   requires connectivity.
3. **Signatures for authenticity.** A cryptographic signature over the payload proves
   origin offline — anyone can verify with the public key, only the key holder can
   issue. Formats and trade-offs: [signed QR codes and authenticity](/docs/security/signed-qr-codes-and-authenticity).

The pattern to internalise: **the QR layer is a transport; trust decisions belong to the
content**. Scanner-side, the same logic applies to humans —
[check what a code decodes to before acting on it](/docs/security/how-to-check-a-qr-code-before-opening).

Generator-side, the useful integrity check is the round trip: render the code, decode it,
compare against the intended payload byte for byte. That is exactly what
[/validate](/validate) automates, and what UseQR's `/api/v1/verify` endpoint exposes for
pipelines.

## FAQ

### Does a QR code have a built-in checksum?
Not over the payload. Reed–Solomon codewords protect against physical damage and BCH codes protect the small format and version fields, but nothing in the symbol fingerprints or validates the data content itself.

### Can a QR code decode to the wrong text?
Physically corrupted codes overwhelmingly fail to decode rather than misdecode, because consistent-but-wrong codeword sets rarely occur by chance. The realistic risk is different — a perfectly valid code that was malicious or mistyped from the start.

### How do payment QR codes prevent errors?
The payload format adds its own protection. EMVCo-derived formats such as PIX end with a mandatory CRC-16 the payment app must verify, and account identifiers often carry check digits, so mangled payloads are rejected before money moves.

### How do I make a QR code tamper-evident?
You cannot at the symbol level — anyone can print a valid code. Embed a server-validated token or a cryptographic signature in the payload, or control the physical medium with tamper-evident labels, and verify scans against the issuing system.

## Try it

- https://useqr.app/validate
- https://useqr.app/scan
- https://useqr.app/url
