Before we shipped Trek Point broadly, I thought about subdomains mostly in branding terms:

  • www for the marketing site
  • app or maps for the product
  • maybe developer later if we ever built API access

In production, subdomains turned out to be much more than naming. They became a real product boundary with security, UX, routing, and operational consequences.

Trek Point ended up using distinct surfaces for:

  • the main site
  • the map product on maps
  • the developer portal on developer
  • tenant-style public pages on dynamic subdomains

That felt clean from the outside. Under the hood it forced us to make very deliberate choices about cookies, CSRF, CORS, and URL ownership.

Why We Did It

The split gave us real product benefits.

The marketing site and the route planner have different jobs. Marketing wants canonical URLs, localized content, and public SEO pages. The map product wants a more app-like feel, different navigation, and freedom to evolve quickly. The developer portal wants to feel like a platform, not an afterthought buried in account settings.

Subdomains made those surfaces legible.

They also helped us model public identity. Trek Point supports member and organization-facing pages that live on tenant-like subdomains. That is not just cosmetic. It changes how users think about ownership and sharing.

The Cost Nobody Mentions Up Front

Once you put authenticated features on multiple subdomains, browser behavior becomes architecture.

Suddenly you care about:

  • the exact cookie domain
  • whether requests are same-origin or merely same-site
  • which endpoints can safely accept credentialed CORS
  • how CSRF tokens move across surfaces
  • what health checks do when SERVER_NAME and subdomain routing are strict

You also learn that “it works locally” is meaningless unless local hostnames mirror production closely enough to surface these problems early.

We Chose Narrow CORS on Purpose

One of the easiest ways to get into trouble with multi-subdomain apps is to make credentialed CORS too broad. Trek Point deliberately took the opposite path.

Instead of treating cross-subdomain requests as generally allowed, we scoped them to a very small set of cases. In one path we allowed credentialed CORS only for a specific route-copy endpoint. The origin had to match the configured cookie domain or an explicit allowlist, and the preflight headers were tightly constrained.

That is not the kind of code people celebrate in architecture talks, but it is exactly the kind of code that keeps a tidy URL strategy from becoming a security mess.

In Some Cases We Avoided CORS Entirely

One of my favorite examples in this codebase is not the CORS logic itself. It is the fact that we duplicated certain routes on tenant subdomains to stay same-origin.

That sounds wasteful until you look at the alternative. If a user action can be served on the current host, doing that is often safer and easier than inventing a cross-subdomain browser dance with cookies and CSRF headers. Sometimes the pragmatic move is to repeat a small amount of routing rather than widen your trust surface.

That is a product engineering lesson I trust more every year:

avoid cross-origin complexity when a same-origin path is available

The Routing Side Was Also Real Work

Subdomains are not just auth concerns. They also shape naming rules.

Once Trek Point had fixed subdomains like maps and developer, tenant naming could not be a free-for-all. We had to reserve names, validate public subdomains, and prevent collisions with usernames and system routes.

That is the kind of issue teams often discover late, after links already exist in the wild. We were lucky to encode those rules into org and member subdomain validation before the public namespace got messy.

What I Like About This Design

I still think the product got real value from this separation.

  • the map experience feels like its own tool
  • the developer portal feels intentional
  • public profile URLs are cleaner and more memorable
  • marketing and app routing can evolve independently

Those are meaningful user-facing wins, not just internal elegance.

What I Would Reconsider

I would be even more aggressive about documenting the “subdomain contract” early. In practice that contract includes:

  • cookie scope
  • host ownership rules
  • which endpoints are allowed to cross origins
  • how local development mirrors production hosts
  • what canonical URLs exist for every surface

If you do not write that down, the codebase will still encode it, but new engineers will learn it by breaking things.

The Broader Takeaway

Subdomains are one of those decisions that look reversible until product, browser, and SEO behavior all start depending on them.

At Trek Point they became a useful boundary, but only because we treated them like architecture instead of decoration. The hostname was part of the product model.

That is the real lesson: as soon as a URL surface carries identity, auth, or trust assumptions, DNS stops being ops trivia and starts being application design.