- SPAは壊れているのではなく「サーバが正しく動いている」
- そもそもSPAのルーティングとは何か
- なぜ404が起きるのか(本質)
- なぜリンククリックでは動くのか
- 必要になるサーバ設定の正体
- よくある誤解:historyモードが悪い?
- もう一つの問題:APIの404を壊す危険
- SEOやSSRとの関係
- 結局どう考えるべきか
SPAは壊れているのではなく「サーバが正しく動いている」
SPA(Single Page Application)を公開した直後に、多くの人が遭遇する現象があります。
トップページは表示できるのに、URLを直接開くと404になる。
リンククリックでは遷移できるのに、リロードすると「ページが見つかりません」になる。React、Vue、Next.js、Nuxt、どれを使っても起きます。これはフレームワークの不具合ではありません。サーバが正しく404を返しているから発生します。
つまりこの問題はフロントエンドではなく、HTTPとWebサーバの仕組みの問題です。
そもそもSPAのルーティングとは何か
通常のWebサイトでは、URLはそのままファイルの場所を意味します。
| URL | サーバの解釈 |
| /about.html | about.htmlを返す |
| /contact.html | contact.htmlを返す |
しかしSPAでは違います。URLは「画面の状態」を表しているだけで、実際のファイルは存在しません。
例:
- /users/1
- /settings/profile
- /dashboard
これらのHTMLファイルはサーバ上に存在していません。存在するのは1つだけです。
- index.html
SPAはこのindex.htmlを読み込んだ後、JavaScriptのルーターがURLを解釈して画面を切り替えています。
つまりSPAのルーティングとは「ブラウザ側の仮想ルーティング」です。サーバはURLの意味を理解していません。
なぜ404が起きるのか(本質)
ブラウザからサーバへは、次のようなHTTPリクエストが送られます。
GET /dashboard HTTP/1.1 Host: example.com
ここでサーバは「/dashboardというファイルをください」と解釈します。しかしそのファイルは存在しません。
結果:
404 Not Found
これは正しい動作です。サーバはルーターを知らないからです。SPAのルーティングはJavaScriptが起動して初めて機能します。しかし404が返された時点で、JavaScriptはまだ実行されていません。
ここが404問題の本質です。
なぜリンククリックでは動くのか
リンククリック時に動く理由は、サーバへリクエストが行われていないからです。
SPAのリンクは通常のaタグではありません。ルーターがクリックイベントを横取りしています。
処理の流れ:
- クリック
- JSがイベントを捕捉
- pushState()でURLを書き換え
- 画面を描画
つまりサーバに問い合わせていません。だから404にならないのです。
一方でリロードや直接アクセスでは、必ずサーバへHTTPリクエストが送信されます。その時点でSPAはまだ存在していません。
必要になるサーバ設定の正体
解決策は「全URLでindex.htmlを返す」ことです。存在しないパスでも404にせず、SPAの入口を返します。
Apacheの場合:
RewriteEngine On RewriteBase / RewriteRule ^index\.html$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.html [L]
Nginxの場合:
location / { try_files $uri $uri/ /index.html; }
これで /dashboard にアクセスしても index.html が返ります。その後JavaScriptが起動し、ルーターが画面を表示します。
つまりサーバ設定は「SPAの代わりに入口を返す」ために必要です。
よくある誤解:historyモードが悪い?
Vue RouterやReact Routerのhistoryモードを使うと404が出ると言われますが、モードの問題ではありません。
- hashモード:/#/dashboard
- historyモード:/dashboard
hashはサーバに送られないため404にならないだけです。本質的な解決ではありません。
historyモードはURLを本来の形に戻しただけです。問題を表面化させただけとも言えます。
もう一つの問題:APIの404を壊す危険
ここで注意が必要です。全てのURLをindex.htmlへリダイレクトすると、APIまで壊れます。
例:
- /api/users
これもindex.htmlが返ってしまいます。結果、フロントエンドはJSONの代わりにHTMLを受け取り、不可解なエラーになります。
そのためAPIパスは除外します。
location /api/ { proxy_pass http://backend; } location / { try_files $uri $uri/ /index.html; }
SPA設定で一番多いトラブルはここです。404対策をしたらAPIが壊れる、という現象です。
SEOやSSRとの関係
この問題はSSRでは発生しません。理由は簡単です。SSRではサーバ自身がルーティングを理解しているからです。
SPAは「ブラウザがルーター」、SSRは「サーバがルーター」です。
つまりSPAの404問題は、SPAが間違っているのではなく「HTTPはファイル取得のプロトコル」であることとのズレから生まれています。
結局どう考えるべきか
SPAを公開するというのは、フロントエンドのアプリを配布しているのではありません。サーバに仮想的な入口を用意するという作業を含みます。
SPAはブラウザの中にWebサイトを作る技術です。しかしユーザーは最初に必ずサーバへアクセスします。この1回目の橋渡しを担当するのがサーバ設定です。
404が出るのは設定ミスではなく、HTTPが正常に動いている証拠です。SPAはその挙動を前提として設計されています。サーバとブラウザの役割の境界を理解すると、この問題は不具合ではなく「設計上の必然」に見えてきます。