概要
Next.jsでSSGした静的サイトをS3にデプロイする場合、基本的には next export
の結果をバケットに入れれば良いんですが、何も考慮せずにただそうすると、CSRしたページでリロードした際に404になります。
他のAWSサービスの導入は管理コスト増加の観点で避けたく、ここではS3とシェルスクリプトだけでなんとかする方法を共有します。
(ただし、シェルスクリプトはawsコマンドに依存しています)
S3デプロイ時に起きる問題
(実際はS3以外のホスティングサービスでも起きますが)
next export
はページを .html
の拡張子つきで書き出す(pages/hoge.tsx
であればout/hoge.html
のように)のですが、何も考慮しないとURLが https://hoge.com/hoge.html
のように、 .html
つきになってしまいます。
これ何が問題かというと、該当ページにNextのCSRをするとURLは https://hoge.com/hoge
(.html
なし)になるので、この状態でリロードすると404になってしまうのです。
脱線だけど
Amplifyでデプロイするとこの問題はおきません。
Amplifyを使うことに懸念が無いのならオススメします。
Next.jsでSSGしたサイトをAmplifyでホスティングする - Qiita
対策
-
next export
で出来る.html
なファイルから拡張子を取り除く。 - S3に配置した後に、拡張子を取り除いたHTMLファイルに
content-type: "text/html"
を付与する。
これらを手で行いたくないので、シェルスクリプトを書きます。
1. next export
で出来るHTMLファイルから拡張子を取り除く。
そもそもCSRが.html
をつけないのなら、ファイル名から.html
を削れば良いですよね。
でも、index.htmlに関してはどちらにしろURLに含まれないし、削らなくていっか。ということで
next export
html_filepaths=$(find ./out -name "*.html" ! -path "*/index.html")
for filepath in $html_filepaths; do
mv $filepath ${filepath%\.html}
done
こんな感じで、index.html
以外のxxx.html
ファイルから拡張子を消します。
(next export
の出力先がout/
の場合)
2. S3に配置した後に、拡張子を取り除いたHTMLファイルに content-type: "text/html"
を付与する。
ただし、.html
を削ったファイルには、Content-Typeヘッダがつきません。
そうすると、ブラウザでそこにアクセスした際にそれがHTMLだと認識されず、ブラウザのダウンロードが実行されてしまいます。
なので、上で拡張子を削ったHTMLファイル(S3のオブジェクト)全てに、Content-Type: "text/html"
をつけます。
# 拡張子を削ったファイルのファイル名を回して、S3上のオブジェクトに対してContent-Typeを付与する
for filepath in $html_filepaths; do
path=${filepath#\.\/out\/}
key=${path%\.html}
aws s3api copy-object \
--bucket $bucket_name \
--copy-source $bucket_name/$key \
--key $key \
--metadata-directive "REPLACE" \
--content-type "text/html"
done
ここでaws
コマンドを使っているので無い場合はインストール必要です。
CSR後リロード時404問題についてはこれで解決です。
デプロイスクリプト
上記の対策込みのデプロイスクリプトはこんな感じです。
実際は環境の振り分けとかその他細かい処理がもっとあるけどその辺はここでは省いてます。
# next exportの実行
npm run export
# 趣旨と関係ないけど、これら消さずにS3に上げるとブラウザからダウンロードできちゃうので消す
find ./out -name ".DS_Store" | xargs rm
find ./out -name ".keep" | xargs rm
# .html なファイルから拡張子を取り除く
html_filepaths=$(find ./out -name "*.html" ! -path "*/index.html")
for filepath in $html_filepaths; do
mv $filepath ${filepath%\.html}
done
# S3にsync(アップロード)
$bucket_name=hoge_bucket
aws s3 sync ./out/ s3://$bucket_name/ --delete
# Content-Type付与する
for filepath in $html_filepaths; do
path=${filepath#\.\/out\/}
key=${path%\.html}
aws s3api copy-object \
--bucket $bucket_name \
--copy-source $bucket_name/$key \
--key $key \
--metadata-directive "REPLACE" \
--content-type "text/html"
done
おしまい
ページが何階層もあるサイトで試してないので、ダメだったらいい感じに直してください🚀
追記
続きとしてこちらの末尾スラッシュ404問題への対応があります。
Next.jsのSSGサイトをS3に静的ホスティングした際に起きる末尾スラッシュ404問題を低コストで解決した - Qiita