背景

Reactで作成したSPAをECS上のnginxでホスティングしました。

そこで、ローカルとホスティングで動作が異なりました。

最初の画面が/loginというURLで表現されています。

アドレスバーに/loginと表示されてまして、ローカルでリロード(Command+R)すると、同じログイン画面が表示されます。一方、ホスティングしたものは「404 NOT FOUND」が表示されます。

ローカルと同じようにログイン画面を表示したいので、この事象と対応策について調べることにしました。

historyApiFallbackの実装が必要

この事象は「あるある」なようで、historyApiFallbackが必要ということがわかりました。

ローカルで動作確認するときは、npm startで動かしています。このときはwebpack-dev-serverが起動します。このwebpack-dev-serverは、historyApiFallback オプションが有効になっており、たとえば/loginなどのルートにアクセスされた場合でも常にindex.htmlを返すようになっています。ブラウザが受け取るのはアプリケーションのエントリーポイントとなるindex.htmlであり、React Routerなどのクライアント側のルーティングライブラリが正しく動作します。

一方、渡しはDockerにnginxを入れて、実装しました。

nginxは、デフォルトではリクエストされたパスに対応する静的ファイルを探します。 なので、例えばブラウザで/loginに直接アクセスすると、そのパスに対応するファイルが存在しないため、「404 NOT FOUND」エラーが返されるというわけです。

historyApiFallbackとは

で、ここでChat−GPTにhistoryApiFallbackとは何かを改めて聞いてみました。

目的

Q:historyApiFallbackの目的を教えて。

A:存在しないパスへのリクエストがあった場合に、404 エラーを返さず、常に index.html を返すことで、SPA のクライアントサイドルーティングを正しく機能させるための仕組みです。

利用シーン

Q:どんなときに使用するの?

A:直接URLにアクセスしてもアプリが正しく表示されるようにするために使用されます。

実装

nginxのdefault.confを以下のようにします。なお、以下は抜粋です。

server {
    listen       8080;
    listen  [::]:8080;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }
}
fallback

ポイントは以下の部分です。

try_files $uri $uri/ /index.html;
fallback

リクエストされたURIに対してファイルを探す順序を指定しています。まず、リクエストされたURIに対応するファイル ($uri)を探し、次にディレクトリ($uri/)を探し、それらが見つからない場合はindex.htmlを返すという設定です。