“Track your QR codes” sounds like surveillance. It is much less than that. A scan is a web request like any other, and a dynamic QR code service can see exactly what any website sees when you open it, and no more. This article sets out what that is, what it is not, and what to do if you need numbers from a static QR code.
Where the numbers come from
A dynamic code holds a short link to a redirect service. When a phone scans it, the phone asks that service for the page, the service writes down one line about the request, then sends the phone on to the real destination. The one line is the whole of QR analytics.
Here is a mock record of the kind a redirect service stores. The values are invented for illustration.
| Field | Example value | Where it comes from |
|---|---|---|
| Code | spring-menu | The short link in the squares |
| Time | 2026-09-07 12:41 UTC | The server’s clock when the request arrived |
| Country | GB | Looked up from the phone’s network address by the hosting provider |
| Device family | iOS | The browser’s user agent string |
| Unique visitor | yes | A one-way hash of the network address and user agent, compared with earlier scans of the same code |
| Destination sent | https://example.org/menu/spring | The code’s current setting |
That is it. There is no name, no email, no phone number and no account. The network address itself is used to work out the country and the hash, then dropped. A well-run service never stores it.
Note
QR Studio’s redirect stores the device family, the country code and a peppered one-way hash for the unique count. The raw network address is not written anywhere. This is how the app can show scan totals without holding personal data.
What a dynamic code can tell you
When. Every scan has a timestamp, so you can see scans by hour of day and by day of the week. A restaurant sees a lunch bump and a dinner bump. A poster campaign sees the week it went up and the week it came down.
Roughly where. The country is estimated from the network address. City-level guesses exist but are unreliable, especially on mobile networks, where a phone in Leeds can appear to be in London. Treat anything finer than country as a hint.
Which kind of phone. The user agent says whether the browser is on iOS, Android or a desktop computer. That is enough to know if your landing page must work well on iPhone, and not much more.
How many, and how many people. Total scans counts requests. Unique scans counts distinct hashes. The hash is a fixed-length fingerprint of the network address and user agent, and the same inputs always give the same fingerprint, so a repeat scan from the same phone on the same network is recognised without storing anything that identifies it.
- 09:00 4 scans
- 10:00 9 scans
- 11:00 18 scans
- 12:00 41 scans
- 13:00 37 scans
- 14:00 15 scans
- 15:00 6 scans
- 18:00 22 scans
- 19:00 28 scans
- 20:00 12 scans
What a dynamic code cannot tell you
- Who scanned. No identity of any kind reaches the service unless the person later types it into your page.
- Exact location. A QR code cannot use GPS. The phone’s camera app opens a link; it does not share position.
- What they did next. Once the redirect hands the phone to your page, the service is out of the picture. Whether they ordered, read or closed the tab is for your website analytics.
- The same person on two networks. Scan on Wi-Fi at home and again on mobile data, and you are two unique visitors.
- Scans that never reached the internet. A phone that recognises the code but does not open the link, or opens it with no signal, records nothing.
Common mistake
Reading unique scans as “people”. Two flatmates on the same Wi-Fi with the same phone model can count as one. One person on the bus and then at home counts as two. Uniques are a useful trend line, not a headcount.
Why a static code cannot be tracked
A static code holds the destination itself. The phone reads https://example.org/menu and opens it directly. No service sits in between, so there is nowhere for a count to happen. Any vendor that claims to track a static code has quietly made it dynamic.
The honest alternative is a UTM parameter. Add a tag to the end of the address, such as ?utm_source=qr&utm_medium=poster&utm_campaign=spring, and your own website analytics will show visits that arrived through that code. The code stays static, nothing depends on a vendor, and you still know the poster worked. The cost is a longer address and therefore a bigger code, as the static vs dynamic article shows.
Three ways people use this in practice:
- A shop with codes on two window posters. Static codes to the same page with
utm_content=left-windowandutm_content=right-window. The shop learns which window gets footfall, with no subscription. - A charity’s donation appeal in a printed newsletter. One dynamic code, because the appeal page changes each quarter and the charity wants a scan count for the trustees.
- A museum with 60 exhibit labels. Dynamic codes, so a label can be repointed when an exhibit moves and curators see which rooms get scanned. Country data tells them which languages to add.
Privacy expectations
If you put a code in front of the public, the people scanning are trusting you. A fair standard is: record only what the request gives you, never store the raw network address, never try to join a scan to a person, and say what you collect in your privacy policy. It is also the standard that data protection law in the UK and EU expects, since a network address counts as personal data while it is stored.
For the person scanning, the practical advice is simple. A phone’s preview banner shows the domain the code opens. If it is a domain you recognise, the analytics behind it are limited to the table above. If you would rather not appear in anyone’s counts at all, do not open the link. Scanning alone records nothing.
▸How the unique-visitor hash works
A hash function such as SHA-256 turns any input into a fixed 256-bit fingerprint. The same input always gives the same fingerprint, and there is no practical way to run it backwards. A redirect service hashes the network address and user agent together with a secret string called a pepper. Without the pepper, nobody who obtains the stored fingerprints can rebuild them from a list of candidate addresses, which is what makes the store safe to keep. Each code has its own set of fingerprints, so the same phone scanning two different codes produces two unrelated hashes and cannot be followed between them.
Try it yourself
Build a link with UTM parameters and watch the code grow as you type. Then decide whether the size cost is worth staying static, or whether you want a dynamic code instead.