Skip to content

Add 8 short-URL redirect routes for business card QR codes #1

Description

@doyled-it

Context

For the upcoming business card, the back side has 8 small QR codes pointing to short URLs on doyled-it.com. Each QR is sized at ~8.4mm — the smallest reliable scan size — and that's only achievable if the URL inside each QR is short. Hence: redirects.

Slug Destination
/m mailto:michael@doyled-it.com
/cal https://cal.com/doyled-it
/gh https://github.com/doyled-it
/p tel:+17754506522
/li https://linkedin.com/in/michaeldoyleml
/s https://scholar.google.com/citations?hl=en&user=LWscs78AAAAJ
/c https://doyled-it.com/card (see #2)
/w reserved (placeholder for future work-email slot)

Why redirects rather than direct URLs?

  • QR module size scales with URL length. Short slugs let every QR be a v1 (21×21) code at ~8.4mm, the smallest reliable scan size.
  • Destinations are editable later without reprinting cards (e.g., switching jobs, moving to a different scheduling tool, etc.).
  • Free server-log analytics on which channels people actually scan.

Approach

Add a single 11ty template that paginates over a JSON data file. Each entry generates a static HTML stub with both <meta http-equiv="refresh"> AND a JS location.replace() fallback. The JS path matters for mailto: and tel: schemes — some browsers refuse those in meta-refresh, but allow them via JS.

Files to add

src/_data/redirects.json

[
  { "slug": "m",   "url": "mailto:michael@doyled-it.com" },
  { "slug": "cal", "url": "https://cal.com/doyled-it" },
  { "slug": "gh",  "url": "https://github.com/doyled-it" },
  { "slug": "p",   "url": "tel:+17754506522" },
  { "slug": "li",  "url": "https://linkedin.com/in/michaeldoyleml" },
  { "slug": "s",   "url": "https://scholar.google.com/citations?hl=en&user=LWscs78AAAAJ" },
  { "slug": "c",   "url": "https://doyled-it.com/card" },
  { "slug": "w",   "url": "mailto:michael@doyled-it.com" }
]

src/redirect.njk

---
pagination:
  data: redirects
  size: 1
  alias: r
permalink: "/{{ r.slug }}/index.html"
eleventyExcludeFromCollections: true
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Redirecting…</title>
<meta name="robots" content="noindex">
<meta http-equiv="refresh" content="0; url={{ r.url | safe }}">
<link rel="canonical" href="{{ r.url | safe }}">
</head>
<body>
<p>Redirecting to <a href="{{ r.url | safe }}">{{ r.url }}</a>…</p>
<script>location.replace({{ r.url | dump | safe }});</script>
</body>
</html>

Slug-conflict check

None of the existing pages collide with the chosen slugs:

  • existing pages: index, baseball, contact, golf, music, projects, resume, resume-print, words
  • redirect slugs: m, cal, gh, p, li, s, c, w

Test plan

  • npm run build succeeds without errors
  • _site/m/index.html (and the other 7) exist after build
  • Each generated file contains the correct destination in both <meta refresh> and the JS fallback
  • Local dev: curl -sI http://localhost:8080/m/ returns 200 and the body contains the correct destination
  • Once deployed to doyled-it.com: scan a printed test QR for each slug from a real phone and verify the destination opens correctly (mailto, tel, https all behave)
  • No new entries in _site/sitemap.xml for the redirect stubs (they have eleventyExcludeFromCollections: true AND <meta name="robots" content="noindex">)

Notes

  • CNAME unchanged — same doyled-it.com domain.
  • No CI changes needed — the existing npm run build will pick up the new template via 11ty's pagination.
  • Once these merge, do a smoke test by hitting https://doyled-it.com/m, /cal, etc. before committing to the print order.

Out of scope

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions