S3のリダイレクト機能を改めて整理してみた

S3のリダイレクト機能について、ウェブサイトエンドポイント自体にリダイレクト設定を行う方法、ウェブサイトホスティングをしながらリダイレクトルールを使って設定する方法、オブジェクト自体にリダイレクトを設定する方法、3つをまとめてみました。
2021.07.31

はじめに

清水です。AWSのオブジェクトストレージサービスであるAmazon S3、静的ウェブサイトホスティング機能を有効にすることでリダイレクト機能も使用することができます。このリダイレクト機能ですが、個人的なイメージとしてZone Apexなドメインexample.comをwww付きのドメインwww.example.comにリダイレクトする(つまりドメインAから別のドメインBにリダイレクトする)のに便利だよね、ぐらいの大まかな印象でおりました。最近はALBのリダイレクト機能を使用することも多かったのですが(固定IPアドレスでフルマネージドなリダイレクト環境をAWSで構成する | DevelopersIO)、その際の設定項目と比較してS3のリダイレクト機能ではこれはできたっけ?と思うこともあり、今回改めてS3のリダイレクト機能を整理してみようと思います。

S3のリダイレクト機能

まず前提として、S3のリダイレクト機能は静的ウェブサイトホスティング機能のオプションとして利用可能です。この静的ウェブサイトホスティング機能を有効化した場合の特徴や、HTTPS対応などを本節ではまとめます。また本エントリではS3のリダイレクト機能について、ユーザガイドの項目をもとに以下3つに分類します。((オプション) ウェブページリダイレクトの設定 - Amazon Simple Storage Service)これらそれぞれの詳細について次節以降でそれぞれまとめています。

  • S3ウェブサイトエンドポイントへのリダイレクト設定
  • リダイレクトルールを使った設定
    • 静的ウェブサイトホスティング設定中のS3バケットに対して、リダイレクトルールを使って多種多様なリダイレクトを設定することができます。ルールにマッチしないオブジェクトへのリクエストに対しては、通常の静的ウェブサイトホスティングとしてレスポンスが返ります。S3マネジメントコンソール上では Bucket Hosting として表示されます。ユーザガイドでは「高度な条件付きリダイレクトを使用するようにリダイレクトルールを設定する」に該当します。
  • オブジェクト単位でのリダイレクト設定
    • 個々のS3オブジェクトごとにリダイレクトの設定を行います。設定を行っていないオブジェクトは通常の静的ウェブサイトホスティングとして利用可能です。こちらもS3マネジメントコンソール上では Bucket Hosting として表示されます。ユーザガイドでは「オブジェクトのリクエストをリダイレクトする」に該当します。

リダイレクト機能は静的ウェブサイトホスティング機能のオプション

前提として、S3のリダイレクト機能は静的ウェブサイトホスティング機能のオプションとして利用可能です。そのため静的ウェブサイトホスティング機能の特徴を把握しておく必要があります。メリット面なども多くありますが(Amazon S3 + Amazon CloudFrontでWebサイトを構築する際にS3静的Webサイトホスティングを採用する理由 | DevelopersIO)、注意点としては、静的ウェブサイトホスティングのエンドポイントがHTTPでのみしか提供されていないこと、またCloudFront連携した場合にもOAIが利用できないため静的ウェブサイトホスティングのエンドポイントへのアクセスが完全には防げないこと、などが挙げられます。後者はS3バケットの名称をわかりにくものにする、などの対応は可能ですが、アクセスできる状態にあることが問題になる場合などは注意です。もしCloudFrontからリダイレクト環境となるオリジンへの通信もHTTPSとしたい、といった場合にはALBのリダイレクト機能を使う、などの必要があるかと思います。Lambda@Edgeでリダイレクトさせる、ということもできそうですね。(Lambda@Edgeを使ってリファラによってリダイレクトさせる | DevelopersIO

HTTPS利用の場合はCloudFrontと連携

リダイレクト機能を使うための静的ウェブサイトホスティング機能の特徴として、エンドポイントがHTTPでのみしか提供されていないことを挙げました。(個人的に少し勘違いしていたのですが、独自ドメインのHTTPSがサポートされていない、ではなく、静的ウェブサイトホスティングのエンドポイントのHTTPS自体がサポートされていません。)そのため、HTTPS利用の場合はCloudFrontと連携します。S3の静的ウェブサイトホスティング利用の場合の鉄板パターンですね。独自ドメインも利用可能です。本エントリではCloudFrontとの連携部分や割愛し、リダイレクトの挙動確認もS3の静的ウェブサイトホスティングのエンドポイントを使って行います。実際のCloudFrontとの連携については以下などを参照ください。

なお、後述しますがリダイレクトの際にパス情報のほかクエリ文字列の情報もLocationヘッダに付与することが可能です。この場合、CloudFrontからみてオリジンとなるS3にクエリ文字列を転送する必要がある点に注意しましょう。

本エントリで利用するS3バケット共通の設定

以下、本エントリの検証にあたっては、S3バケットのブロックパブリックアクセス設定を無効にし、以下形式のバケットポリシーを設定しています。(バケット配下の任意のオブジェクトが公開される設定となります。)またS3バケットはすべて東京リージョンに作成しました。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::Bucket-Name/*"
            ]
        }
    ]
}

S3ウェブサイトエンドポイントへのリダイレクト設定

まずはS3ウェブサイトエンドポイントへのリダイレクト設定を確認します。冒頭に述べたような、Zone Apexドメインexample.comへのアクセスをwww.example.comにリダイレクトする、というシンプルなリダイレクトが実現可能です。S3のリダイレクト機能でも最もオーソドックスなものではないでしょうか。

後述しますが、リダイレクトの際にはリクエスト時のパスやクエリ文字列もLocationヘッダに含まれます。例えばこれまでexample.comで運用していたページをなんらかの事情でwww.example.comに移管したとします。リダイレクト設定を行い新しいドメインにユーザを誘導したいのですが、可能であればパスやクエリ文字も含めてリダイレクトしたい、という場合に利用できます。

他2つのリダイレクト設定とは異なり、静的ウェブサイトホスティングの際に Redirect request という項目で設定するので、本エントリでもこれにならいRedirect requestと記してみたいと思います。

Redirect requestを設定してみる

実際にS3バケットに対して、このウェブサイトエンドポイントへのリダイレクト設定(Redirect request)行ってみます。S3バケットの中身は空(オブジェクトが存在しない)状態です。

S3マネジメントコンソールで該当のS3バケットを選択、Propertiesの項目を選択します。

一番下まで画面をスクロールすると、Static website hostingの項目があります。デフォルトではDisabledになっているので[Edit]ボタンで設定していきます。

Static website hostingでEnableを選択、Hosting typeでRedirect request for an objectを選択します。新たに現れるHost nameの項目にリダイレクト先となるドメイン名を指定します。今回はwww.example.comとしました。Protocolの項目ではhttpsを選択しておきます。

設定後の画面です。以下のようにウェブサイトホスティング用のエンドポイントが表示されます。ドメイン名はバケット名.s3-website-ap-northeast-1.amazonaws.comという形式となります。(なおこの形式については、リージョンごとでs3-website-Regionとなるかs3-website.Regionとなるかの違いがあるようです。詳細はユーザガイドや実際のマネジメントコンソールなどの表示を確認してください。)

実際にリダイレクトのレスポンスを確認してみます。

% curl -I http://s3-redirect-1-XXXXXX.s3-website-ap-northeast-1.amazonaws.com
HTTP/1.1 301 Moved Permanently
x-amz-id-2: xRr/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: 71HYxxxxxxxxxxxx
Date: Sat, 31 Jul 2021 04:04:25 GMT
Location: https://www.example.com/
Server: AmazonS3
Content-Length: 0

ステータスコードとして301 Moved Permanentlyがレスポンスとして返り、別ドメインにリダイレクトされるという挙動です。Locationヘッダにはリダイレクト先となる、指定したドメインが入ります。また先ほどProtocolでhttpsを指定したとおりhttpsプロトコルとなりリダイレクト先はhttps://www.example.com/となります。

なお、Protocolでhttpを選べばリダイレクト先(Locationヘッダの値)はhttp://www.example.com/となりました。noneを選択したときの挙動も同様でした。また、上記curlコマンドの例ではレスポンスヘッダのみ示していますが、レスポンスボディは特にありません。Content-Lengthヘッダの値も0となっていますね。

パスとクエリ文字列の扱い

続いてパスとクエリ文字列の扱いについて確認します。結論としてはこのRedirect requestのリダイレクト設定では、リクエスト時のパスとクエリ文字列がレスポンスのLocationヘッダに追加されて返ってきます。

 % curl -I "http://s3-redirect-1-XXXXXX.s3-website-ap-northeast-1.amazonaws.com/path/to/file.html?query=string&key=value"
HTTP/1.1 301 Moved Permanently
x-amz-id-2: ZiPLxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: 9562xxxxxxxxxxxx
Date: Sat, 31 Jul 2021 04:23:43 GMT
Location: https://www.example.com/path/to/file.html?query=string&key=value
Server: AmazonS3
Content-Length: 0

実は個人的に、「あれ?どうだっけ?」と思っていたのはこの点でした。ALBのリダイレクト機能の以下の設定状況と同様の挙動かと思います。

リダイレクト先にパスを追加できる

確認しながら気になったこととして、リダイレクト先のHost nameにパスは追加できるのか?という点があります。以下のような設定ですね。

このまま[Save changes]で設定できたので、挙動を確認してみます。以下のようにパスを追加したかたちでLocationヘッダが返ってきました。

% curl -I http://s3-redirect-1-XXXXXX.s3-website-ap-northeast-1.amazonaws.com
HTTP/1.1 301 Moved Permanently
x-amz-id-2: Wdx5xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: S07Qxxxxxxxxxxxx
Date: Sat, 31 Jul 2021 04:25:36 GMT
Location: https://www.example.com/path/
Server: AmazonS3
Content-Length: 0

なお、リクエストURLにパスを追加すると、さらにパスが加わる具合です。

% curl -I http://s3-redirect-1-XXXXXX.s3-website-ap-northeast-1.amazonaws.com/path/to/file.html
HTTP/1.1 301 Moved Permanently
x-amz-id-2: RSxwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: HJA5xxxxxxxxxxxx
Date: Sat, 31 Jul 2021 04:27:26 GMT
Location: https://www.example.com/path/path/to/file.html
Server: AmazonS3
Content-Length: 0

S3ウェブサイトエンドポイントへのリダイレクト設定はブロックパブリックアクセスが有効でも機能する

Edit static website hostingの項目を設定中に気がついたのですが、Hosting typeがHost a static website (Bucket Hosting)の場合だと、「パブリックアクセス可にする必要がある」とインフォメーションが現れますが、Redirect requests for an object (Redirect request)の場合はインフォメーションが現れません。

これはもしや、S3でパブリックアクセス不可、ブロックパブリックアクセスを有効にした状態でも、静的ウェブサイトホスティングを有効にしていればエンドポイントのリダイレクト設定は機能するのかな、と思ったらその通りでした。(具体的にはブロックパブリックアクセスを有効、バケットポリシーが空の状態で確認しています。あくまで個々のオブジェクトにアクセスしているわけではなく、静的ウェブサイトホスティングのエンドポイントで処理している、ということかと推測します。

リダイレクトルールを使った設定

続いてリダイレクトルールを使った設定です。先ほどのS3ウェブサイトエンドポイントへのリダイレクト設定ではバケット全体で一つのリダイレクト設定を行う具合でしたが、リダイレクトルールを使うと条件に応じたきめ細やかなリダイレクトの設定が可能です。

例えばリダイレクト先のドメインではパスやクエリ文字列の扱いが異なるため、example.comドメインへのアクセスを一括して(どんなパス、クエリ文字列がついていたとしても)https://www.example.com/にリダイレクトさせたい、という設定が行えます。またステータスコードを301ではなく302としたい、といった変更も可能です。これらは先ほどのS3ウェブサイトエンドポイントへのリダイレクト設定では行えなかったことですね。そのほかにも、特定のパスを同じドメイン内の別のパスにリダイレクトさせたり、404エラーが発生した場合にリダイレクトさせる、といったことが可能です。より詳細な設定項目についてはユーザガイドを参照ください。(「高度な条件付きリダイレクトを使用するようにリダイレクトルールを設定する」 (オプション) ウェブページリダイレクトの設定 - Amazon Simple Storage Service

リダイレクトルールは多様なリダイレクト設定が行えるため、先ほど確認したS3ウェブサイトエンドポイントへのリダイレクトと同等のリダイレクト設定も実現可能です。(パスやクエリ文字列情報がリダイレクト先に引き継がれるパターン)後述するオブジェクト単位でのリダイレクト設定と同様なことも実現可能かと思います。多様な設定が行える分、リダイレクトルールをきちんと理解して記述する必要があります。なおこのリダイレクトルールの記述方法ですが、従来はXML記法でした。最新のユーザガイドを確認するとS3マネジメントコンソールからの設定ではJSON記法で記述する方式になったようです。(きちんと確認はしていませんが、おそらくCORS指定の記法がJSONに変わったタイミングと同じかなと推測しています。AWSマネジメントコンソールからのAmazon S3のCORS設定方法がJSON記法になっていました | DevelopersIO

それでは実際にS3バケットに設定してみます。設定箇所は先ほどと同様の場所です。Hosting typeで今回はHost a static websiteを選択します。(今回、試してみる構成とは異なりますが、特定のパスのみリダイレクトを行い他のパスでは通常の静的ウェブサイトホスティングを行う、という構成が可能です。そのため、バケット全体がリダイレクト設定となるRedirect requestの設定ではなく、あくまで静的ウェブサイトホスティングの設定、Bucket Hostingなのかなと思います。)

Index documentとError documentを適切に入力して(Index documentは指定必須です)、Redirection rulesを入力します。

Redirection rulesは以下の内容を入力しました。(リダイレクト先はnew.example.comとしています。)

[
    {
        "Redirect": {
            "HostName": "new.example.com",
            "HttpRedirectCode": "301",
            "Protocol": "https",
            "ReplaceKeyWith": ""
        }
    }
]

実際にcurlコマンドで確認してみます。まずはパスやクエリがないパターンです。意図したとおり、https://new.example.com/への301リダイレクトとなっています。

% curl -I http://s3-redirect-2-XXXXXX.s3-website-ap-northeast-1.amazonaws.com
HTTP/1.1 301 Moved Permanently
x-amz-id-2: 9WCnxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: 1VVExxxxxxxxxxxx
Date: Sat, 31 Jul 2021 04:50:09 GMT
Location: https://new.example.com/
Server: AmazonS3
Content-Length: 0

続いてパスを追加してみます。パスを省いたかたちのLocationヘッダが返ります。

% curl -I http://s3-redirect-2-XXXXXX.s3-website-ap-northeast-1.amazonaws.com/path/to/file.html
HTTP/1.1 301 Moved Permanently
x-amz-id-2: j+G+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: HDDJxxxxxxxxxxxx
Date: Sat, 31 Jul 2021 04:52:44 GMT
Location: https://new.example.com/
Server: AmazonS3
Content-Length: 0

パスに加えてクエリ文字列も追加してみます。こちらも省いたかたちのLocationヘッダが返ってきましたね。

% curl -I "http://s3-redirect-2-XXXXXX.s3-website-ap-northeast-1.amazonaws.com/path/to/file.html?query=string&key=value"
HTTP/1.1 301 Moved Permanently
x-amz-id-2: zPo1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: F419xxxxxxxxxxxx
Date: Sat, 31 Jul 2021 04:53:28 GMT
Location: https://new.example.com/
Server: AmazonS3
Content-Length: 0

繰り返しになりますが、リダイレクトルールではこの他にもいろいろなリダイレクト設定が可能です。以下エントリなどもあわせてご参照ください。

オブジェクト単位でのリダイレクト設定

S3の静的ウェブサイトホスティングではオブジェクト単位でリダイレクト設定を行うことも可能です。例えばexample.comでS3の静的ウェブサイトホスティングを使ったサイトを運用していたとします。example.com/specialpage.htmlというページがこれまでもありましたが、このページを新しくspecialpage.example.comという別のドメインで運用をはじめるためリダイレクトしたい、という場合に使用できます。その他、別ドメインでなく同じS3バケット内の別オブジェクトにリダイレクトする、といケースにも利用可能です。(こちらの例はユーザガイドを参照ください。)

実際に上記の例の挙動を設定して確認してみます。他の手順と同様に静的ウェブサイトホスティングを有効にしたS3バケットを準備します。Redirect requestの設定はせずBucket Hostingの設定とし、またリダイレクトルールは設定していない状態です。このS3バケットに、specialpage.htmlをアップロードしておきます。

% curl -i http://s3-redirect-3-XXXXXX.s3-website-ap-northeast-1.amazonaws.com/specialpage.html
HTTP/1.1 200 OK
x-amz-id-2: BsACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: 445Xxxxxxxxxxxxx
Date: Sat, 31 Jul 2021 05:05:57 GMT
Last-Modified: Sat, 31 Jul 2021 05:05:15 GMT
ETag: "7a075b3dac882740335ebbff1d01aab5"
Content-Type: text/html
Server: AmazonS3
Content-Length: 111

<html>
  <head>
    <title>special page</title>
  </head>
  <body>
    This is special page.
  </body>
</html>

マネジメントコンソールからこのオブジェクトspecialpage.htmlのPropertiesの項目を確認します。

ページ最下部のほうまでスクロールするとMetadataという項目があるので、[Edit]ボタンで進めます。

Metadataの項目で[Add metadata]からデータを1つ追加します。TypeではSystem definedを選択します。Keyではx-amz-website-redirect-locationを選択し、Valueにリダイレクト先のURLを入力します。今回はhttps://specialpage.example.com/と入力しました。

以下が設定後の状態です。

設定後、このオブジェクトを参照してみます。以下のように301 Moved Permanentlyのリダイレクト用レスポンスが返りました。Locationヘッダは先ほど設定したhttps://specialpage.example.com/となっています。Content-Lengthヘッダも0となり、これまであったコンテンツを返さず、リダイレクト用の情報のみを返していることがわかります。

% curl -i http://s3-redirect-3-XXXXXX.s3-website-ap-northeast-1.amazonaws.com/specialpage.html
HTTP/1.1 301 Moved Permanently
x-amz-id-2: csvCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: B8YExxxxxxxxxxxx
Date: Sat, 31 Jul 2021 05:18:30 GMT
Location: https://specialpage.example.com/
Server: AmazonS3
Content-Length: 0

リダイレクトルールを使用しても同様の設定は実現できるかと思いますが、リダイレクトさせるパスが1つと決まっており、リダイレクト先なども上記要件にマッチするのであれば、リダイレクトルールよりもシンプルに設定できるのかなと思います。ただし、リダイレクトの設定は各オブジェクトのメタデータを参照して確認する必要がある点には注意しましょう。(設定するオブジェクトの数が多くなった場合、きちんと管理していないと確認などで苦労しそうな印象です。)

なお、このオブジェクトに(静的ウェブサイトのエンドポイントではなく)REST APIエンドポイントでアクセスすると上記設定を行った状態でオブジェクトの内容の参照が可能でした。静的ウェブサイトのエンドポイントにアクセスした場合、該当オブジェクトにx-amz-website-redirect-locatioのメタデータがあるか確認、あった場合はリダイレクトのレスポンスを返す、という挙動でしょうか。この点が気になる場合は0バイトのオブジェクトとしてspecialpage.htmlをアップロードし直し、そちらにMetadataを設定するようにしましょう。

 % curl -i http://s3-redirect-3-XXXXXX.s3.ap-northeast-1.amazonaws.com/specialpage.html
HTTP/1.1 200 OK
x-amz-id-2: sa9Sxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
x-amz-request-id: A016xxxxxxxxxxxx
Date: Sat, 31 Jul 2021 05:27:03 GMT
Last-Modified: Sat, 31 Jul 2021 05:17:28 GMT
ETag: "7a075b3dac882740335ebbff1d01aab5"
x-amz-website-redirect-location: https://specialpage.example.com/
Accept-Ranges: bytes
Content-Type: text/html
Server: AmazonS3
Content-Length: 111

<html>
  <head>
    <title>special page</title>
  </head>
  <body>
    This is special page.
  </body>
</html>

まとめ

Amazon S3のリダイレクト機能について、ウェブサイトエンドポイント自体にリダイレクト設定を行う方法、ウェブサイトホスティングをしながらリダイレクトルールを使って設定する方法、オブジェクト自体にリダイレクトを設定する方法、3つをまとめてみました。もともとS3でウェブサイトホスティングをしていない状況で、冒頭で述べたようなZone Apexなドメインexample.comをwww付きのドメインwww.example.comにリダイレクトする、といった別ドメインにリダイレクトするようなシンプルなパターンでは、まずS3ウェブサイトエンドポイントへのリダイレクト設定を検討しましょう。もしパスやクエリ文字列を省いたかたちでリダイレクトさせたい、といった場合にはリダイレクトルールを使うことを検討します。またS3でウェブサイトホスティングを行っていて、特定のオブジェクトでリダイレクトさせたい場合は、まずオブジェクト単位でのリダイレクト設定を検討するとシンプルかと思います。その他より複雑な条件でのリダイレクトにはリダイレクトルールを使用、といった使い分けになります。個人的に、今回S3のリダイレクト機能をまとめるにあたって大まかな印象がだいぶクリアになりました。と同時に、S3のリダイレクト機能が多機能であることと、また単に「リダイレクト」といっても様々な条件があるよなぁと考えさせられたしだいです。