AWSでサーバーレスな開発用HTTPSシングルページアプリケーションを立ち上げる方法

## 本記事について
シングルページアプリケーションを作っていて、外部には公開したくないが、ローカル以外のサーバーに置いて配信テストをしたいことがあったので、その時の構築メモです。

## 要件
静的配信だし、S3を使えばサーバー立てなくてもAWSのサービスだけでできそうな感じだったので、下記のようなものを想定しました。

- サーバーレス（AWSのサービスを利用する・インスタンスを立ち上げない）
- httpsで静的配信
- http通信は不可
- キャッシュしない（アップした更新をすぐに確認できるようにするため）
- サブディレクトリによるルーティング可能
- IPアドレスによるアクセス制限

## 手順
## HTTPS静的配信
S3にファイルを置いて、Static website hostingすればいいのかなと考えていたのですが、https配信するにはCloudFrontを使う必要がありました。
また、CloudFrontで配信すれば、S3をStatic website hostingで公開する必要はなかったです。

CloudFrontのDistributionsページから[Create Distribution]-[Web]-[Get Started]と進んで配信を作成します。

### Origin Settings
> Origin Settings
> ![Origin Settings](https://qiita-image-store.s3.amazonaws.com/0/100574/5201beb7-a22c-b0c3-08a7-025f99612a2e.png)

- Origin Domain Name
  - 公開するバケットを選択します
- Origin Path
  - どのサブディレクトリ配下を公開するかを指定します。「/」から始まり、最後の「/」は不要です。
- Restrict Bucket Access
  - CloudFrontを使って公開する場合、S3を公開する必要はありません。「Yes」にします。
- Origin Access Identity
  - Identityが必要なのですが、作成していない場合「Create a New Identity」で作成します。
- Grant Read Permissions on Bucket
  - よしなに権限設定してくれるので「Yes」にします。

### Default Cache Behavior Settings
> Default Cache Behavior Settings (1/2)
> ![Default Cache Behavior Settings (1/2)](https://qiita-image-store.s3.amazonaws.com/0/100574/25eef240-36cc-bc4f-faea-ffa0c00d135f.jpeg)

- Viewer Protocol Policy
  - HTTPS配信のみを行いたいので「Redirect HTTP to HTTPS」にします。

> Default Cache Behavior Settings (2/2)
> ![Default Cache Behavior Settings (2/2)](https://qiita-image-store.s3.amazonaws.com/0/100574/eb768df6-dd47-5ab0-7167-64a10c45ae40.png)

- Object Caching
  - 開発用サイトなので、ファイルをアップしたらすぐに確認したく、キャッシュをしない設定にするため「Customize」を選択します。
  - Minimum TTL・Maimum TTL・Default TTL
     - キャッシュ時間の設定。キャッシュしないので「0」にします.

### Distribution Settings
> Distribution Settings (1/2)
> ![Distribution Settings](https://qiita-image-store.s3.amazonaws.com/0/100574/d65441ba-59cb-eed4-9ebc-11369b51e4bb.png)

- AWS WAF Web ACL
  - ここでIP制限をかけることができます。後からでも設定できるので「None」にしておきます。

> Distribution Settings (2/2)
> ![index_html.png](https://qiita-image-store.s3.amazonaws.com/0/100574/745fe227-f8fc-ddc4-5429-31c00824662a.png)

- Default Root Object
  - シングルページアプリケーションの配信HTMLのファイル名を指定します。

「Create Disctibution」ボタンを押して配信を作成します。
結構時間がかかります。おそらく、想像しているより時間がかかり、動いているのかと途中心配になるかもしれませんが、忘れたころに完了します。

## サブディレクトリによるルーティング
シングルページアプリケーションだと、サブディレクトリでルーティングを行うことがあるかと思います。

初期設定のままだと、サブディレクトリにファイルは無いためエラーがでてしまいます。
なので、エラーではなく配信ページを呼び出すように設定します。

先程作成した配信の詳細を開いて[Error Pages]-[Create Custom Error Response]ボタンを押して、エラー毎の挙動を追加します。

> Custom Error Response
> ![Custom Error Response](https://qiita-image-store.s3.amazonaws.com/0/100574/11bdb823-794a-fca3-dfa9-54ed0fabec8f.png)

- HTTP Error Code
  - 「404」を選択します。
- Customize Error Response
  - エラーページではなく、配信ページを表示するため「Yes」を選択します。
- Response Page Path
  - アプリケーションの配信HTMLファイル名を指定します。
- HTTP Response Code
  - 「200」を選択します。

エラーコード「403」も同様に作成します。

これで、サブディレクトリにアクセスするとエラーではなく、配信ページが呼び出されるようになります。


## ブラウザキャッシュ時間の設定
CloudFrontで設定したキャッシュ時間はCloudFrontのキャッシュ時間で、ブラウザのキャッシュ時間ではありません。
CloudFrontのキャッシュ時間を0にしても、ブラウザでキャッシュが有効になっていると、アクセスしても更新されません。
ですので、別途ブラウザのキャッシュを無効にする必要があります。

ブラウザがそのファイルをどれくらいキャッシュしておくかは、ファイル取得時のヘッダーの「Cache-Control」で判断し、値が「no-chache, no-store」だとブラウザはそのファイルをキャッシュしません。

CloudFrontで「Cache-Control」を設定するには、S3に置かれた大本のファイルの「メタデータ」に設定します。

> S3 メタデータ
> ![S3 メタデータ](https://qiita-image-store.s3.amazonaws.com/0/100574/9e62091a-93b9-23ca-4603-48a31ea7b05c.png)

ただ、これを手動で１ファイル毎に設定するのは手間なので、AWS CLIでファイルをS3にアップする時に、フォルダごと設定するようにしました。

```sh
aws s3 cp --recursive --metadata-directive "REPLACE" \
    --cache-control "no-cache, no-store" \
    $LOCAL_PATH "s3://${UP_PATH}/"
```

##  IPアドレスによるアクセス制限
AWSのWAFでアクセス制限の設定（=ACL）を作成して、それをCloudFrontの配信に割当ます。

配信の詳細で、[General]-[Edit]-[AWS WAF Web ACL]で作成したACLを指定します。

> AWS WAF Web ACL
> ![Distribution Settings](https://qiita-image-store.s3.amazonaws.com/0/100574/d65441ba-59cb-eed4-9ebc-11369b51e4bb.png)

ACLの作成は難しくはないのですが、手順が多いので、ここではIPアドレス制限のACLの作成についてザックリとだけ説明します。

- [IP addresses]で許可するIPアドレスを作成する
- [Rules]で指定IPアドレスにマッチした時のルール「When a request [does] [originate from an IP address in] [上記で作成したIPアドレス]」を作成する
- [ACL]で上記のルールを追加し、「Action」を「Allow」、「Default action」を「Block」とする

以上で、CloudFrontが作成したURLにアクセスすると、S3にアップした「index.html」を起点としたSPAが読み込まれるようになります。

## 元記事
- [AWSでサーバーレスな開発用シングルページアプリケーションを立ち上げる方法](https://www.kwbtblog.com/entry/2018/11/30/010557)