How Does Vercel Subdirectory Reverse Proxy Work?
Consolidating External Server Content Under a Next.js Domain Subdirectory for SEO Authority
The main domain (negabaro.com) runs on Vercel with Next.js.
Blog content (/blog/) is rendered by **separate servers*.
Vercel's Next.js Middleware proxies requests via NextResponse.rewrite(), replacing the Host header with the origin server's host.
The browser URL remains negabaro.com/blog/... so domain authority is not diluted.
Why Subdirectory
Google treats subdomains (blog.example.com) as separate sites.
But subdirectories (example.com/blog) are recognized as part of the same site.
Backlinks, traffic, and content authority built by the blog directly contribute to the main domain.
Backend-Agnostic Pattern
Whether it's a dynamic server (Rails on DigitalOcean) or a static site (Jekyll on GitHub Pages), the same proxy pattern applies.
The only difference is asset URL handling:
Rails:
asset_hostProc for dynamic resolution at request timeJekyll:
{{ site.url }}in_config.ymlfor static resolution at build time
Pragmatic Choice: Jekyll Stays on Subdomain
While subdirectory proxy is optimal for SEO, aligning Jekyll (GitHub Pages) URL paths is tedious.
Matching Jekyll permalinks to /blog/archive/... requires:
Modifying permalink/baseurl settings
Rewriting all internal links
So Jekyll stays on its subdomain (negabaro.github.io) with only canonical/OGP/absolute asset URLs configured for SEO benefits.
This doesn't achieve full domain authority consolidation, but prevents duplicate indexing via canonical and ensures asset loading works through the proxy.
Performance Benefits of Vercel Edge Frontend
Vercel's global Edge Network acts as cache + CDN in front of the proxy.
s-maxage=60โ instant response from Edge for 60 secondsstale-while-revalidate=300โ serves stale content for 5 more minutes while refreshing in background
Users get cached HTML from the nearest Vercel Edge node worldwide, achieving fast TTFB regardless of origin server location.
Deployment Independence
Each origin server deploys completely independently.
Redeploying Rails doesn't affect Jekyll blog (and vice versa)
Vercel Edge cache absorbs origin downtime โ brief deployment interruptions are invisible to users
Adding new categories only requires one rewrite rule in Vercel Middleware
Architecture Diagram
if (path.startsWith('/blog/new-category')) {
return NextResponse.rewrite(`https://new.origin.com${path}`)
}
"#{req.protocol}#{req.host_with_port}"
}
https://protocols.negabaro.com/static/app.cssurl: https://negabaro.github.io
https://negabaro.github.io/css/style.css| File | Changes |
|---|---|
| _includes/head.html | 3 CSS โ {{ site.url }}/css/... absolute URL + canonical/OGP |
| _includes/footer.html | 3 JS โ {{ site.url }}/js/... absolute URL |
| _layouts/post.html | header background image โ {{ site.url }}/img/... |
| _layouts/page.html | Same pattern applied |
| blog.example.com | example.com/blog | |
|---|---|---|
| Google recognition | Separate site | Same site |
| Domain authority | Distributed | Concentrated (backlinks combined) |
| Config complexity | Low | High (proxy required) |
| Infra independence | Fully independent | Independent (via proxy) |
| ๐ค๏ธ Rails (DO) | ๐ Jekyll (GitHub Pages) | |
|---|---|---|
| Asset URL | Dynamic Proc (request.host) | Static {{ site.url }} |
| Result | protocols.negabaro.com/... | negabaro.github.io/... |
| canonical | Helper method (dynamic) | Liquid template (static) |
| robots.txt | Controller Disallow | Consolidated via canonical |
| Deploy | Independent deploy to DO | Auto deploy via git push |
How It Works
[Common] User visits negabaro.com/blog/... โ Next.js Middleware on Vercel Edge matches path pattern
[Common] NextResponse.rewrite() proxies to origin server (Host header replaced with origin host)
[Rails] Origin server dynamically renders HTML. asset_host = Proc { request.host_with_port } โ asset URLs resolved to absolute paths at request time
[Jekyll] GitHub Pages returns static HTML. _config.yml url: https://user.github.io โ assets baked as {{ site.url }}/css/... at build time
[Common] Vercel Edge caches response (s-maxage + stale-while-revalidate) and delivers to user
[Common] Browser loads CSS/JS directly from origin server (absolute URLs bypass proxy)
Pros
- ✓ SEO authority concentration: blog backlinks, traffic, and content authority directly add to main domain
- ✓ Global Edge caching: cached HTML served instantly from Vercel Edge nodes worldwide โ fast TTFB regardless of origin server location
- ✓ stale-while-revalidate: serves stale response first while refreshing in background after cache expiry โ users always get fast responses
- ✓ Deployment downtime absorption: Edge cache continues serving during origin redeployment โ zero-downtime deployment effect
- ✓ Independent deployment: Rails/Jekyll/other services deploy independently. Failures in one do not propagate to others
- ✓ Tech stack freedom: choose optimal technology per category (Rails, Jekyll, Hugo, Django โ anything works)
- ✓ Gradual expansion: adding a new category only requires one rewrite rule in Vercel Middleware
Cons
- ✗ Configuration complexity: Host header rewriting, asset_host, canonical, robots.txt and more required
- ✗ Asset loading issues: relative path CSS/JS returns 404 on Next.js side when proxied
- ✗ Debugging difficulty: hard to pinpoint issues in proxy chain (Vercel Edge โ Rails)
- ✗ TTFB increase: added Vercel โ Rails round trip on cache miss (mitigated by stale-while-revalidate)
- ✗ Subdomain duplicate indexing risk: same content indexed at 2 URLs without robots.txt + canonical