When building scalable web applications with Next.js, engineering teams frequently face an architectural trade-off: should you manage separate repositories for your public landing pages and internal CMS dashboard, or unify them into a single monorepo?
Maintaining multiple repositories introduces overhead: duplicated TypeScript schemas, double database configuration, and tedious design system synchronization. Conversely, placing /dashboard and /login alongside public pages creates unnecessary security surface exposure if administrative paths are discovered by automated crawlers.
The modern solution to this challenge is implementing an Advanced Reverse Proxy / Middleware Layer in Next.js with domain-level boundary isolation.
Multi-Domain Routing Topology
The proxy intercepts incoming traffic right at the edge entry point by inspecting the Host or x-forwarded-host headers:
Why 404 Rewrites Outperform 302 Redirects
A common anti-pattern is using standard HTTP redirects (302 Found or 307 Temporary Redirect) when unauthorized visitors access /dashboard, redirecting them to /login or /.
From an information security standpoint, an HTTP redirect confirms to automated penetration scanners that the route exists behind access control.
By issuing a NextResponse.rewrite pointing to the /not-found component, the server returns an authentic 404 Not Found response. Automated vulnerability scanners register the path as completely nonexistent.
Next.js Proxy Implementation
Here is a production-ready proxy pattern demonstrating clean multi-domain isolation:
1typescript] import { NextResponse, type NextRequest } from "next/server";2 3export async function proxy(request: NextRequest) { const { pathname } = request.nextUrl; const host = request.headers.get("x-forwarded-host") || request.headers.get("host") || ""; const hostname = host.split(":")[0].toLowerCase();4 5// 1. Identify Environment & Host Target const isLocal = process.env.NODE_ENV === "development" || hostname === "localhost" || hostname === "127.0.0.1";6 7const isAdminSubdomain = hostname.startsWith("admin.") || hostname === "admin"; const isAdminOnlyRoute = pathname === "/login" || pathname.startsWith("/dashboard");8 9// 2. Shield Public Domain: Hide Administrative Paths with 404 Rewrite if (!isAdminSubdomain && !isLocal && isAdminOnlyRoute) { const notFoundUrl = request.nextUrl.clone(); notFoundUrl.pathname = "/not-found"; return NextResponse.rewrite(notFoundUrl, { status: 404 }); }10 11// 3. Restrict Admin Subdomain: Limit Strictly to CMS Operations if (isAdminSubdomain) { const isAllowedAdmin = isAdminOnlyRoute || pathname === "/" || pathname.startsWith("/auth"); if (!isAllowedAdmin) { const notFoundUrl = request.nextUrl.clone(); notFoundUrl.pathname = "/not-found"; return NextResponse.rewrite(notFoundUrl, { status: 404 }); } }12 13return NextResponse.next(); }Hybrid Internationalization (i18n) Strategy
Another key challenge in a unified codebase is balancing different internationalization needs:
Public Domain (Path-based Routing): Requires
/en/...and/id/...sub-paths to allow search engine bots to crawl and index localized content independently.Admin Dashboard (Zero-Refresh Client State): Administrative users prefer clean URLs without path prefixes. Language preferences are stored in cookies (
admin-language) and managed instantaneously via React Context.
Ensure your session cookies specify
SameSite=Laxand configure root domain scoping (.example.com) only if session sharing between domains is intentionally required.
Conclusion
By employing a multi-domain proxy pattern in Next.js, engineering teams achieve maximum operational simplicity without compromising on security. One unified build and deployment process delivers two isolated web experiences tailored for their specific audiences.
