Files
marketplaces/docs/context/adrs/ADR-0004-route-every-tenant-api-through-a-same-origin-gateway.md
sdarbinyan f4ea4c7af8
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
fix(api): route tenants through origin gateway
2026-08-20 14:23:12 +04:00

2.2 KiB

id, title, status, date, supersedes, tags
id title status date supersedes tags
ADR-0004 Route every tenant API through a same-origin gateway active 2026-08-20
architecture
multi-tenant
api
routing
nginx

ADR-0004: Route every tenant API through a same-origin gateway

Context

The production bundle embedded api.dexarmarket.ru and constructed unknown tenant URLs as https://{tenant}.api.dexarmarket.ru:445. Bootstrap used a different route (/bootstrap on the storefront origin), while auth had its own fixed API base. A custom domain could therefore use three different backend origins.

Directly calling the backend port is not a safe fallback: production returned 403 for both a bootstrap request carrying Origin: https://gorbushka.market and the corresponding CORS preflight. Meanwhile an unmatched /bootstrap on the storefront nginx server fell through to index.html, producing a misleading HTTP 200 with HTML instead of bootstrap JSON.

Decision

Every tenant frontend uses one API base derived at runtime from the browser origin: {origin}/backend.

  • Bootstrap loads from {origin}/backend/bootstrap.
  • Auth receives the same base through the AUTH_API_URL provider.
  • Legacy endpoints append their existing paths to the same base.
  • Versioned /api/... endpoints retain the /api prefix when routed.
  • nginx owns /backend/, removes that prefix when proxying, and forwards the original Host, X-Forwarded-For, and X-Forwarded-Proto headers upstream.

The frontend contains no tenant/domain allowlist and no production backend hostname. Tenant selection remains a server-side responsibility based on the verified forwarded host.

Consequences

Browser traffic is same-origin, so custom domains do not require per-tenant CORS configuration and one bundle works for every attached domain. Bootstrap, auth, legacy routes, and versioned routes cannot silently drift to different hosts.

Every nginx tenant/catch-all configuration must include the /backend/ gateway. Deploy verification must check that /backend/bootstrap returns JSON rather than accepting a generic HTTP 200 from the SPA fallback. The upstream must still reject unknown hosts; the gateway preserves Host but does not authenticate a tenant by itself.