# Amazon S3 の静的ウェブサイトにお問い合わせフォームを追加する

> S3 のウェブサイトエンドポイントは平文 HTTP の GET と HEAD しか受け付けません。お問い合わせフォームはフォームバックエンドに送信し、リダイレクトは CloudFront で正しく扱います。

公開日 2026-10-25 · https://ikiform.com/ja/guides/contact-form-static-site/aws-s3

S3 のウェブサイトエンドポイントはフォームの `POST` に `405 MethodNotAllowed` を返すので、フォームの `action` は Ikiform のエンドポイントに向けます。エンドポイントは平文の `http:` なので、そこへ戻る `_redirect` はたどられません。フォーム設定の **Redirect after submit** を設定するか、`https:` のために CloudFront を前段に置いてください。バケットの REST エンドポイントの前に CloudFront を置く場合、デフォルトルートオブジェクトはルートにしか適用されないので、`thanks.html` のような完全なファイル名にリダイレクトします。

## S3 ならではの違い

静的ウェブサイトホスティングを設定したバケットには、`http://www.example.com.s3-website-us-east-1.amazonaws.com` のような専用のウェブサイトエンドポイントが付きます。AWS のドキュメントによると、オブジェクトに対しては `GET` と `HEAD` リクエストしかサポートしていません。フォームを送信すると HTML のエラーページが返ります:

`POST への応答`:

```html
<html>
<head><title>405 Method Not Allowed</title></head>
<body>
<h1>405 Method Not Allowed</h1>
<ul>
<li>Code: MethodNotAllowed</li>
<li>Message: The specified method is not allowed against this resource.</li>
<li>Method: POST</li>
<li>ResourceType: OBJECT</li>
```

お問い合わせフォームに影響する S3 の特徴があと 3 つあります:

| S3 の特徴 | フォームへの影響 |
| --- | --- |
| ウェブサイトエンドポイントは HTTPS をサポートしない | Ikiform は `https:` の URL への `_redirect` しかたどらないので、素のエンドポイントへ戻るリダイレクトは無視される |
| レスポンスヘッダーはバケットではなく CloudFront が付ける | Content-Security-Policy は CloudFront のレスポンスヘッダーポリシーで設定する |
| CloudFront のデフォルトルートオブジェクトはルートにしか適用されない | REST オリジンでは `https://example.com/thanks/` で `thanks/index.html` は読み込まれない。`thanks.html` にリダイレクトする |

`https:` のサイトには、CloudFront 上で動く Amplify Hosting か、バケットの前に CloudFront を置く構成を AWS は推奨しています。フォームはどちらでも動きます。変わるのは、送信後に訪問者がたどり着く場所です。

図: CloudFront の有無による、送信後の訪問者の行き先

1. 素のウェブサイトエンドポイントでは、ページは http で配信され、Ikiform に送信します。
2. Ikiform はメッセージを保存しますが、http のアドレスへの _redirect は無視し、ホストされたサンクスページを表示します。
3. ダッシュボードで Redirect after submit を設定すれば回避できます。こちらは http の URL も受け付けるからです。
4. 独自ドメインで CloudFront の背後に置けば、ページは https になります。
5. ドメインが Allowed domains にあれば、Ikiform は _redirect に従って thanks.html に移動させます。

どちらの場合もメッセージは保存されます。https かどうかで変わるのは、ブラウザーの行き先という最後の段階だけです。

## どの CloudFront オリジンを使うか

CloudFront がバケットを読む方法は 2 つあり、どちらを選ぶかでサンクスページの URL の形が決まります:

| オリジン | バケット | フォルダーのインデックスページ | サンクスページの URL |
| --- | --- | --- | --- |
| S3 ウェブサイトエンドポイントをカスタムオリジンとして | 公開読み取りが必要 | 使える。`/thanks/` で `thanks/index.html` が読み込まれる | `/thanks/` または `/thanks.html` |
| REST エンドポイントとオリジンアクセスコントロール | 非公開のまま、CloudFront だけが読み取る | ルートのみ。デフォルトルートオブジェクトで | `/thanks.html` のように完全なファイル名 |

完全なファイル名ならどちらでも使えるので、サンプルではそうしています。CloudFront を自分で運用したくなければ、Amplify Hosting で `https:` とマネージド CDN 付きでバケットからデプロイできます。その場合もフォームの変更は不要です。

## フォーム

S3 のサイトは企業サイトであることが多いので、このフォームでは仕事用のメールアドレスと会社名を尋ねます。`thanks.html` と同じ場所に `contact.html` としてアップロードします:

`contact.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="name">Name</label>
  <input id="name" name="name" autocomplete="name" required />

  <label for="email">Work email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="company">Company</label>
  <input id="company" name="company" autocomplete="organization" />

  <label for="message">How can we help?</label>
  <textarea id="message" name="message" rows="6" minlength="10" required></textarea>

  <input type="hidden" name="_redirect" value="https://www.example.com/thanks.html" />
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button type="submit">Send</button>
</form>
```

- `_redirect` はファイル名付きで CloudFront のドメインを指しています。`www.example.com` か `example.com` が **Allowed domains** にある場合にだけ従います。
- 素の `http:` エンドポイントでは、`_redirect` の入力欄を削除し、代わりに **Redirect after submit** をサンクスページに設定します。この設定は自分のチームしか変更できないので、`http:` の URL も受け付けます。
- フィールド名は `email` のままにしてください。通知メールはこれを reply-to アドレスとして使います。

企業サイトでは、問い合わせの受信トレイを複数人で読むのが普通です。確認済みの通知先をフォームに最大 10 件まで追加すると、全員にメッセージが届き、**Reply** を押した人が送信者に直接返信できます。共有チャンネルやチケットのキューにも届けたい場合は、署名付き Webhook を追加し、[Zapier](https://ikiform.com/blog/form-submissions-to-zapier.md) や自分のコードで振り分けてください。`company` フィールドはプレーンテキストで届くので、どちらでもそのまま絞り込みに使えます。

アップロードの前に、`localhost` を **Allowed domains** に入れてローカルサーバーでページをテストします。`localhost` からの送信はテストとして扱われるので、バケットで配信する前にフォームを仕上げられます。

フォルダーをバケットに同期し、CloudFront でページを無効化して、新しいフォームがすぐに配信されるようにします:

`ターミナル`:

```bash
aws s3 sync ./site s3://www.example.com --delete

aws cloudfront create-invalidation --distribution-id E1EXAMPLE --paths "/contact.html" "/thanks.html"
```

## AWS のホストでの許可ドメイン

| ページの配信元 | 追加するもの |
| --- | --- |
| 素のウェブサイトエンドポイント | `www.example.com.s3-website-us-east-1.amazonaws.com` のような、バケットのエンドポイントのホスト全体 |
| CloudFront のデフォルトドメイン | テスト中は `d111111abcdef8.cloudfront.net` のホストそのもの |
| 独自ドメイン | `example.com`。`www.example.com` もカバーする |

`amazonaws.com`、リージョンごとの `s3-website` のサフィックス、`cloudfront.net` だけを追加してはいけません。世界中のすべてのバケットやディストリビューションが一致してしまいます。AWS が S3 ウェブサイトエンドポイントのドメインを Public Suffix List に登録しているのも同じ理由で、バケットはそれぞれ別のサイトだからです。

## CloudFront でヘッダーを付ける

S3 のオブジェクトメタデータでは Content-Security-Policy を設定できないので、CloudFront のレスポンスヘッダーポリシーで設定し、ディストリビューションのデフォルトキャッシュ動作に関連付けます。ヘッダーの値は 1,783 文字までです:

`csp-policy.json`:

```json
{
  "Name": "site-csp",
  "SecurityHeadersConfig": {
    "ContentSecurityPolicy": {
      "Override": true,
      "ContentSecurityPolicy": "default-src 'self'; form-action 'self' https://ikiform.com; connect-src 'self' https://ikiform.com"
    }
  }
}
```

`ターミナル`:

```bash
aws cloudfront create-response-headers-policy --response-headers-policy-config file://csp-policy.json
```

> `Override: true` は、オリジンが送る CSP を置き換えます。素の S3 オリジンには CSP はありませんが、後でオリジンを変えてもこのポリシーが優先されるようになります。

## 動作を確認する

1. 上の表のドメインと、テスト中は `localhost` を追加します。
2. CloudFront のドメインから送信します。独自ドメインの `thanks.html` に移動するはずです。
3. 素のウェブサイトエンドポイントも公開したままにする場合は、そこから送信し、**Redirect after submit** で設定したページに移動することを確認します。
4. ダッシュボードで **Submissions** を開きます。送ったすべてのフィールドを含む新しい送信データがあります。
5. 通知の受信者それぞれの受信トレイを確認します。各自にフィールド入りのメールが届き、フォームに `email` フィールドがあれば **Reply** の宛先は送信者になります。
6. 何も届かない場合は、ダッシュボードのステータスフィルターを **Spam** に切り替えてください。スパムにも本物の送信と同じ成功の応答が返ります。

## ハマりどころ

| 問題 | 対処 |
| --- | --- |
| `Code: MethodNotAllowed` 付きの `405 Method Not Allowed` | フォームがバケットに送信されています。CloudFront がまだ古い `contact.html` を配信しているかもしれないので、無効化してください。 |
| リダイレクト後に `AccessDenied` や `403` になる | REST オリジンの CloudFront ディストリビューションで、リダイレクト先が `/thanks/` のようなフォルダーになっています。`thanks.html` を指定してください。 |
| 自分のページではなく Ikiform のサンクスページに移動する | `_redirect` は、ドメインが **Allowed domains** にあり、かつ `https:` の URL でない限り無視されます。ドメインを追加するか、代わりにフォーム設定で **Redirect after submit** を設定してください。 |
| `403 origin_not_allowed` が返るか、すべての送信がスパムになる | ページのドメインがフォームの **Allowed domains** にありません。ドメインと、ローカルのテスト用に `localhost` を追加してください。登録済みドメインのサブドメインは許可されます。 |

## よくある質問

### S3 の静的ウェブサイトでフォーム送信を処理できますか？

いいえ。S3 のウェブサイトエンドポイントは GET と HEAD リクエストしかサポートしないので、POST には 405 MethodNotAllowed が返ります。フォームの送信先をフォームバックエンドにするか、Lambda で API を作ってください。

### お問い合わせフォームを動かすのに CloudFront は必要ですか？

いいえ。http のページからでもフォームは Ikiform に送信されます。CloudFront で https にすると、Ikiform が _redirect に従って自分のサンクスページに戻せるようになります。

### API Gateway、Lambda、SES を使わないのはなぜですか？

その構成でも動き、すべてを AWS 内に収められますが、バリデーション、スパムフィルタリング、保存、リトライを自分で作る必要があり、SES には認証済みの送信者が必要です。お問い合わせフォームなら、フォームバックエンドのほうが保守の手間が少なくて済みます。

### フォームを変更した後、CloudFront は古いフォームをキャッシュし続けますか？

キャッシュが期限切れになるまでは、その可能性があります。変更のたびに contact.html を無効化するか、Cache-Control の max-age を短くして HTML をアップロードすれば、新しいフォームがすぐに反映されます。

### Ikiform は無料ですか？

いいえ、無料プランはありません。テストは無料で、支払いの前に localhost から月 50 件のテスト送信ができます。本番運用するには、月 $6 で 500 件からの Pro を選ぶか、1 回払いで 10,000 件 $49 または 50,000 件 $149 を購入できます。クレジットに有効期限はありません。

## その他のガイド

- [静的サイトのお問い合わせフォーム](https://ikiform.com/ja/guides/contact-form-static-site.md): サーバーなしで静的サイトに動くお問い合わせフォームを追加します。POST の送り先、スパム、メールでの返信、各ホストとフレームワーク向けのコードまで。
- [Cloudflare Pages](https://ikiform.com/ja/guides/contact-form-static-site/cloudflare-pages.md): Function を書かずに Cloudflare Pages にお問い合わせフォームを追加します。フォームバックエンドに送信し、_headers でヘッダーを設定し、自分の Turnstile キーを使います。
- [GitHub Pages](https://ikiform.com/ja/guides/contact-form-static-site/github-pages.md): GitHub Pages ではサーバーコードが動かず、フォームの POST は 405 Not Allowed になります。フォームバックエンドに送信し、プロジェクトサイトのリダイレクトを正しく設定しましょう。
- [Vercel](https://ikiform.com/ja/guides/contact-form-static-site/vercel.md): Vercel の静的ページはフォームの POST に 405 Method Not Allowed を返します。その理由と、代わりにバックエンドへフォームを送る方法を説明します。
- [Netlify](https://ikiform.com/ja/guides/contact-form-static-site/netlify.md): Netlify Forms を使わずに、Netlify のサイトでお問い合わせフォームを運用します。ビルド時の検出なし、React からの JSON 送信、デプロイプレビュー用の別フォーム。
- [ポートフォリオサイト](https://ikiform.com/ja/guides/contact-form-static-site/portfolio-website.md): 初回の打ち合わせで聞くことを尋ねるポートフォリオのお問い合わせフォーム。案件、予算、時期、リンクを、サーバーなしで受け取り、受信トレイから返信できます。
