QR label URLs
A QR label encodes a link to the item or location it's stuck on. Scan it, and that link opens the record in Cobblr. Because the link is printed into the code, the address it points at is baked in the moment you print.
Why this matters
Say you print a hundred labels, stick them on a hundred bins, and later you move Cobblr to a new home, or just change how you reach it. Every label was printed with the old address, so every one of them now points at nothing. A hundred dead stickers.
The fix is to never print the moving address in the first place. Print a stable name that you control, and forward that name to wherever Cobblr actually lives today. Move Cobblr later, and you re-point the forward once instead of reprinting a hundred labels.
Which path, and what's my base URL?
Two questions settle it.
What is my base URL? The address you open Cobblr at, up to the host and port,
with nothing after it: https://cobblr.example.com, a DuckDNS name, or
http://192.168.1.50:8088. Never a /w/<workspace> path. A label carries which
workspace and item in its own token, so the base is just the instance.
Could that address ever change? This decides whether you print the address itself or put a redirect in front of it.
- It will not change. Print it straight into your labels: set it as the base URL and you are done.
- It might change. Maybe you are on a free name (a DuckDNS or shared address) today and could move to your own domain later, or you self-host and might switch boxes. Print a stable name with a redirect instead of the address, so a move is re-pointing the redirect once, not reprinting a hundred stickers. This holds even on an address that works fine today: if you might ever leave it, the redirect is what keeps the labels alive.
One more rule if your instance is private (tailnet or LAN): a label someone else scans still has to reach a public address. If only your own devices scan, a tailnet name works as the base URL. If outside people scan, make it public first (Tailscale Funnel, a Cloudflare Tunnel, or a domain), then use that.
For example: you self-host at a DuckDNS name today but expect to buy your own domain later. Point a redirect at the DuckDNS address now, print the redirect's URL onto the labels, and re-point it to the domain the day you switch. The stickers never change.
Use a stable name you control
Pick an address that belongs to you, not to whichever machine happens to run Cobblr right now:
- A domain you own (
labels.example.com), with a redirect rule at it. - A free Cloudflare Worker on
workers.dev: a ten-line redirect with no domain to buy and no machine behind it, so the name survives every box you own. The URL is longer, but nobody types it; a QR code doesn't care. - The DuckDNS or Tailscale name you already reach Cobblr at (from the install options). Simplest when the name and the instance move together; a Tailscale name is private to your tailnet with Serve, or publicly reachable with Funnel, so keep in mind who'll be scanning.
There's a difference in kind between these. A name that points at your instance (DuckDNS, Tailscale) keeps labels alive as long as Cobblr stays behind that name. A name with a redirect at it (the domain, the Worker) is decoupled from the instance entirely: when Cobblr moves anywhere, even to a different address scheme, you edit one forward and every printed label keeps resolving. The sticker never changes; only the thing it forwards to does.
If you go the redirect route, the exact steps for a Cloudflare Worker and for Caddy or nginx are at the end of this page, under Setting up the redirect.
Setting the base URL
Set it once per workspace, in the label settings, as the custom label base URL. From then on, every label you print encodes that name no matter how you happen to be reaching the app at print time.
Leave it blank and labels fall back to the address you're viewing Cobblr at when you print. That works for a quick batch you'll scan at home, but for labels meant to last, set the stable name first. If you still need to create that name, Setting up the redirect below has the steps.
What the QR's link reveals
The link a QR label encodes ends in a token, and the token comes in two styles. Descriptive tokens carry a readable hint of what they point at, so a phone's camera preview shows something meaningful before you even open the link. Opaque tokens are random, revealing nothing about your stuff to someone who photographs the label. Pick per workspace in the label settings; plain navigate-to-record labels default to descriptive.
The difference shows up right in the URL a phone previews before you tap it. A descriptive token opens with a three-letter code for the record's type; an opaque one is just the slug:
Descriptive: https://labels.example.com/qr/inv/Xk7Bq2mNp8rL
Opaque: https://labels.example.com/qr/Xk7Bq2mNp8rL
Both resolve to the same record. The descriptive link says inv on the way in;
the opaque one gives nothing away. Both are short on purpose: labels often print
small, and a shorter URL makes a coarser, easier-to-scan QR code.
| Code | Record |
|---|---|
inv | Inventory item |
loc | Location |
ast | Asset |
mch | Machine |
prj | Project |
tsk | Task |
lst | List |
Any other kind of record uses the first three letters of its type, so a record type you add yourself still reads sensibly.
Descriptive is used only for a plain "open this record" label that never expires and expects you to be signed in. A label that triggers an action, one you gave an expiry date, or one set to work without a login always gets the opaque form, since those are the labels where naming the record type would give away the most.
Printing and moving
Labels are printed from the Labels page: browse to what needs labeling (by kind, or walk the location tree), queue them, and print the batch. Every label printed after you set the base URL carries the stable name.
When Cobblr does move, the labels don't change and neither does Cobblr: you update the forward at your DNS or proxy so the stable name points at the new home, and every sticker in the building keeps resolving.
Setting up the redirect
A redirect-style stable name (a domain you own, or a Cloudflare Worker) needs a
small forwarder: it takes an incoming label URL and sends the scanner on to
wherever Cobblr lives today, keeping the path so a label pointing at
/qr/inv/Xk7Bq2mNp8rL lands on that exact record. Always use a temporary
redirect (HTTP 302), never a permanent one (301). A 301 gets cached by browsers
and phones, which is the opposite of what you want when the whole point is to
change the target later.
Cloudflare Worker
A Worker fits when you want the stable name decoupled from every box you own, with nothing to host yourself. It is free for this.
A Worker is public, so it forwards anyone who scans a label on to your instance. That means the instance needs to be reachable from the public internet: a domain, a DuckDNS name, or a Tailscale name with Funnel on. (A private Tailscale name with Serve only resolves for your own devices, which is fine if you are the only one scanning.)
There are two ways to set the Worker up: a one-line script that does it for you, or by hand with the Cloudflare CLI.
The one-line script
This writes the Worker, deploys it to your Cloudflare account, and prints the stable URL to paste into Cobblr. It needs nothing installed beyond what your computer already ships with. Pick the tab for the computer you are running the command on (your own laptop or desktop, not where Cobblr is hosted):
- macOS or Linux
- Windows
Open the Terminal app (on macOS: press Cmd+Space, type "Terminal", press Enter), then paste this and press Enter:
curl -fsSL https://docs.cobblr.xyz/scripts/setup-qr-redirect.sh | sh
Open PowerShell, not Command Prompt (click Start, type "PowerShell", press Enter), then paste this and press Enter:
irm https://docs.cobblr.xyz/scripts/setup-qr-redirect.ps1 | iex
You need a free Cloudflare account (the script links you to sign up). It then
asks three things: a Cloudflare API token (it walks you through creating one),
your instance's public URL, and a name for the Worker. The first time you use
Workers, Cloudflare also needs a free workers.dev sub-url (a one-time,
account-wide name like yourname.workers.dev); the script claims one for you if
you don't have it yet. Re-run it any time Cobblr moves to re-point the same
labels.
By hand, with the Cloudflare CLI
Prefer to see every step, or already have Node installed? wrangler does the
same thing:
-
Install the CLI and sign in:
npm install -g wranglerwrangler login -
In an empty folder, create
wrangler.toml.TARGETis the public address you reach Cobblr at right now, and the only line you change when Cobblr moves:name = "cobblr-labels"main = "src/index.js"compatibility_date = "2024-11-01"[vars]TARGET = "https://cobblr.your-tailnet.ts.net" -
Create
src/index.js:export default {async fetch(request, env) {const url = new URL(request.url);return Response.redirect(env.TARGET + url.pathname + url.search, 302);},}; -
Deploy with
wrangler deploy. The first deploy registers aworkers.devsubdomain and prints your Worker's URL, likehttps://cobblr-labels.your-name.workers.dev. -
Set that URL as the custom label base URL in Cobblr (Setting the base URL, above), then print your labels.
When Cobblr moves, change the one TARGET line and run wrangler deploy again.
The printed labels never change.
Local reverse proxy
If you already terminate TLS on your own box (Caddy or nginx in front of
Cobblr), host the redirect there instead of using a third party. Point a stable
hostname (labels.example.com) at your box and add one redirect rule.
Caddy:
labels.example.com {
redir https://cobblr.example.com{uri}
}
nginx:
server {
listen 443 ssl;
server_name labels.example.com;
# ... your existing ssl_certificate lines ...
return 302 https://cobblr.example.com$request_uri;
}
Both keep the full path and query and use a temporary redirect, so you re-point
cobblr.example.com at the new home whenever the instance moves.