🔀

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_host Procでリクエスト時に動的決定

  • 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行追加だけ

構造ダイアグラム

🌐
User Browser
negabaro.com/blog/...
① Request
Vercel Edge Network
Next.js Middleware + Global CDN
Rewrite
Host header
Cache
s-maxage=60
② Reverse Proxy
🛤️
Rails (DigitalOcean)
protocols.negabaro.com
canonical → www.negabaro.com
robots.txt: Disallow: /
asset_host = Proc { req.host }
📝
Jekyll (GitHub Pages)
negabaro.github.io
canonical → www.negabaro.com
canonical → SEO
url: https://user.github.io
PERFORMANCE Vercel Edgeフロントエンド配置の性能メリット
CACHE HIT
User → Vercel Edge (最近接ノード) → キャッシュ済みHTML即座に返却 ~50ms
CACHE MISS
User → Vercel Edge → Origin Server → HTML → Edgeキャッシュ保存 → 返却 ~200-500ms
STALE
User → Edge (staleレスポンス即座に返却) + バックグラウンドでOrigin更新 ~50ms
0~60s: キャッシュ応答
60~360s: stale + 更新
360s+: 再検証必須
~50ms
Edge TTFB (キャッシュヒット)
70+
Edgeノード (世界中)
0s
デプロイ時ダウンタイム
DEPLOY 独立デプロイのメリット
Rails
Railsサーバー再デプロイ中 → Vercel Edgeキャッシュが継続応答 → Jekyllブログ影響なし
Jekyll
GitHub Pagesにgit push → ビルド完了後自動反映 → Railsサーバー影響なし
Vercel
Middlewareルール変更 → Edgeに即座に反映 (数秒) → オリジンサーバー変更不要
新カテゴリ追加時
// src/middleware.ts
if (path.startsWith('/blog/new-category')) {
  return NextResponse.rewrite(`https://new.origin.com${path}`)
}
ASSET アセットURL処理: Rails vs Jekyll
🛤️ Rails: 動的決定
config.asset_host = Proc.new { |_, req|
  "#{req.protocol}#{req.host_with_port}"
}
https://protocols.negabaro.com/static/app.css
リクエストごとにホスト自動判別
📝 Jekyll: ビルド時に確定
# _config.yml
url: https://negabaro.github.io
https://negabaro.github.io/css/style.css
ビルド時に{{ site.url }}置換
📝 Jekyllプロキシ適用時の変更ファイル
ファイル 変更内容
_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 同一パターン適用
🔍 SEO戦略 (Rails / Jekyll共通)
canonical: www.negabaro.com/blog/... → ドメイン権威集中
robots.txt: オリジンサーバー Disallow: / → 重複インデックス防止
hreflang: ko/en/ja + x-default → 多言語SEO
アセット絶対URL: プロキシ経由でもオリジンから直接ロード
サブドメイン vs サブディレクトリ
blog.example.com example.com/blog
Google認識 別サイト 同一サイト
ドメイン権威 分散 集中 (バックリンク合算)
設定の複雑さ 低い 高い (プロキシ必要)
インフラ独立性 完全独立 独立 (プロキシ経由)
Rails vs Jekyll: プロキシ設定比較
🛤️ 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だけで自動デプロイ

動作フロー

1

[共通] ユーザーがnegabaro.com/blog/...にアクセス → Vercel EdgeのNext.js Middlewareがパスパターンマッチ

2

[共通] NextResponse.rewrite()でオリジンサーバーにプロキシ(Hostヘッダーをオリジンサーバーのホストに書換)

3

[Rails] オリジンサーバーが動的にHTMLレンダリング。asset_host = Proc { request.host_with_port } → アセットURLがリクエスト時に絶対パスで決定

4

[Jekyll] GitHub Pagesが静的HTMLを返却。_config.ymlのurl: https://user.github.io → ビルド時にアセットが{{ site.url }}/css/...で確定

5

[共通] Vercel Edgeがレスポンスをキャッシュ(s-maxage + stale-while-revalidate)してユーザーに配信

6

[共通] ブラウザが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でインデックス

ユースケース

技術ブログをメインドメイン配下パスに統合(SEO権威集中) マイクロサービスを単一ドメインに統合(サービスごとに異なる技術スタック) 静的サイト(Next.js)+ 動的コンテンツ(Rails/Django)ハイブリッド GitHub Pages(Jekyll)ブログをメインドメインサブディレクトリに統合 マルチアプリブログ統合(Rails + Jekyll + その他サービスをカテゴリ別にプロキシ)