# 生成された静的ページを配信するときの Tips
静的サイトジェネレータ(Gatsby, VuePress, Next.js, Nuxt.jsなど)で生成された静的ページを S3 などのホスティング用ストレージで配信するときの注意
# 1. パスや index.html の補完
生成されるWebページはビルド時の設定などによるが hoge/fuga.html
だったり、piyo/index.html
といったパスとファイルになるのが一般的。
このとき、例えば以下のように正確ではないパスにブラウザからアクセスが有ったときもそれをサーバサイドで補完して配信できるようにしたい。
URLのパス | 補完後のパス |
---|---|
hoge/fuga | hoge/fuga.html |
piyo/ | piyo/index.html |
手元で開発用のサーバで動作確認をしているときは、勝手にサーバプロセスが補完してくれることがあるが、実際に静的ページを生成してストレージにデプロイしたときはそのストレージサービスの機能を利用しないと補完できない。
# S3 の静的Webサイトホスティング
静的Webサイトホスティングの機能ではインデックスドキュメントの設定で index.html の補完はできる。 また、パスの末尾が「/」じゃないときは、「/」をつけた URL へのリダイレクトが返されるため、結果として「/index.html」が補完される。
「/hoge」 --(リダイレクト)--> 「/hoge/」 --(補完)--> 「/hoge/index.html」
# CloudFront × Lambda@Edge
CloudFront にもルートオブジェクトという機能があって、S3 のインデックスドキュメントと類似する機能があるが、ルートパス(/)にアクセスしたときにしか補完してくれないため使いづらい。
CloudFront では Lambda@Edge を利用することで Lambda 関数によってリクエストのパスを書き換えることができる。例えば以下のような関数を書くことで、「/」や「index.html」の補完は実現できる。この suzutomo.dev でも Lambda@Edge を利用して URL を補完している。
'use strict';
const path = require('path');
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
let request_path = request.uri
if(request_path[request_path.length - 1] === '/') {
// 末尾が「/」ならindex.htmlを追加
request_path = request_path + 'index.html';
} else if(request_path[request_path.length - 1] !== '/' && path.extname(request.uri) === '') {
// 末尾が「/」以外でファイルでもなければ末尾に「.html」を追加
request_path = request_path + '.html';
}
request.uri = request_path;
callback(null, request);
};
# 2. 直アクセスとページ遷移時のリクエストの違い
ジェネレータで生成された静的ページでは、アクセスの仕方によって発生するリクエストに以下のような違いがある。
# 直アクセス(アドレスバーのURL入力や外部ページからの遷移)
直アクセスでは指定されたパスに対して HTTP のリクエストが実行され、HTML などが配信される。 その後 CSS, JS などが読み込まれ、以降のサイト内のページ遷移は SPA として振る舞うことが多い。
↑VuePress で作成されている本サイトの例。アドレスと同じ /Cloud/ にリクエストが発生している。
# SPA 的なページ遷移
前述の直アクセス後のページ遷移では、遷移先のパスにある HTML を読み込むのではなく、遷移先に対応する javascript ファイルなどに HTTP リクエストが発生する。すでに読み込んでいた場合はリクエストが発生しない場合もある。
このときアドレスバーには History APIを利用して、遷移先のパスが表示されているがそのパス自体に HTTP リクエストは発生しない。
↑アドレスは /Cloud/ になっているが、実際には js ファイルにリクエストが発生している。
# 注意点
どちらもアドレスバーには同一のパスが表示されるが、実際に発生しているリクエストが異なるため、CDN などで特定のパスを対象に機能を有効にしていたときにアクセスの違いによってその機能が動作したりしなかったりという状態になる可能性がある点に注意。