Skip to content
UseQR
ESC

↑↓ MOVE↵ OPEN48 PLACES

Developers & agents

Caching and CDN strategy for QR images

A QR image is a pure function of its parameters, so it can be cached forever. Serve API-generated codes with Cache-Control public, max-age 31536000, immutable; key stored files by a hash of the generation inputs; and never cache-bust — changing the URL of a QR image just re-downloads identical bytes.

View as MarkdownPaste this page into any AI assistant — it is plain, portable Markdown.

The property that makes it easy

Most image caching is a compromise between freshness and speed. QR images are the rare case with no compromise to make: the image is a pure function of its inputs. Same data, size, error-correction level and styling → same matrix → with a deterministic renderer, the same bytes, today and in ten years. A static QR code for a given payload can never "update" — there is nothing to be stale about.

So the correct cache policy is the most aggressive one HTTP offers:

Cache-Control: public, max-age=31536000, immutable

That is a year of caching plus the immutable hint, which tells browsers not to revalidate even on reload. It is exactly what UseQR's keyless API sends on every generated image, which has a pleasant consequence: an embedded https://useqr.app/q/hello.png is fetched roughly once per viewer per year, and usually once per CDN edge, ever.

If you serve your own QR endpoint

  • Make output deterministic first. No timestamps in SVG comments, no random ids, no library-version drift baked into bytes (why generators differ). Determinism is what makes every downstream layer safe.
  • Canonicalise parameters before caching. ?data=hi&size=512 and ?size=512&data=hi are the same image; sort and normalise the query so your CDN stores one object, not one per parameter ordering.
  • Send immutable, and mean it. If a parameter combination could ever return different bytes, that is a bug in determinism, not a reason for a short TTL.
  • Let the CDN take the traffic. With a year-long public TTL, any CDN turns your QR endpoint into a mostly-static asset: origin load approaches one render per unique parameter set. This is why a keyless, unmetered endpoint is sustainable at all.

If you store generated files

Key them by content, not by name:

key = sha256(canonical_json({data, size, ec, style, eye, color, bg, margin}))
path = qr/{key}.png

Hash-keyed storage gives you deduplication (a million rows with 900k unique payloads render 900k codes), idempotent re-runs, and safe parallelism — two workers producing the same key write identical bytes. It is the same discipline that makes bulk generation restartable.

Cache-busting is an anti-pattern here

Appending ?v=2 to image URLs is a habit from assets that change. A QR image cannot change — busting its cache re-downloads identical bytes and, worse, changes the URL your documents reference for no reason. The URL is the state: if you want a different code, you change the parameters, which is a new URL and caches independently. If you want the same code, the old URL is already correct forever.

The one honest caveat: signed or expiring URLs break all of this. A QR image served from storage with a signed URL (?X-Amz-Expires=…) has a URL that changes on every signing, so browsers and CDNs cache it as a new object each time — and if you ever print such a URL inside a QR code, it dies at expiry (the failure mode). Serve QR images from public, stable, unsigned paths.

FAQ

How long should QR code images be cached?

Forever, in practice: Cache-Control public, max-age=31536000, immutable. A QR image is fully determined by its generation parameters, so there is no freshness problem — the same URL can never legitimately return different pixels.

Should I put a CDN in front of a QR generation API?

Yes. With deterministic output and a year-long public TTL, the CDN absorbs nearly all traffic and the origin renders each unique parameter set roughly once. Canonicalise query parameter order so equivalent requests share one cache object.

Why is cache-busting wrong for QR images?

Cache-busting exists for assets whose content changes under a stable URL. A QR image cannot change — its URL encodes its parameters — so a version query string only defeats caching and churns the URLs your documents embed.

Can I use signed URLs for QR images?

Avoid it. Every re-signing produces a different URL, which defeats browser and CDN caching, and a signed URL embedded in a printed code stops working when the signature expires. Serve QR images from stable public paths.

Try it — free, no signup

  • A free QR code API with no keyUseQR's REST API needs no signup, no API key and no SDK. GET /api/v1/qr?data=hello returns a PNG. The shortest form is /q/hello.png, which drops straight…
  • Bulk QR generation at scaleGenerating hundreds to millions of QR codes: the batch API and its limits, local generation with worker pools, determinism as a caching strategy, and manifests.
  • QR code generation performanceWhat generating a QR code actually costs: encoding is microseconds, rendering dominates, and PNG rasterisation is the expensive step. Where optimisation pays.
  • QR codes in serverless functionsGenerating QR codes in Lambda, Cloudflare Workers and Vercel functions — bundle size, native dependency pitfalls, streaming, and when to skip the function entirely.