shrt.design

Auxiliary Systems

Custom Domains & Branded Links

How does go.acme.com/promo redirect correctly when a thousand different customers each bring their own domain?

DNS CNAMESNI / TLSMulti-tenancyRedis
RequestDomain LookupCode Lookup

Click a node to see what it does. Switch tiers above to see how the design scales.

Overview

A real, differentiating feature of a Bitly-style product: customers use their own branded domain (go.acme.com) instead of the shared one. It’s a good “expand on later” topic because it changes the shape of the redirect lookup from single-tenant to multi-tenant, touching DNS and TLS along the way — real depth, but squarely not part of the core three subsystems.

API Design

POST /api/domains
// request
{ "domain": "go.acme.com" }

// 201 Created
{
  "domain": "go.acme.com",
  "verification_status": "pending_dns",
  "cname_target": "custom.shrt.design"
}

The customer points a CNAME record for go.acme.com at custom.shrt.design. Once DNS and TLS are verified, links created under that domain redirect from go.acme.com/{code} instead of the shared domain.

Database Schema

CREATE TABLE custom_domains (
  domain              VARCHAR(255) PRIMARY KEY,
  tenant_id           BIGINT NOT NULL,
  verification_status VARCHAR(20) NOT NULL DEFAULT 'pending_dns',
  cert_status         VARCHAR(20) NOT NULL DEFAULT 'pending',
  created_at          TIMESTAMPTZ NOT NULL DEFAULT now()
);

ALTER TABLE urls ADD COLUMN domain VARCHAR(255) REFERENCES custom_domains(domain);

Basic Approach — Per-Domain Row, Single Shared Redirect Path

How it works

Store each verified custom domain in a table. On a redirect request, look up which domain it arrived on (from the HTTP Host header) first, then look up the code within that domain’s namespace.

Request (Host: go.acme.com, path /promo) ──▶ look up domain "go.acme.com"
                                          └▶ look up code "promo" scoped to that domain ──▶ 302

Tradeoffs

  • Pro: Minimal change to core Redirection — it’s the same lookup, just keyed by (domain, code) instead of code alone.
  • Con: Every custom domain needs its own valid TLS certificate for HTTPS, and provisioning and renewing one per customer domain on the fly is nontrivial infrastructure the single-domain version never needed.

Scaled Approach — Wildcard TLS Termination + Verification Pipeline

How it works

Rather than provisioning a certificate synchronously and blocking on it, an automated pipeline (e.g., ACME/Let’s Encrypt) verifies DNS ownership — the CNAME must resolve correctly — then requests and installs a certificate asynchronously, moving the domain from pending to active. The edge load balancer terminates TLS using SNI to pick the right certificate per incoming domain.

Customer adds CNAME ──▶ Verification Worker polls DNS ──resolves──▶ request cert via ACME
                                                                  └▶ install at edge (SNI) ──▶ domain: active

Tradeoffs

  • Pro: Customers don’t need a human in the loop or a deploy to go live — the whole onboarding flow is self-service and automatic.
  • Pro: SNI-based termination means one edge fleet serves TLS for thousands of distinct customer domains without a dedicated IP each.
  • Con: DNS propagation and certificate issuance both have real-world delays outside the system’s control — the product has to communicate a “pending” state honestly rather than pretending it’s instant.
  • Con: A misconfigured or expired customer DNS record can silently break redirects for that one domain without affecting anyone else — worth having per-domain health checks rather than assuming “worked once” means “still works.”

Advanced Approach — Fully Multi-Tenant Routing with Isolation

How it works

As custom domains grow into the thousands, the domain-to-tenant lookup itself needs to be as fast and cache-friendly as the code lookup already is — a two-level cache: domain → tenant, then (tenant, code) → long_url. Rate limiting and click analytics become tenant-scoped rather than global, so one customer’s traffic spike or abuse can’t affect another’s limits or dashboards.

Request (Host header) ──▶ Edge Cache: domain → tenant_id (cached)
                       └▶ Edge Cache: (tenant_id, code) → long_url (cached) ──▶ 302
                                    └▶ per-tenant rate limits & click analytics, scoped independently

Tradeoffs

  • Pro: The redirect path stays just as fast for custom domains as for the shared domain, since the extra domain→tenant hop is cached at the edge, not a database round-trip per request.
  • Pro: Per-tenant isolation means a noisy or abusive customer can’t exhaust rate limits or pollute analytics for a different customer, even on shared infrastructure.
  • Con: Cache invalidation now has two layers instead of one — the domain mapping and the code mapping can each go stale independently.
  • Con: True per-tenant isolation (separate rate-limit buckets, separate analytics rollups) is more data modeling and operational complexity than most companies need until they have many large customers.

Tech Choices

  • DNS CNAME — the standard mechanism for a customer to point their domain at the shortener’s infrastructure without transferring ownership.
  • SNI-based TLS termination — one edge fleet, one IP range, correct certificate served per incoming domain.
  • ACME / Let’s Encrypt-style automation — self-service certificate issuance with no manual step.
  • Multi-tenant caching (domain → tenant → code) — keeps custom-domain redirects as fast as the shared-domain path.

How to Vocalize This in an Interview

Bring this up only if the interviewer explicitly asks about branded or custom links — it’s real depth (TLS, DNS, multi-tenancy all at once), but it’s squarely a “later” subsystem, not part of the core three worth naming in minute one.