【AWS】S3でSSIが利用できるかやってみた

## 0. 目次
[0. 目次](#0-目次)
[1. 何についてまとめたものか](#1-何についてまとめたものか)
[2. 結論](#2-結論)
[3. 検証構成](#3-検証構成)
[4. 設定概要](#4-設定概要)
[5. 確認結果](#5-確認結果)
[6. 参考情報](#6-参考情報)

## 1. 何についてまとめたものか
[「S3を利用して静的コンテンツをホスティングできる」](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/WebsiteHosting.html)ことは有名な話ですが、SSI(*1)のサポート有無について明記されているドキュメントが見つからなかったため、実際に検証した結果をまとめたものです。
 *1:SSI (Server-side include)とは、WebサーバがHTMLに含まれる命令を解釈し、クライアントへ返却する技術です。


## 2. 結論
以下に引用している[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/WebsiteHosting.html)の一文に記載されている通り、S3はサーバーサイドによるコンテンツの動的な処理をサポートしていないため、SSIは利用できませんでした。
※本検証の確認結果は[「こちら」](#5-確認結果)を、サーバーサイドについての詳細は、[「サーバーサイドの概要」](https://developer.mozilla.org/ja/docs/Learn/Server-side/First_steps/Introduction)をご参照いただけますと幸いです。
> Amazon S3 はサーバーサイドスクリプトをサポートしていませんが、AWS には動的ウェブサイトをホストするための他のリソースがあります。

## 3. 検証構成
※1. あくまでS3でのSSIの利用可否を検証したいので、通信の暗号化などの考慮はスコープ外としております。
※2. EC2は挙動差分を確認するために配置しております。
![S3-SSI-arch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959817/6cb23b8b-6c71-0dee-41fe-da81f3fbe69a.png)


## 4. 設定概要
※一部マスキングしております。
<details>
<summary>EC2</summary>

- OSのバージョン
```
# cat /etc/os-release
NAME="Amazon Linux"
VERSION="2"
ID="amzn"
ID_LIKE="centos rhel fedora"
VERSION_ID="2"
PRETTY_NAME="Amazon Linux 2"
ANSI_COLOR="0;33"
CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2"
HOME_URL="https://amazonlinux.com/"
SUPPORT_END="2025-06-30
```
- カーネルのバージョン
```
# uname -r
5.10.228-219.884.amzn2.x86_64
```
- Apache HTTPdのバージョン
```
# httpd -v
Server version: Apache/2.4.62 ()
Server built:   Aug 13 2024 20:16:58
```
- モジュール確認
```
# httpd -M | grep include
 include_module (shared)
```
- SSI設定確認
```
# grep -i shtml /etc/httpd/conf/httpd.conf
    # To parse .shtml files for server-side includes (SSI):
    AddType text/html .shtml
    AddOutputFilter INCLUDES .shtml
```
- 設定ファイル(httpd.conf)の差分
※「httpd.conf_bk」はApache HTTPdインストール時に生成されたファイルをリネームしたものです。
```
# diff -U 13 /etc/httpd/conf/httpd.conf_bk /etc/httpd/conf/httpd.conf
--- /etc/httpd/conf/httpd.conf_bk       2024-12-11 09:14:45.974347742 +0000
+++ /etc/httpd/conf/httpd.conf  2024-12-12 05:31:24.122746669 +0000
@@ -131,27 +131,27 @@
 <Directory "/var/www/html">
     #
     # Possible values for the Options directive are "None", "All",
     # or any combination of:
     #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
     #
     # Note that "MultiViews" must be named *explicitly* --- "Options All"
     # doesn't give it to you.
     #
     # The Options directive is both complicated and important.  Please see
     # http://httpd.apache.org/docs/2.4/mod/core.html#options
     # for more information.
     #
-    Options Indexes FollowSymLinks
+    Options Indexes FollowSymLinks Includes

     #
     # AllowOverride controls what directives may be placed in .htaccess files.
     # It can be "All", "None", or any combination of the keywords:
     #   Options FileInfo AuthConfig Limit
     #
     AllowOverride None

     #
     # Controls who can get stuff from this server.
     #
     Require all granted
 </Directory>
```
</details>

<details>
<summary> CloudFront</summary>

- オリジン、ビヘイビアの設定概要
※その他の設定はデフォルトです。
```
$ aws cloudfront get-distribution --id < CFディストリビューションID >
~ 前略 ~
            "Origins": {
                "Quantity": 1,
                "Items": [
                    {
                        "Id": "< S3バケット名 >.s3.ap-northeast-1.amazonaws.com",
                        "DomainName": "< S3バケット名 >.s3.ap-northeast-1.amazonaws.com",
                        "OriginPath": "",
                        "CustomHeaders": {
                            "Quantity": 0
                        },
                        "S3OriginConfig": {
                            "OriginAccessIdentity": ""
                        },
                        "ConnectionAttempts": 3,
                        "ConnectionTimeout": 10,
                        "OriginShield": {
                            "Enabled": false
                        },
                        "OriginAccessControlId": "< OAI ID >"
                    }
                ]
            },
            "OriginGroups": {
                "Quantity": 0
            },
            "DefaultCacheBehavior": {
                "TargetOriginId": "< S3バケット名 >.s3.ap-northeast-1.amazonaws.com",
                "TrustedSigners": {
                    "Enabled": false,
                    "Quantity": 0
                },
                "TrustedKeyGroups": {
                    "Enabled": false,
                    "Quantity": 0
                },
                "ViewerProtocolPolicy": "allow-all",
                "AllowedMethods": {
                    "Quantity": 2,
                    "Items": [
                        "HEAD",
                        "GET"
                    ],
                    "CachedMethods": {
                        "Quantity": 2,
                        "Items": [
                            "HEAD",
                            "GET"
                        ]
                    }
                },
~ 後略 ~
```

</details>

<details>
<summary>S3</summary>

- 当該S3バケット内のオブジェクト確認
```
$ aws s3 ls < S3バケット名 >
2024-12-11 09:40:19         41 footer.html
2024-12-12 02:17:33        142 hello.html
2024-12-11 09:40:19        142 hello.shtml
```

- バケットポリシー
※CFディストリビューション作成時にAWSより生成されたポリシーを使用。
```
{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "AllowCloudFrontServicePrincipal",
            "Effect": "Allow",
            "Principal": {
                "Service": "cloudfront.amazonaws.com"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::< S3バケット名 >/*",
            "Condition": {
                "StringEquals": {
                    "AWS:SourceArn": "arn:aws:cloudfront::< AWSアカウントID >:distribution/< CFディストリビューションID >"
                }
            }
        }
    ]
}
```

</details>

<details>
<summary>検証用に作成したファイル</summary>

「 \<!--#include virtual="footer.html" --> 」により、
Webサーバが、footer.htmlの内容を埋め込んだ状態でクライアントに結果を返却する。
※S3バケットにも同様のファイルを格納。
```
# cat /var/www/html/hello.shtml
<!DOCTYPE html>
<html>
  <body>
    <h1>TEST Contents</h1>
    Hello world
  </body>
    <!--#include virtual="footer.html" -->
</html>
```
```
# cat /var/www/html/footer.html
<footer>
  <p>&copy; TEST</p>
</footer>
```
</details>

## 5. 確認結果
<details>
<summary>ブラウザによる確認(EC2)</summary>

- footer.html
![20241213_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959817/74bd0580-a3c9-6b7c-a71c-041250f5ec69.png)

- hello.shtml
→footer.htmlの内容が表示されていることを確認。
![20241213_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959817/ce369003-f1f9-aaae-cbf9-f8569154ed0d.png)
</details>

<details>
<summary>ブラウザによる確認(CF + S3)</summary>

- footer.html
![20241213_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959817/b6f1a0ba-8974-dd73-0f04-710168e6ebbc.png)

- hello.shtml
→footer.htmlの内容が表示されないことを確認。
![20241213_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959817/f1ee4c20-01c2-f144-7518-c20480df5c99.png)

- hello.html
→念のためファイルの拡張子"html"で格納し確認するも結果は変わらず。
![20241213_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3959817/903f65d1-4545-84f1-ae88-c4a4af3f1132.png)

</details>

<details>
<summary>コマンドによる確認(EC2)</summary>

```
>curl http://< グローバルIPアドレス >/hello.shtml
<!DOCTYPE html>
<html>
  <body>
    <h1>TEST Contents</h1>
    Hello world
  </body>
    <footer>
  <p>&copy; TEST</p>
</footer>

</html>
```
</details>

<details>
<summary>コマンドによる確認(CF + S3)</summary>

→ 「 \<!--#include virtual="footer.html" --> 」が処理されずに返却される。
```
>curl http://< ディストリビューションドメイン名 >.cloudfront.net/hello.shtml
<!DOCTYPE html>
<html>
  <body>
    <h1>TEST Contents</h1>
    Hello world
  </body>
    <!--#include virtual="footer.html" -->
</html>
```
```
# ブラウザ同様に、ファイルの拡張子"html"に対しても確認する。
>curl http://< ディストリビューションドメイン名 >.cloudfront.net/hello.html
<!DOCTYPE html>
<html>
  <body>
    <h1>TEST Contents</h1>
    Hello world
  </body>
    <!--#include virtual="footer.html" -->
</html>
```

</details>

## 6. 参考情報
[Apache httpd Tutorial: Introduction to Server Side Includes](https://httpd.apache.org/docs/2.4/en/howto/ssi.html)
[Apache Module mod_include](https://httpd.apache.org/docs/2.4/en/mod/mod_include.html)
[［改訂新版］プロになるためのWeb技術入門](https://gihyo.jp/book/2024/978-4-297-14571-2)