A QR code doesn't look like a link. Nobody hovers over it to see where it goes, and the email filter only sees a black-and-white image. Quishing, phishing through QR codes, grew through that gap by 146% in the first quarter of 2026: from 7.6 million attacks in January to 18.7 million in March, according to Microsoft's quarterly email threat report. It was the fastest-growing vector of the period.
The irony is that the scammers' favorite feature, the dynamic QR code, is also the best defense for anyone who prints QR codes on packaging, menus, invoices or posters. Below is how the attack works and what you can do about it, with code.
What is quishing and why does it get past filters?
In regular phishing, the link is in the text. Email filters and browsers read that text, compare it against lists of dangerous domains and block it. In quishing, the URL is encoded in the modules of an image. Many security tools don't decode the image, so the message goes through clean.
The second effect is the device switch. Someone who gets the email on a laptop points their phone camera at the screen. The attack leaves a computer with antivirus, a corporate proxy and security extensions and lands in a mobile browser, with a shortened address bar and a rush to get things done.
How scammers use dynamic QR codes
A static QR code stores the final URL inside the image. A dynamic QR code stores a short link, and the server decides where to redirect on every scan. Scammers exploit that split: when the message is sent, the QR code points to a harmless page. If a smarter filter decodes the image and follows the link, it finds something clean and lets the message through. Hours later, the destination is switched to the fake page.
Unit 42, Palo Alto Networks' research team, documented this tactic: QR code shorteners turn a static image into a dynamic endpoint, and the attacker can change the redirect at will. In one case they analyzed, the code went through a CAPTCHA before landing on a fake Outlook page. Checking at delivery time isn't enough, because tomorrow's destination doesn't exist yet.
For end users, the practical rule is simple: if a parking meter, a shipping company or a bank asks you to act through a QR code, skip the code and open the official app you already have, or type the address yourself. For anyone publishing QR codes, the lesson is different: your customers will be increasingly suspicious, and the QR code has to give them a reason to trust it.

The risk for those who print QR codes
If your company puts QR codes on products or printed material, the problem isn't just a customer falling for a scam. It's a customer falling for a scam that looked like yours. Three scenarios come up often:
- Sticker on top: someone places a fake QR code over yours on a table, kiosk or parking meter. Visually, nothing changes.
- Rotten destination: the campaign domain expired, was bought by someone else, and the QR code printed on a thousand packages now leads somewhere else.
- Static QR code with a mistake: the URL had an error or the page went down, and there's no way to fix it without reprinting.
All three share the same cause: a printed QR code is out of your control once it leaves the print shop. A dynamic QR code gives that control back.
Defense 1: check the URL before publishing
The first step is cheap: before any URL becomes a QR code, ask whether it's safe. This applies to links your team types and, above all, to links that come from users, partners or integrations. The URL Safety Check API answers in a single call:
curl -sS 'https://api.respawntech.dev/v1/tools/safety/check?url=https://loja.example.com/promo' \
-H "X-API-Token: $RESPAWN_API_TOKEN"
# { "is_safe": true }async function isSafe(url) {
const res = await fetch(
`https://api.respawntech.dev/v1/tools/safety/check?url=${encodeURIComponent(url)}`,
{ headers: { "X-API-Token": process.env.RESPAWN_API_TOKEN } }
);
const { is_safe } = await res.json();
return is_safe;
}
if (!(await isSafe(targetUrl))) {
throw new Error("Destino bloqueado: URL marcada como insegura");
}In the Dynamic QR Code this check is built in: malicious URLs are rejected at creation with the dynamic_qr.malicious_url error, and a destination that later becomes unsafe sends the visitor to the QR unavailable page with reason=safety_risk instead of redirecting them.
Defense 2: change the destination without reprinting
Found out the destination page was compromised, or that the campaign is over? With a dynamic QR code, the printed image stays the same and only the destination changes. It's a PATCH, and editing the destination costs no credits:
curl -sS -X PATCH "https://api.respawntech.dev/v1/tools/image/qr-code/dynamic/$QR_ID" \
-H "X-API-Token: $RESPAWN_API_TOKEN" \
-H 'Content-Type: application/json' \
-d '{ "target_url": "https://loja.example.com/aviso-de-seguranca" }'await fetch(`https://api.respawntech.dev/v1/tools/image/qr-code/dynamic/${qrId}`, {
method: "PATCH",
headers: {
"X-API-Token": process.env.RESPAWN_API_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({ target_url: "https://loja.example.com/aviso-de-seguranca" }),
});During an incident, pointing to a warning page beats deactivating the QR code. Whoever scans understands what happened instead of thinking the code is broken.
Defense 3: controls that limit the damage
Not every QR code needs to work forever, or for anyone. The smaller the usage window, the less it's worth to someone looking to abuse it. The fields below work both at creation and in a PATCH:
| Field | What it does | When to use it |
|---|---|---|
| expires_at | Turns the QR code off on a date | Promotions, events, tickets |
| scan_limit | Turns it off after N scans | Limited-use coupons, giveaways |
| password | Requires a password before the redirect | Internal material, restricted documents |
| expired_url / limit_reached_url | Sends people to your own page when the QR code expires or runs out | Avoid a dead end after the campaign ends |
| custom_domain_id | Uses your domain in the short link | Reinforce the brand and teach customers to recognize your address |
The custom domain deserves attention. When every QR code from your company opens on qr.yourcompany.com, it's easy to guide customers: if the address is different, be suspicious. It's the same logic as teaching people to check the browser padlock, applied to the scan.
Checklist for teams that use QR codes
- Check every URL before it becomes a QR code, including those from partners and users.
- Prefer dynamic QR codes on any printed material that lasts more than a week.
- Use your own domain in the short link and tell your customers about it.
- Set an expiration or scan limit on campaigns with an end date.
- Watch the scans: a spike at odd hours or from an unexpected region may mean the QR code was copied.
- Never take a customer straight from a scan to a login or payment without context. If it's unavoidable, make the domain clear.
- Physically inspect QR codes in public places: a sticker on top is the cheapest scam there is.
Frequently asked questions
Can a QR code have a virus?
The QR code itself is just encoded text, usually a URL. The danger is the destination: a page that imitates a login, asks for card details or downloads a malicious app.
How can I tell if a QR code is safe?
Before opening it, check the address the camera shows. Be wary of strange domains, generic link shorteners and pages that ask for a password or payment right away. In your systems, check the URL via API before displaying it or redirecting.
Is a dynamic QR code more dangerous than a static one?
It depends on who controls it. In a scammer's hands, the changeable destination helps fool filters. In the hands of the company that prints it, it's what lets you fix a compromised destination without recalling material.
What should I do if one of my company's QR codes is tampered with?
If it's dynamic, point the destination to a warning page right away and investigate. If it's static, remove the material and replace it with a dynamic QR code on your own domain.
Start with the QR codes already out there
Quishing grows because it exploits the spot nobody looks at. For anyone printing QR codes, the defense is to treat each code as a link you must keep controlling after the print shop: check the destination, be able to change it and limit the usage window.
The Dynamic QR Code already rejects malicious destinations and lets you change the link without reprinting. The URL Safety Check brings the same check to the rest of your system. Details on every field are in the documentation.
About the author
Rychard
Respawn Tech Dev Team
The people who build and run the Respawn Tech Dev APIs.