A restaurant reprints its menu boards because the QR codes "expired". A conference reissues badges for the same reason. Both are usually wrong about what happened, and being wrong about it means they will hit the same problem again.
The pattern itself has no shelf life. Understanding why, and what actually broke instead, is the difference between a code you print once and a code you keep replacing.
The Code Is Just Text, Frozen
A QR code is a two dimensional encoding of a string of characters, defined by the ISO/IEC 18004 standard. The modules, the finder squares in three corners, the timing patterns, the alignment blocks: all of it exists to let a camera find the grid, correct for angle and recover the characters.
What it does not contain, anywhere in the specification:
- A creation date
- An expiry date
- A scan counter
- Any link back to whoever generated it
There is no field for those things. A QR code cannot phone home, because it is ink. When your camera decodes one, nothing has been contacted yet. Your phone has simply read a string, recognised it as a URL, and offered to open it.
That is the whole reason the "expired QR code" idea is confusing. The failure is always downstream of the square.
The Four Things That Actually Break
1. The destination moved or died
The most common cause by far. The code still decodes perfectly to "https://example.com/spring-menu", and that page returns a 404 because the site was rebuilt, the campaign page was deleted, or the domain lapsed.
The code did its job. There is just nothing at the other end.
2. The dynamic redirect stopped
This is the one people mean when they say expired, and it is worth understanding precisely because it is avoidable.
Most free online QR generators produce dynamic codes by default. The pattern does not encode your URL. It encodes something like "https://qr-service.example/a7Xk2", a short address on the generator's own domain. When someone scans, they hit that company's server, which looks up where you said to send them and redirects.
That indirection is genuinely useful. You can change the destination after printing, and you get scan analytics. It also means the generator sits permanently in the middle of your code, and there are several ways for that to end:
- The free trial expires, usually after 7 to 14 days, and the redirect starts serving an upgrade page instead of your link
- A scan quota is hit on a free plan
- The subscription lapses
- The company shuts down, and every code it ever issued dies at once
None of this is written on the code, and most of it is not obvious at generation time. People print thousands of stickers with a trial code and find out in week three.
3. A link shortener disappeared
Same failure shape, different middleman. Codes built on shortener links inherit whatever happens to that shortener. When Google retired goo.gl links, every printed QR code pointing through one stopped resolving. The square was perfect. The chain behind it was not.
4. Physical damage beyond the error correction budget
QR codes use Reed-Solomon error correction, which is why a code with a logo punched through the middle still scans. There are four levels, chosen when the code is generated:
| Level | Recoverable damage | Typical use |
|---|---|---|
| L (Low) | About 7% | Clean digital display, maximum data in minimum size |
| M (Medium) | About 15% | The common default |
| Q (Quartile) | About 25% | Print, some risk of wear |
| H (High) | About 30% | Logo overlay, outdoor print, industrial labels |
Level H is not magic. It is a budget, and real world wear spends it: sun fading the contrast, a sticker corner peeling, condensation on a window decal, scratches on a table laminate, a smudge across the finder pattern. Once the damage exceeds the level you chose, the code stops decoding. It did not expire. It was destroyed slowly enough that nobody noticed.
Static Versus Dynamic, Decided Properly
This is the choice that determines whether your code has an expiry date, so make it deliberately rather than accepting a generator default.
Static. The destination is encoded in the pattern itself. No server in the middle, no account, no subscription, no analytics. Nobody can switch it off, because there is nothing to switch off. The trade is that the destination is permanent: change it and you reprint. Longer URLs also produce denser patterns, which need more physical size to scan reliably.
Dynamic. The pattern encodes the generator's short URL. You can repoint it later and you get scan data. The trade is a permanent dependency on a third party remaining alive, funded and paid.
A reasonable rule:
- Print it once and forget it (business card, product label, plaque, book, equipment tag, WiFi code by the door): static, on a domain you own.
- Short campaign where you will want numbers (poster run, event, promotion, packaging insert with a changing offer): dynamic is a fair trade, from a paid plan, with a diary note for the renewal.
- Long-lived but might need repointing (restaurant menu, venue signage): static code pointing at a URL on your own domain, then change what that URL redirects to. You get the flexibility of dynamic with none of the dependency, because you are the middleman.
That last option is the one most people miss, and it is usually the right answer. "yoursite.com/menu" as a static code, with your own server deciding where /menu goes, is a code that cannot be turned off by anyone but you.
How to Print One That Outlives You
Own the domain in the code. If the string decodes to a domain you do not control, someone else can end your code.
Pick error correction level Q or H for anything physical. The pattern gets denser, so print it slightly larger. That is a cheap trade against fading and wear.
Respect the quiet zone. The standard calls for a clear margin of four modules on all sides. Designers crop it constantly. Without it, scanners cannot reliably find the grid edge, and the code becomes unreliable at exactly the angles people actually hold phones.
Size it for the scan distance. A rough working guide is a code width of about one tenth the intended scanning distance. A poster read from two metres wants roughly 20 centimetres of code. Table tent read at 30 centimetres is fine at 3.
Keep contrast dark on light. Inverted codes, low contrast pairings and busy background photos all cost you decode reliability. Some scanners handle inversion. Not all do, and you do not get to choose which phone shows up.
Do not put it behind glare or a fold. Laminated menus under a window light, codes printed across a brochure crease and codes on curved bottles all fail in ways that look random to the person holding the phone.
Test it after printing, not just on screen. On the actual material, at the actual size, in the actual light, from two or three angles, on both an old and a new phone. Screen previews hide almost every real world failure.
Diagnosing a Code That Stopped Scanning
Work down this list in order and you will find the cause in a couple of minutes.
1. **Decode it and read the raw string.** Do not just tap through to whatever opens. Look at the characters. This one step tells you whether you are dealing with a static code (your own domain), a dynamic one (a generator's short domain) or a shortener.
2. **Open that URL manually in a browser.** If it 404s or lands on an upgrade page, the code is fine and the destination is the problem.
3. **Try the same code on a second phone.** Working on one device and not another usually means marginal print quality, contrast or size rather than a dead link.
4. **Look at the physical code closely.** Check the three finder squares and the quiet zone first. Damage there fails the whole code far faster than damage in the data area.
5. **Try a straight-on scan in even light.** If that works and an angled scan does not, the issue is contrast, glare or size, and it will keep getting worse.
Step one is the one people skip, and it is the one that decides everything after it.
Reading the Raw String
You need a scanner that shows you the decoded content before it opens anything, which is not what a phone camera does by default. The camera app is optimised for tapping straight through, which is exactly the behaviour that hides the diagnosis from you.
Smart QR & Barcode Reader shows the decoded string first, keeps a scan history so you can compare what a code resolved to last month against today, and generates codes as well as reading them. Seeing the destination before opening it is also the single most effective habit against malicious codes, since a swapped sticker over a parking meter or restaurant table is the whole basis of quishing.
It cannot repair a physically damaged code, and it cannot resurrect a redirect a company has switched off. What it does is show you which of those two you are dealing with in about five seconds.
The Short Version
- QR codes contain no expiry information and cannot expire on their own
- What expires: the destination page, the generator's dynamic redirect, the shortener in the middle, or the physical print
- Free generators usually hand you a dynamic code by default, and dynamic means someone else can turn it off
- Static codes on your own domain have no third party to fail, and you can still repoint them by redirecting the URL yourself
- Error correction is a damage budget, not immunity. Use Q or H for anything printed
- Before blaming the code, decode it and read the raw string. The answer is almost always right there