カテゴリー
[Lambda@Edge x ウェブサイト運用] AWS S3で静的ホスティングしたウェブサイトをレスポンス400~500番台の時にエラーページへ誘導する
※ 当ページには【広告/PR】を含む場合があります。
2021/04/21
AWS CloudFrontを介してウェブサイトを公開していると、存在しないページにブラウザからアクセスした際に、以下のような症状がみられると思います。

通常のWebサーバーを利用したウェブサイトであれば、存在しないページにアクセスがあった場合、きちんとクライアント側にエラーページを送り返す仕組みが備わっていますが、AWS CloudFrontを利用したなんちゃってウェブサイトはCDN方式でのファイル配信を行うだけなので、当然何もしないとエラーページなんて返してくれません。
ではどうするかというと、前の回でも解説をしていた
前回の記事のこのLambda@Edgeの使い方だと、HTTPリクエストの情報を変形してサーバー側に送りだしていたわけですが、今回は、サーバー側からのHTTPレスポンスの情報を読み取って、エラーページにリダイレクトさせる方法を紹介します。
HTTPレスポンスエラー?
ブラウザからだとクライアントの受けとるレスポンスヘッダーが分かりにくいので、Curlでウェブサイトにアクセスしてみましょう。
まずAWS S3バケットにちゃんとindex.htmlが存在していて、CloudFrontが絶賛配信中のページにアクセスしてみると、
$ curl -I https://deep.tacoskingdom.com/blog/85
HTTP/2 200
content-type: text/html
content-length: 867893
date: Wed, 21 Apr 2021 03:31:02 GMT
last-modified: Wed, 21 Apr 2021 02:53:25 GMT
etag: "6d0db1bf6c10f5cb07c18c87a6f8e47e"
cache-control: no-cache, no-store
content-encoding: br
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 853dab48fd1de187261c15f5b98cd2a0.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT20-C4
x-amz-cf-id: 76TktFSL4CqQ5a3roVMpFyYB1npWVb8kimKz9tO7bTCLL7rzb1zAsQ==
というように、HTTPレスポンスステータスコードが200(リクエストが正常に終了した)ことが分かります。
対して、存在していないページにアクセスすると、
$ curl -I https://deep.tacoskingdom.com/hoge
HTTP/2 403
content-type: application/xml
date: Wed, 21 Apr 2021 03:34:01 GMT
server: AmazonS3
x-cache: Error from cloudfront
via: 1.1 89d55be039a98056c94d7056281033e7.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT20-C4
x-amz-cf-id: dtu8tCWT0Run9IjRrQgkfRhvmTHub-F-nchXTMOqc-z4kQqsliCDdQ==
というように403(サーバーから適切なレスポンスの返信が拒否されている)ステータスコードが跳ね返ってきます。
このようにサーバー側で問題が起こった場合の400〜500番台のHTTPレスポンスステータスコードが返されるわけです。
ではどのようにしてこのエラーステータスがサーバー側から返されたときにエラーページに遷移させてクライアントに返すのかを以降で考えてみます。
困ったときのLambda@Edge
以前、301&302ステータスコードをLambda@Edgeで処置する方法を以下のブログで紹介しました。
今回もこの内容と代替同じような考え方で、
Lambda@Edge
早速、Lambdaのhandler関数を以下のようなソースコードで新しいLambda関数を作成してみます。
なお
exports.handler = async (event, context, callback) => {
const response = event.Records[0].cf.response;
if (response.status >= 400 && response.status <= 599) {
console.log('+++ Redirect To ERROR Page +++');
response.status = 302;
response.statusDescription = 'Found';
response.body = '';
response.headers['location'] = [{ key: 'Location', value: '/error/index.html' }];
//👇①レスポンスヘッダの入れ替えは原則禁止(※後述)
// const redirectRes = {
// status: '302',
// statusDescription: 'Found',
// body: '',
// headers: {
// location: [{
// key: 'Location',
// value: '/error/index.html',
// }],
// },
// };
// return callback(null, redirectRes);
}
return callback(null, response);
};
Lambda@Edgeをデプロイする上で、最大のつまづきポイントになるのが、Lambda関数がトリガーされるイベントの設定です。
+ Origin Requestタイプ:
リクエストをオリジン(サーバー側)へ転送する直前
+ Origin Responseタイプ:
オリジン(サーバー側)からレスポンスが到着した直後
+ Viewer Requestタイプ:
ビュアー(クライアント側)からリクエストが到達した直後
+ Viewer Responseタイプ:
ビュアー(クライアント側)へレスポンスを転送する直前
これを適切に設定していないと、
503 ERROR The request could not be satisfied.
今回のステータスコードエラーを判定してエラーページを跳ね返すのは、
Origin Response

さて、出来たばかりのLambda関数はまだリージョン間でリソースの参照できない状態になっている場合がありますので、メトリクスなどのエラーを監視できるようになるまでに最大24時間程度待機する必要があります。
できたての稼働状態がどうなっているかというと、CloudWatch辺りを確認して、メトリクス監視が有効になっているかどうかで判断してみてください。

Lambda@Edgeのデプロイが完了し、リダイレクト機能が正常に働いていたら、htmlの存在しないページにブラウザからアクセスしてみますと、

ちなみにCurlでは、
$ curl -I https://deep.tacoskingdom.com/hoge
HTTP/2 302
content-length: 0
server: CloudFront
date: Wed, 21 Apr 2021 05:20:58 GMT
location: /error/index.html
x-cache: Hit from cloudfront
via: 1.1 77ffb7fa0ceed0e909a8f69baef40302.cloudfront.net (CloudFront)
x-amz-cf-pop: NRT20-C4
x-amz-cf-id: S3CMArnb8i-eh213_l2W5pSt_8PV0_e7QzQbokiwnT9AXQnh7Tkvbg==
age: 14352
ちゃんと302リダイレクトも出来ていることが分かります。
これでCloudFrontからでも問題なくエラーページに飛ばすことが可能となりました。
よくあるエラー〜Readonlyのレスポンスヘッダは書き換えられない
Lambda@Edgeで選択したトリガーイベントタイプによっては、書き換えられないプロパティを持っていて、それをうっかり書き換えてしまうと、以下のようなエラーが起こります。 このエラーはステータスコードは502で警告されることに注意しましょう。

この原因としては、先程のソースコード内でコメントアウトして残していた①の部分のように、レスポンスのオブジェクト全体を置き換えてしまったために起こったエラーのようです。
レスポンスヘッダを書き換えるときは、Readonlyではないプロパティ値のみを置き換えるようにコーディングしましょう。
まとめ
特にエラーページはSEO対策としても薄いですし、要らないと言えば要らないのですが、こだわりのエラーページなどを実装する場合に使えるテクニックです。
覚えておくと、何かの時に役に立ちそうな気がします。
参考サイト
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー