Build a QR Tiger alternative
Build a QR Tiger alternative as branded squares, a short URL, and daily scan counts. Print once, change the destination later, skip the $15–$49 rent.
You can build a QR Tiger alternative as squares that point at a URL you control. QR Tiger is that product. The catalog line is “Squares that send people wherever you say. Even later.”
You pay $15–$49 a month for it. That is $180–$588 a year. The basic replacement is 90 minutes: styled codes, editable redirects, and daily scan counts.
I would ship this if I still printed menus and posters from a rented generator. It is marked Tonight. The verdict on the card is “It’s literally squares”.
Yesterday I wrote about how Slack works. That was a teardown. This one is a regular backlog topic. The useful v1 is the image plus the short URL behind it.
What you need to build a QR Tiger alternative
The QR Tiger page splits the work into three rows.
The first row is the one you want tonight. Styled codes, a short redirect you can edit after printing, and a daily scan count. Colors, an optional logo, a PNG, a slug.
The second row is 4 hours. SVG export, bulk creation, UTM tagging, expiry. Those can wait until one printed code already works.
The third row is a month and up. Accounts, campaigns, landing pages, GPS analytics, a public API. The table labels that row as the part you should skip.
If you tell an agent to “clone QR Tiger”, it aims at the third row. Copy the prompt, then pin it to the first row.
Here is the shape of the data. Nothing else has to exist at 90 minutes.
const code = {
slug: 'menu-spring',
shortUrl: 'https://go.flaviocopes.com/r/menu-spring',
destination: 'https://flaviocopes.com/menu',
fg: '#171713',
bg: '#f4f1e8',
logo: null,
errorCorrection: 'M',
enabled: true,
}
A phone camera reads https://go.flaviocopes.com/r/menu-spring. Your server sends the visitor to the current destination. You change the menu URL in October. The poster from March still works.
Who a QR Tiger alternative is for
The audience line on the page is specific. A marketer putting QR codes on menus, posters, and packaging.
The goal is branded codes whose destinations stay editable, plus a count of scans. Tonight, one PNG you can print and one scan that lands on the current URL.
If you need GPS heatmaps, a landing-page builder, or a public API for other people’s codes, keep paying. The site puts that rebuild at a month on purpose.
The same v1 also replaces Flowcode and Uniqode. One focused build covers whichever of those you currently pay for.
A tweet-sized short link is Bitly, $35–$300 a month, about 90 minutes. The link shortener walkthrough is that post. Bitly’s 5-hour row already includes a QR download. Build this if the poster is the rent.
How does a QR code generator work
A QR code is a grid of modules. Three finder squares in the corners tell the camera which way is up. The payload is text. For this app the text is a short URL on a domain you own.
I am describing the catalog’s model here, not reverse-engineering QR Tiger. Encode the short redirect URL in the image, never the destination. A printed code cannot change its pixels.
Paste a destination, style the code, download the PNG, send it to print. Then edit the destination and confirm a fresh scan lands on the new URL.
The screens on the page are a codes list, an editor, an export view, and analytics. For 90 minutes you can live with the editor, a download button, and a count on the row.
The data model is Code, RedirectRule, ScanEvent. Tonight you need Code. The redirect is a slug and a destination on that row. ScanEvent can be a (code_id, day) counter.
The capability line is Astro, Workers, and D1. This site is an Astro app on Cloudflare, so that is the path I default to. Use Rails if that is your weekday language. Scope matters more than the framework.
Encode the short URL, never the destination
Agents stamp https://flaviocopes.com/menu straight into the image. That code dies the day the path changes, and the printed poster cannot be updated.
The mini-prompt on the page is “The redirect that outlives the print run”. Each code encodes a short URL you control. The slug maps to a destination row you can edit. The next scan after an edit must land on the new URL.
Use a 302 with short cache headers. A cached 301 makes edits invisible. The lookup has to be a single row read.
async function handleScan(db, slug, requestedAt) {
const code = await db.findBySlug(slug)
if (!code || !code.enabled) {
return { status: 302, location: '/code-off' }
}
queueIncrement(db, code.id, requestedAt)
return {
status: 302,
location: code.destination,
cacheControl: 'private, no-store',
}
}
Fire the redirect first. Count the scan after the visitor is already gone. The mini-prompt wants the lookup lean enough to answer in a few milliseconds.
Disabled codes serve a polite this-code-is-off page. Invalidate any cached redirect in the same write that flips enabled, so the next scan sees it.
Validate the destination before you print
Your short domain becomes an open redirect the moment the field accepts javascript: or a loop back at itself.
The guardrail on the page is explicit. Validate destinations as http or https. Keep redirects fast. Record scans without cookies, fingerprints, or precise location.
function normalizeDestination(raw, redirectHost) {
const trimmed = raw.trim()
const withScheme = trimmed.includes('://') ? trimmed : `https://${trimmed}`
const url = new URL(withScheme)
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
throw new Error('Only http and https destinations are allowed')
}
if (url.username !== '' || url.password !== '') {
throw new Error('Destinations cannot include credentials')
}
if (url.hostname === redirectHost) {
throw new Error('Destination cannot point at the redirect host')
}
return url.href
}
normalizeDestination('flaviocopes.com/menu', 'go.flaviocopes.com')
//'https://flaviocopes.com/menu'
Never fetch the destination to check that it loads. Fetching turns the save path into an SSRF proxy. You are validating shape, not liveness.
Rate-limit creation so a script cannot mint thousands of codes on your domain. The same function should run for a single create and for a CSV import later.
Keep styled codes scannable
Brand colors and a centered logo are why people pay for this. They are also how codes stop scanning.
QR error correction comes in four public levels, L, M, Q, and H. A logo covers modules. The rule on the page is to raise the level to H when a logo is present, and to leave the finder patterns untouched. Cap the logo so it stays a small fraction of the code.
Scanners want dark modules on a light field. A pretty inverted scheme with high contrast still fails. Warn on that, not only on a low ratio.
The mini-prompt is “The scannability check that saves your print budget”. After you render, decode the styled image with a QR reading library and refuse to export anything that fails a round-trip read.
Tonight, a live preview that you scan with your own phone is enough. The automatic decoder is the 4-hour version of the same check.
The ISO QR spec also wants a quiet zone, a 4-module light border around the grid. Designers crop it. Bake it into the export so a print shop cannot trim it off.
Count scans without cookies
The catalog is explicit about what to keep: date, code, and a coarse device category, with no cookies, IP addresses, or precise location.
Tonight you can skip device. Increment a (code_id, day) row. Filter obvious bots and link-preview crawlers by user agent before counting.
function scanKey(codeId, requestedAt) {
const day = requestedAt.toISOString().slice(0, 10)
return `${codeId}:${day}`
}
scanKey('menu-spring', new Date('2026-10-20T18:04:00.000Z'))
//'menu-spring:2026-10-20'
Increment atomically so two simultaneous scans from a launch-day poster do not lose a count. Disabled codes do not increment anything.
The dashboard needs a number that moves. The 7- and 30-day totals can wait until you have a week of real scans. Show a zero-state that explains when data will appear, so an empty chart does not look broken.
Print-ready PNG, then cancel the subscription
A screen-sized PNG is not a print file. The export mini-prompt is “The export that speaks millimeters, not pixels”.
The user picks a physical size and a DPI. Pixel width is size in millimeters times DPI, divided by 25.4. Round so each module lands on a whole number of pixels. Fractional module widths anti-alias and break scanning.
function pngPixels(sizeMm, dpi) {
return Math.round((sizeMm * dpi) / 25.4)
}
pngPixels(30, 300)
//354
Ninety minutes can ship one print-ready PNG at a fixed size. 30mm at 300 DPI is plenty for a menu. SVG, bulk, and a size picker belong on the 4-hour row.
The codes list needs the thumbnail, the short URL, the destination, the scan total, and disable. Campaign folders can wait.
What not to build in a QR Tiger clone
Leave out landing pages, GPS tracking, team workspaces, white-label domains, and a public API.
The non-goal on the page already names those. You are replacing the generator you currently rent for posters.
If the agent starts a page builder, stop it. A landing page is Carrd, $2–$4 a month, about 2 hours. The Carrd walkthrough is that post. Point the QR at a page you already host.
The 4-hour row, after you print one
Use a real poster or a real menu for a week. Then look at the second estimate.
SVG export, so a print shop can scale without blur. Bulk creation from a CSV of destinations. UTM tags on the destination, so a spring menu and a window sticker can share a slug family. Expiry, so a launch code dies on schedule.
The catalog says 4 hours for that row. Stop there. Skip the month-long platform.
QR Tiger alternative or a link shortener
A short URL for a tweet is still Bitly. A QR Tiger alternative is that redirect plus an image people scan from paper.
A bio page of links is Linktree, $9–$24 a month, about 30 minutes. You already have that post if a list of URLs is the rent. Put one of those URLs behind the QR if you want both.
Host it, print a real poster, cancel
The product pages put hosting this way: a focused replacement costs your build time plus close-to-zero hosting.
Put the redirects on a subdomain you already own, go.flaviocopes.com. There is a D1 table for codes and counts, so take a backup before you delete the paid account.
Use it for a week as the real generator, not as a demo. Print one code, scan it, edit the destination, scan again. If you miss SVG or bulk, add them from the 4-hour row.
Then cancel QR Tiger, or Flowcode, or Uniqode, whichever one you pay for.
If you want another Tonight card after this one, the audit is still the index. Shots.so is about 2 hours if the next bill is a screenshot frame.
Start now
Go to Build your own QR Tiger. Copy the AHA prompt, or the stack you already write.
Tell the agent the first estimate only: styled codes, editable redirects, daily scan counts. Ninety minutes.
Download the PNG. Point one real poster at it tonight.