Vercel Subdirectory Reverse Proxyはどう動くのか?
Next.jsドメイン配下のサブディレクトリで外部サーバーのコンテンツを統合しSEO権威を集中
メインドメイン(negabaro.com)はVercelでNext.jsを運用しています。
ブログコンテンツ(/blog/)は別サーバー*でレンダリングします。
VercelのNext.js MiddlewareがNextResponse.rewrite()でリクエストをプロキシし、Hostヘッダーをオリジンサーバーのホストに書き換えます。
ブラウザURLはnegabaro.com/blog/...のまま維持されるため、ドメイン権威が分散しません。
なぜサブディレクトリか
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など多数の設定が必要
- ✗ アセットローディング問題: プロキシ経由時の相対パスCSS/JSがNext.js側で404発生
- ✗ デバッグの困難さ: プロキシチェーン(Vercel Edge → Rails)で問題発生時の原因特定が困難
- ✗ TTFB増加: キャッシュミス時のVercel → Rails往復時間追加(stale-while-revalidateで緩和)
- ✗ サブドメイン重複インデックスリスク: robots.txt + canonical未設定時に同一コンテンツが2つのURLでインデックス