What’s Actually in a QR Code? (And Why Your Marketing Team’s QR Codes Phone Home)
Published on May 18, 2026 by The Kestrel Tools Team • 8 min read
You’re at a conference. The booth has a printed QR code with a tagline like “Scan to learn more.” You point your phone at it, the URL preview pops up, and instead of acmecorp.com/whitepaper it reads lnk.bz/8fT2qx?utm_source=fall_summit_2026&fbclid=IwAR.... You tap through anyway, the page eventually loads, and somewhere a row appears in a dashboard that knows roughly when you scanned, on which device, and from which campaign.
That round-trip happens because of a quiet trend in how QR codes are generated online. The QR code itself — the black-and-white grid — is a static byte container. It cannot “track” anything by itself. But the payload that vendors choose to encode into that grid increasingly isn’t the destination URL; it’s a redirect through a server they own. The grid is the same. The privacy story is not.
This post is a peer-voiced walkthrough of what actually goes into a QR code at the byte level — encoding modes, error correction, Reed-Solomon — and how to tell whether a generator is producing a pure QR code or a tracking shim dressed up as one. The framing matters for qr code tracking privacy specifically: it’s the difference between a code that reveals nothing and a code that logs every scan.
Does a QR code track you?
A QR code is a static encoding of a string of bytes — by itself it does not track anything. Tracking happens only when the encoded string is a URL pointing at a redirect server, which logs the scan and forwards you to the real destination. If a generator encodes your URL directly, the QR code is purely client-side data and there is nothing to phone home. If a generator encodes its own short-link URL with a tracking ID, every scan is a server hit logged against you.
The quick test: scan the code with a reader that previews the URL before opening it (every modern phone camera does this). If the preview shows your domain, it’s a pure encoding. If it shows a different domain — qrco.de, bit.ly, tracker.example.com, lnk.bz, anything ending in a vendor’s branded short host — there is a redirect server in the path.
What’s actually inside the grid
A QR code is a 2D barcode defined by ISO/IEC 18004. The visible structure has three fixed components and one variable one:
- Three position-detection patterns — the large square “finder” markers in the top-left, top-right, and bottom-left corners. They tell a scanner where the code is and how it’s rotated.
- Timing patterns — the alternating black-and-white lines connecting the finders. They tell the scanner the size of one module (one cell of the grid).
- Format and version information — small fixed regions that encode the error-correction level and the QR version (1 through 40, where version 1 is 21x21 modules and version 40 is 177x177).
- The data area — everything else. This is the part that actually holds your encoded payload plus its error-correction codewords.
The data area is filled in a zigzag pattern from the bottom-right corner upward, two columns at a time, skipping over the structural patterns. Each pair of dark/light modules represents one bit. Eight bits make a codeword. A version-3 QR code at error-correction level M has 70 data codewords and 18 error-correction codewords — 88 bytes total of capacity, of which 70 are yours.
The four encoding modes
QR codes don’t just encode “text.” The spec defines four primary encoding modes, and a single QR code can mix them within one payload to save space. The mode is signaled by a 4-bit prefix at the start of the payload.
| Mode | Mode bits | Encodes | Bits per character |
|---|---|---|---|
| Numeric | 0001 | 0–9 | ~3.33 |
| Alphanumeric | 0010 | 0–9, A–Z, space, $%*+-./: | 5.5 |
| Byte | 0100 | Any byte (UTF-8 by convention) | 8 |
| Kanji | 1000 | Shift-JIS double bytes | 13 |
A URL like https://kestreltools.com/tools/qr-code-generator is encoded in byte mode, because lowercase letters and / aren’t in the alphanumeric set. That’s 49 characters × 8 bits = 392 bits, plus a 4-bit mode header and a length indicator, plus terminator and padding. It fits comfortably inside a version-3 QR code at error-correction level M.
If you encode the same URL in uppercase only — HTTPS://KESTRELTOOLS.COM/TOOLS/QR-CODE-GENERATOR — the encoder can use alphanumeric mode and shrink the payload to 49 × 5.5 ≈ 270 bits. That’s why you sometimes see all-caps URLs on printed marketing material: the QR code is physically smaller and prints crisper at the same module size.
Error correction: why a QR code still works with a coffee stain on it
The other half of the data area is Reed-Solomon error-correction codewords. Reed-Solomon is the same algorithm used in CDs, DVDs, and the Voyager probes’ deep-space telemetry. It treats the data as polynomial coefficients in a Galois field and computes redundancy that can recover from a known number of corrupted bytes.
QR codes ship four error-correction levels, and you pick one when you generate the code:
| Level | Recoverable damage | Use case |
|---|---|---|
| L (Low) | ~7% | Clean digital displays |
| M (Medium) | ~15% | Default — most printed material |
| Q (Quartile) | ~25% | Industrial / factory floor |
| H (High) | ~30% | Logos overlaid on the QR, outdoor signage |
The level you pick changes how much of the data area is yours. At level L, more bytes go to your payload. At level H, more go to redundancy. The H level is what lets you punch a logo through the middle of a QR code and have it still scan: up to 30% of the modules can be obscured and Reed-Solomon will recover the rest.
This matters for qr code tracking privacy in one specific way: a tracking-redirect URL is shorter than the destination URL it forwards to (that’s the point), so it can fit inside a smaller, denser QR code at higher error-correction levels. Vendors lean into this — a smaller code prints cleaner — but the reason they can use a smaller code is precisely that the encoded payload is their server, not your link.
A side-by-side: pure encoding vs tracking redirect
Let’s encode the same logical destination two ways. Suppose you want to send people to https://acmecorp.com/whitepaper-2026.
Pure encoding (what a static QR generator produces):
Mode: byte (0100)
Length: 36
Payload: https://acmecorp.com/whitepaper-2026
Version: 3 (29x29 modules)
Error correction: M (15%)
When scanned, the phone camera resolves DNS for acmecorp.com and connects directly. There is no third party in the path. The QR code itself is a static image you can print on a billboard and forget about.
Tracking redirect (what many “branded” or “dynamic” QR services produce):
Mode: byte (0100)
Length: 24
Payload: https://qrco.de/8fT2qx9k
Version: 2 (25x25 modules)
Error correction: M (15%)
The QR code looks the same shape, only smaller. But on scan, the phone resolves qrco.de, hits the vendor’s redirect endpoint, the vendor logs { scan_id, timestamp, user_agent, ip_geo } into their analytics backend, and then serves a 302 redirect to https://acmecorp.com/whitepaper-2026?utm_source=qrco&utm_campaign=8fT2qx9k. From the user’s perspective, the page still loads. From the privacy perspective, an entire row was just written to a database neither party explicitly asked for.
The tradeoff is real, not imaginary: the redirect lets the vendor change the destination after the QR is printed, gives the marketing team scan analytics, and enables campaign attribution. Those are legitimate marketing goals. The cost is that every scan is logged. Whether that cost is acceptable depends on the use case — and on whether your audience has been told that scanning is a server hit, not a printed-paper handoff.
How to tell which kind of generator you’re using
Three quick checks, none of which require special tools:
- Decode the QR before printing. Generate the code, then scan it with your phone while you can still see the URL preview. If the preview shows the URL you typed in, it’s pure. If it shows a vendor’s domain, it’s a redirect.
- Read the encoded text directly. Most decoders (including macOS Preview’s image inspector and any open-source QR decoder library) will dump the raw payload string. Compare that to what you originally entered.
- Check the network tab. On a desktop, open the page that hosts your QR code, generate one, and watch DevTools. A pure client-side encoder makes zero outbound requests when generating. A tracking generator hits its own backend at least once to register the short link.
A fourth check, slower but conclusive: open the generator’s privacy policy. Pure encoders typically have nothing to say about scans because nothing is logged. Tracking generators usually disclose scan analytics under a heading like “Information we collect when codes are scanned.”
When tracking is actually what you want
Not every marketing case calls for a static QR code. There are real reasons to use a redirect-based generator:
- You need to change the destination after print. The poster is in 200 train stations and the campaign URL just changed.
- You need scan-volume analytics for attribution. Reporting that the campaign produced 3,400 scans is a deliverable.
- You need A/B routing. Half of scans go to variant A, half to variant B.
In those cases, use a redirect generator deliberately and disclose it. The honest version of “scan to learn more” is closer to “scan to be redirected through our analytics endpoint to the article.” That’s a long tagline. It’s also accurate.
For everything else — Wi-Fi credentials, contact cards (vCards), payment URLs, plain-text notes, or links you control end-to-end — a pure client-side encoder is shorter, faster, more private, and can’t break if a vendor goes out of business or rotates a redirect.
A pure QR code in 20 lines of JavaScript
The encoding itself is well-documented and there are mature open-source libraries for it. Here is what a minimal, no-tracking encoder looks like in the browser using qrcode-generator:
import qrcode from 'qrcode-generator';
function makeQrSvgString(text, ecLevel = 'M') {
const qr = qrcode(0, ecLevel); // 0 = auto-version
qr.addData(text, 'Byte');
qr.make();
return qr.createSvgTag({ cellSize: 6, margin: 2 });
}
const svg = makeQrSvgString('https://acmecorp.com/whitepaper-2026');
const parser = new DOMParser();
const node = parser.parseFromString(svg, 'image/svg+xml').documentElement;
document.getElementById('out').replaceChildren(node);
No network request is made. The string you pass in is the string that gets encoded. The SVG that comes out is a self-contained image you can save, print, or embed. If you scan it, the destination is exactly the URL you provided.
That’s the whole technical surface area of a privacy-respecting QR code. The complexity people associate with QR codes — the Reed-Solomon polynomial math, the masking patterns, the version-and-format encoding — is real, but it’s been solved and packaged. What hasn’t been standardized is the commercial layer that wraps the encoding, and that’s the layer to scrutinize.
The takeaway
A QR code is a deterministic byte container. It does not track. It cannot track. The grid of dark and light squares you see on a poster is exactly as static as a printed phone number.
What tracks is the payload, and the payload is whatever the generator decides to put inside. Marketing-vendor generators commonly put their own redirect URL inside, then route every scan through their server before forwarding the user along. That’s a legitimate product — it solves real attribution problems — but it’s not what most people picture when they hear “QR code,” and it’s worth being explicit about which kind you’re producing.
If you’re encoding a Wi-Fi password for guests, a vCard for your business cards, or a static URL you control, a pure client-side encoder is the right tool. Kestrel Tools’ QR Code Generator is exactly that — your input string is encoded directly into the grid, in your browser, with no redirect, no short link, and no scan log. You can verify it the same way you’d verify any client-side tool: open the network tab while you generate, watch nothing leave the page, and scan the result to confirm the preview shows the URL you typed.
For the cases where you genuinely need scan analytics, use a redirect generator on purpose and tell your audience. For everything else, the answer to “what’s actually in this QR code?” should be the same as the question on the poster — and not a row in someone else’s database.