Vercel Subdirectory Reverse Proxy는 어떻게 동작하나?
Next.js 도메인 아래 서브디렉토리로 외부 서버 콘텐츠를 통합하여 SEO 권위를 집중
메인 도메인(negabaro.com)은 Vercel에서 Next.js로 운영합니다.
블로그 콘텐츠(/blog/)는 *별도 서버**에서 렌더링합니다.
Vercel의 Next.js Middleware가 NextResponse.rewrite()로 요청을 프록시하되, Host 헤더를 원본 서버 호스트로 교체합니다.
브라우저에는 negabaro.com/blog/... URL이 유지되므로 도메인 권위가 분산되지 않습니다.
왜 서브디렉토리인가
Google은 서브도메인(blog.example.com)을 별도 사이트로 취급합니다.
반면, 서브디렉토리(example.com/blog)는 같은 사이트의 일부로 인식합니다.
블로그에서 쌓은 백링크·트래픽·콘텐츠 권위가 메인 도메인에 직접 합산됩니다.
백엔드에 무관한 패턴
동적 서버(Rails on DigitalOcean)든, 정적 사이트(Jekyll on GitHub Pages)든 동일한 프록시 패턴으로 통합됩니다.
차이점은 에셋 URL 처리 방식뿐입니다:
Rails:
asset_hostProc으로 요청 시점에 동적 결정Jekyll:
_config.yml의{{ site.url }}로 빌드 시점에 정적 확정
현실적 선택: Jekyll은 서브도메인 유지
서브디렉토리 프록시가 SEO에 최적이지만, Jekyll(GitHub Pages)은 URL 경로 구조를 맞추는 작업이 번거롭습니다.
Jekyll의 permalink 구조를 메인 도메인의 /blog/archive/... 경로와 완벽히 일치시키려면:
permalink, baseurl 수정
내부 링크 전면 교체
따라서 Jekyll은 서브도메인(negabaro.github.io)에 그대로 두되, canonical/OGP/에셋 절대 URL만 설정하여 SEO 이점만 취하는 방식을 선택했습니다.
서브디렉토리만큼의 도메인 권위 통합은 안 되지만, canonical을 통한 중복 방지와 에셋 절대 URL로 프록시 호환성은 확보됩니다.
Vercel Edge를 앞단에 둔 성능 이점
Vercel의 글로벌 Edge Network가 프록시 앞단에서 캐시 + CDN 역할을 합니다.
s-maxage=60→ 60초간 Edge에서 즉시 응답stale-while-revalidate=300→ 캐시 만료 후에도 5분간 stale 응답을 먼저 전달, 백그라운드에서 갱신
사용자 입장에서는 전 세계 어디서든 Vercel Edge에서 캐시된 HTML을 받으므로 원본 서버의 지리적 위치와 무관하게 빠른 TTFB를 얻습니다.
배포 독립성
각 원본 서버는 완전히 독립적으로 배포됩니다.
Rails 서버를 재배포해도 Jekyll 블로그에 영향 없음 (역도 마찬가지)
Vercel Edge 캐시가 원본 서버 다운타임을 흡수 → 짧은 배포 중단은 사용자에게 노출되지 않음
새 카테고리 추가 시 Vercel Middleware에 rewrite 규칙 1줄 추가로 끝
구조 다이어그램
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| 파일 | 변경 내용 |
|---|---|
| _includes/head.html | CSS 3개 → {{ site.url }}/css/... 절대 URL + canonical/OGP |
| _includes/footer.html | JS 3개 → {{ site.url }}/js/... 절대 URL |
| _layouts/post.html | header 배경이미지 → {{ site.url }}/img/... |
| _layouts/page.html | 동일 패턴 적용 |
| blog.example.com | example.com/blog | |
|---|---|---|
| Google 인식 | 별도 사이트 | 동일 사이트 |
| 도메인 권위 | 분산 | 집중 (백링크 합산) |
| 설정 복잡도 | 낮음 | 높음 (프록시 필요) |
| 인프라 독립성 | 완전 독립 | 독립 (프록시 경유) |
| 🛤️ Rails (DO) | 📝 Jekyll (GitHub Pages) | |
|---|---|---|
| 에셋 URL | 동적 Proc (request.host) | 정적 {{ site.url }} |
| 결과 | protocols.negabaro.com/... | negabaro.github.io/... |
| canonical | Helper 메서드 (동적) | Liquid 템플릿 (정적) |
| robots.txt | Controller에서 Disallow | canonical로 통합 |
| 배포 | DO에 독립 배포 | git push만으로 자동 배포 |
동작 흐름
[공통] 사용자가 negabaro.com/blog/... 에 접속 → Vercel Edge의 Next.js Middleware가 경로 패턴 매칭
[공통] NextResponse.rewrite()로 원본 서버에 프록시 (Host 헤더를 원본 서버 호스트로 교체)
[Rails] 원본 서버가 동적으로 HTML 렌더링. asset_host = Proc { request.host_with_port } → 에셋 URL이 요청 시점에 절대 경로로 결정
[Jekyll] GitHub Pages가 정적 HTML 반환. _config.yml의 url: https://user.github.io → 빌드 시 에셋이 {{ site.url }}/css/... 로 확정
[공통] Vercel Edge가 응답 캐시 (s-maxage + stale-while-revalidate) 후 사용자에게 전달
[공통] 브라우저가 CSS/JS를 원본 서버에서 직접 로드 (절대 URL이므로 프록시를 거치지 않음)
장점
- ✓ SEO 권위 집중: 블로그의 백링크·트래픽·콘텐츠 권위가 메인 도메인에 직접 합산
- ✓ 글로벌 Edge 캐싱: Vercel의 전 세계 Edge 노드에서 캐시된 HTML을 즉시 응답 → 원본 서버 위치와 무관하게 빠른 TTFB
- ✓ stale-while-revalidate: 캐시 만료 후에도 stale 응답을 먼저 전달하면서 백그라운드 갱신 → 사용자는 항상 빠른 응답
- ✓ 배포 다운타임 흡수: 원본 서버 재배포 중에도 Edge 캐시가 응답을 계속 제공 → 제로 다운타임 배포 효과
- ✓ 독립 배포: Rails/Jekyll/기타 서비스를 각각 독립 배포. 한쪽 장애가 다른 쪽에 전파되지 않음
- ✓ 기술 스택 자유: 카테고리별로 최적의 기술 선택 (Rails, Jekyll, Hugo, Django 등 무엇이든 가능)
- ✓ 점진적 확장: 새 카테고리 추가 시 Vercel Middleware에 rewrite 규칙 1줄 추가로 끝
단점
- ✗ 설정 복잡도: Host 헤더 교체, asset_host, canonical, robots.txt 등 다수 설정 필요
- ✗ asset 로딩 이슈: 프록시 경유 시 상대 경로 CSS/JS가 Next.js 측에서 404 발생
- ✗ 디버깅 어려움: 프록시 체인(Vercel Edge → Rails)에서 문제 발생 시 원인 특정 어려움
- ✗ TTFB 증가: 캐시 미스 시 Vercel → Rails 왕복 시간 추가 (stale-while-revalidate로 완화)
- ✗ 서브도메인 중복 인덱싱 위험: robots.txt + canonical 미설정 시 동일 콘텐츠가 2개 URL로 인덱싱