The portfolio and this blog are the same deployment. vasharma05.com and
blogs.vasharma05.com resolve to one Caddy instance, which forwards both to one
Next.js container on port 3000. Nothing about the request distinguishes them
except the Host header, and that turns out to be enough.

Why not two containers
The obvious alternative is a second app. I ran the numbers on the box first:
| MB | |
|---|---|
| One Next.js process | 150 |
| A second Next.js process | 150 |
| Headroom left on a 2 GB box | ~180 |
A second process costs about what the headroom is worth, and the two apps would share the theme provider, the fonts, the CSS custom properties, and the navigation shell anyway. Duplicating all of that to get a separate port is a bad trade.
The rewrite
Middleware runs before routing, so it can move a request to a different path without the browser ever seeing it:
export function middleware(request: NextRequest) {
const host = normalizeHost(request.headers.get("host"));
const prefix = host.startsWith("blogs.") ? "/_blog" : "/_site";
const url = request.nextUrl.clone();
url.pathname = prefix + request.nextUrl.pathname;
return NextResponse.rewrite(url);
}
Two details cost me time. First, App Router treats a directory whose name begins
with _ as private and excludes it from routing entirely — to get a real
/_blog URL segment the directory has to be named %5Fblog. Second, the matcher
has to exclude anything with a file extension, or /favicon.ico gets rewritten
into a route group that has no handler for it.
What this rules out
output: "export". Middleware does not run in a static export, so hostname routing disappears with it.- Client-side host sniffing. It would work, but only after hydration, which means shipping the wrong page first and correcting it.
The default branch is the portfolio. Localhost, the Docker healthcheck, and any Host header I did not anticipate all land somewhere sensible instead of a 404.
Pages are still prerendered — output: "standalone" keeps a Node runtime for
middleware and image optimisation, not for rendering. The server is handing back
cached HTML on both hostnames.