codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
Notes›security

Public-route allow-list — keep it in sync when adding domains

Published 2026-05-26·0 views

Public-route allow-list

In any app with an auth gate, there's a spot that decides which routes anonymous users may see. That spot is usually a public-route allow-list. When you add a new domain and forget to update it, users hit informational pages and get bounced to login.

1. The incident shape

Situation What the user sees Real cause
Anonymous user opens /pharmacy → redirected to /login?returnUrl=/pharmacy "Why do I need to log in to read this?" Allow-list missing /pharmacy
Search-engine crawler can't index informational pages SEO penalty Same cause — the crawler is anonymous
New domain merges with no allow-list update User has to report it; you patch post-hoc Sync obligation missed

2. Common matcher shape

There are two kinds of routes anonymous users should see:

  • Exact paths: /, /login, /signup, /help
  • Prefix matchers: /pharmacy, /pharmacy/seoul/12345 (whole domain)

A prefix matcher lets a single startsWith line cover an entire domain — the most robust form.

function isPublicRoute(pathname: string): boolean {
  if (pathname === "/" ) return true;
  if (pathname.startsWith("/login")) return true;
  if (pathname.startsWith("/signup")) return true;
  // informational domains ↓
  if (pathname.startsWith("/pharmacy")) return true;
  if (pathname.startsWith("/price-compare")) return true;
  if (pathname.startsWith("/long-term-care")) return true;
  // ...
  return false;
}

A function beats a string-union type here — you need dynamic matching like startsWith.

3. The 7 spots to sync when adding a domain

This is spot #7 of the same 7-spot sync from dashboard widget uniformity.

New-domain PR checklist
├─ Route registration (app/x/page.tsx)
├─ Dashboard widget
├─ Sidebar
├─ Footer link
├─ i18n ko/en
├─ Search category
└─ Public-route allow-list   ← this spot

When this single spot is missed, anonymous users can't even reach the domain — maximum user impact for one of the easiest items to overlook.

4. Before / after

Aspect Allow-list missing Allow-list in sync
Anonymous entry Forced login redirect Informational page renders
Crawler indexing Fails Succeeds
New domain discovery "Why can't I see it?" → user reports Smooth launch
SEO Penalty Unaffected
Regression detection After the user complains At PR review

5. Adopting this in your own project

  • At your auth gate (middleware · ClientLayout · server guard) make a single SSOT isPublicRoute() function.
  • Add "registered in publicRoute" as a checkbox on your new-domain PR template.
  • Default to prefix matchers (startsWith) so all sub-paths are covered automatically.
  • Separate "informational" vs "auth-required" routes in code with a comment block, so review can spot category drift.
  • Add an e2e regression — anonymous fetch returns 200 for informational domains and 308 for auth domains. Count the buckets.

One anonymous user shut out might be a search engine. The allow-list is a small spot, but it decides the width of your public surface.

More in security

All in this category →
  • Anonymous forms — minimum safety net
  • Security Headers and CORS
  • Password Hashing — bcrypt, scrypt, Argon2
  • Input Validation — Trim at the Boundary
  • Rate Limiting — Algorithms and Implementation
  • OAuth — state, PKCE, OIDC