QR codes are everywhere now. Restaurant menus, business cards, conference badges, event flyers — if there's something that needs to point someone to something else, there's probably a QR code involved. Most people just Google "QR code generator," use whatever free web tool comes up, and move on with their day.
I did that for a while too. Then I got annoyed at the ads, the upsell prompts, and the general feeling that my data was probably being monetized in some creative way I didn't agree to. So I built my own.
What It Is
QR Code Generator is a self-hosted web app and API for generating QR codes and 1D barcodes. It runs in Docker, it's stateless, and it's fast. You can use the live demo at qrcode.chrisrmiller.com or self-host it yourself using the repo at github.com/cmivxx/qrcodegen.
What It Supports
On the QR code side, it handles plain text, URLs, email, phone numbers, SMS, Wi-Fi credentials, contacts (MECARD format), geo locations, and calendar events. On the barcode side, it covers Code 128, Code 39, EAN-13, EAN-8, UPC-A, ITF, and Codabar.
Beyond format support, you get a fair amount of control over the output — foreground and background colors, size anywhere from 100 to 2000 pixels, margin, error correction level, and your choice of PNG or SVG output. There's a live preview so you see what you're getting before you download it, a dark mode because it's 2026 and everything should have dark mode, and a shareable API endpoint if you want to generate codes programmatically.
The API
This was important to me from the start. The UI is convenient, but the API is what makes the tool actually useful for automation.
Every generation option available in the UI is also available via a POST request to /api/generate. Want to drop a QR code for a URL into a script without touching a browser? One curl command:
curl -s -X POST https://qrcode.chrisrmiller.com/api/generate \
-d "format=qrcode&content_type=url&url=https://chrisrmiller.com&size=400&ec_level=H&output_format=png" \
| python3 -c "import sys,json,base64; d=json.load(sys.stdin); open('qr.png','wb').write(base64.b64decode(d['image'].split(',')[1]))"You get a base64-encoded image back in JSON, or a direct binary download if you hit /api/generate/download instead. Clean, simple, no authentication required for the public instance.
The Stack
It's Python 3.9 with Flask, served by Gunicorn with four workers, containerized with Docker. The QR generation is handled by the qrcode library with Pillow for image processing, and python-barcode handles the 1D formats. The frontend is vanilla HTML, CSS, and JavaScript — no framework, no build step, no 47 npm dependencies just to render a button.
The whole thing runs as a single container behind Cloudflare, which handles TLS, WAF, and rate limiting. The app itself stays intentionally simple — no database, no persistent storage, no moving parts that can break at 2am.
On the security side, inputs are validated server-side before anything generates. Colors are checked against a hex regex before hitting Pillow, size is clamped at 2000px to prevent someone from requesting a 50,000-pixel image and taking the server out, and string inputs are length-limited. Standard security headers are set on every response.
What's Coming
The current tool is useful as-is, but it's really the foundation for something bigger. The plan is to integrate it with a URL shortener I'm building — one with click and impression analytics, A/B testing, stored history, and editable destinations for codes that have already been printed. The QR generator will stay stateless and standalone either way, but when the shortener is connected, you'll be able to generate a code, have it point to a short URL, and then change what that URL redirects to later without reprinting anything.
The shortener may end up with a small cost to cover running expenses — but small means small. Nothing wild. The self-hosted path will always be available for people who'd rather run it themselves.
Try It or Host It
The live demo is at qrcode.chrisrmiller.com. If you'd rather self-host, clone the repo and you're up in about 60 seconds:
git clone [email protected]:cmivxx/qrcodegen.git
cd qrcodegen
docker compose up -dThen open http://localhost and you're good to go.