こんにちは。
ソーイ株式会社1年目、村上です。
前回LaravelのルーティングとコントローラーをAWSサービスを用いて再現しました。
動作確認の際には CloudShell から値を含め送信し、構築できたか確認しました。
今回はその記事の第2弾です。
実際のWebアプリでは、ブラウザで動くフロントエンドからAPIを呼び出します。ですので 今回はAWSだけでWebサイトを公開し、そこから自作APIを動かしてデータを保存する という、フルサーバーレスなWebアプリの完結編に挑戦しました。
この記事でわかること
- Amazon S3 を使って、HTMLファイルをWebサイトとして世界に公開する方法
- ブラウザ(フロント)からAPIを叩く際に必ず遭遇する 「CORS」 という壁の正体と、その突破方法
- Amazon EC2 を立てずにDB保存までできるWebアプリの作成方法
全体構成
今回は、前回作成したAPIを実際のWebサイト(フロントエンド)から呼び出し、データを保存できる 完結編 を目指します。
使用するサービス
Laravelの「Blade(表示)」の役割を Amazon S3 で、外部からの通信許可を API GatewayのCORS設定 で再現します。
Amazon S3(Viewの代わり)
- HTML/JavaScriptなどの静的ファイルを配信するストレージサービス
- 今回はここを「Webサーバー(Nginx等)」に見立てて、フロント画面を公開します
Amazon API Gateway(CORS設定の追加)
- ブラウザ(S3)からのリクエストを受け入れるための「門番」の設定を行います
AWS Lambda(Controller)
- 前回作成した保存ロジックをそのまま使用します
Amazon DynamoDB(Database)
- 前回作成したテーブルに、ブラウザから入力したデータが保存されます
全体図
フロントエンド連携編(静的サイトとAPIの疎通)
今回の「フロントエンド連携編」をスムーズに進めるための、最初のステップは S3でフロントエンド(HTML) の居場所を作ることです。
HTMLを公開する手段は他にもいくつかあります。
Amazon EC2 : 仮想サーバーを立ててNginxなどを動かす(Laravelのいつもの構成に近い)。
AWS Amplify : モダンなフロントエンド開発に特化したフレームワーク。
Amazon S3 : 静的なファイルを配置して公開するストレージ。
その中で今回 S3 を選んだ理由は、「サーバー管理が一切不要で、最も安価かつシンプルにWebサイトを公開できるから」 です。DB保存までを「フルサーバーレス(サーバーを1台も持たない構成)」で実現するという本記事のテーマに、最も適した選択肢でしょう。
手順1: S3バケットの作成
まずはHTMLファイルを置くための「バケット」を作成します。
名前は好きなものでいいですが、管理できる名前がいいです。
例:my-laravel-aws-frontend-2026

今回はS3バケットのブロックパブリックアクセス設定をオフにします。
S3をWebサーバーとして使い、HTMLを不特定多数の人に見てもらう必要があるからです。

ブロックをオフ、すると警告が出てくるのでチェックをつけた状態でバケットを作成します。
手順2:「静的ウェブサイトホスティング」の有効化
バケットを単なるストレージではなく、Webサーバーとして動かすための設定を行います。
作成したバケットの詳細 > プロパティを開き、一番下までスクロールします。
すると 静的ウェブサイトホスティングという設定項目があります。

編集 を開き、静的ウェブサイトホスティング を有効にします。
するとWebページのファイル名とエラー用ページのファイル名を設定する場所があります。
ファイル名を指定した後変更の保存を押下します。
手順3:バケットポリシーの設定(閲覧許可)
最後に、「世界中の誰でもこのHTMLを読み取っていいよ」という許可を出します。Laravelの storage フォルダに 755 権限を振るようなイメージです。
バケットの アクセス許可 タブをクリック。
「バケットポリシー」の 編集 をクリックし、以下のコードを貼り付けます。

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::あなたのバケット名/*"
}
]
}
ここまでできれば、フロントエンドの受け皿は完成です。
手順4:index.html(HTML/JavaScript)の作成
次は、実際にブラウザに表示させる フロントエンドのコード(HTML/JavaScript) を作成して、S3にアップロードしましょう。
ここで、Laravelの resources/views に相当するHTMLファイルを作ります。APIを叩くための fetch 処理を含んだコードです。
以下の内容を index.html という名前で保存してください。
※
https://xxxxxxxx.execute-api...の部分は、前回作成したAPI Gatewayの「呼び出しURL」 に必ず書き換えてください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>AWS API Test</title>
</head>
<body>
<h1>タスク登録フォーム</h1>
<input type="text" id="taskName" placeholder="タスクを入力">
<button onclick="saveTask()">送信</button>
<p id="result"></p>
<script>
async function saveTask() {
const task = document.getElementById('taskName').value;
// ここを自分のAPIのURLに書き換えてください!
const apiUrl = 'https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/hello';
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: Date.now().toString(), task: task })
});
const data = await response.json();
document.getElementById('result').innerText = data.message;
}
</script>
</body>
</html>
アップした後、先ほど設定した 静的ウェブサイトホスティング の項目を見ると、URLが貼られていると思うので開いてみましょう。
ページが表示できました。
しかし、試しに送ろうとしてみると、下記画像のようにCORSエラーによって通信がはじかれてしまいました。
手順5:API Gatewayで「CORSの門番」を突破する
ブラウザは「セキュリティ」の観点から、異なるドメイン間(S3とAPI Gateway)の通信をデフォルトで遮断します。これをAPI Gateway側で「このS3からの通信は許可するよ!」と教えてあげるのが CORS設定 です。
そもそもCORS設定って何?
CORS(Cross-Origin Resource Sharing) とは、ブラウザが持っている「セキュリティの門番」のような仕組みです。通常、ブラウザはセキュリティの観点から、「Webサイトが表示されている場所(S3)」と「APIを叩く場所(API Gateway)」のドメインが異なる場合、その通信をブロックします。
今回の場合、S3(http://バケット名... )から API Gateway(https://xxxxxx.execute-api... )へデータを送ろうとしていますが、ドメインが違うため、API側で「このURLからの通信は許可するよ!」と明示的に設定しない限り、エラーが出てしまうのです。
API Gatewayのコンソールを開いてAPIを選択、CORS をクリックし 設定 をクリックします。
開くと色々項目が出てくるので設定していきます。
- Access-Control-Allow-Origin:
*
※本来はS3のURLを指定するのが安全ですが、まずは疎通を優先して (どこからでも許可) にします。
本番では https://your-app-domain.com のように、「自分のサイトのドメイン」 だけを記述します。* のままにしておくと、悪意のある第三者のサイトから自分のAPIが勝手に呼び出されてしまうリスクがあるため、本番運用では必ず制限をかけるのが良いです。
- Access-Control-Allow-Headers:
Content-Type
※ブラウザが送るJSON形式を許可するために必要です。
- Access-Control-Allow-Methods:
POST, OPTIONS
※APIを叩くためのPOSTと、通信前に確認を行うOPTIONSメソッドを許可します。
ステージが
$defaultだった場合、対応不要ですが別だった場合、保存だけでは反映されません。
CORS設定ページでデプロイボタンを押下し、反映となります。
もう一度先ほどのWebページで送信してみましょう。
保存成功! となれば達成です。
DynamoDBに作成しているテーブルの 返された項目を確認してみましょう。

まとめ
今回は、前回作成したAPIという バックエンド に、S3という フロントエンド を接続し、実際にWebブラウザからデータを保存する一連の流れを構築しました。
これまでLaravelの routes/web.php や Blade が自動で行ってくれていた 表示 と 通信 の役割を、AWSの各サービスに分解して繋ぎ合わせることで、Webアプリケーションの本質を改めて体験できたのではないでしょうか。
まずは CORS という 境界線 を攻略できたことが挙げられます。ブラウザの強力なセキュリティ機能であるCORSを、API Gatewayのインフラ設定として実装したことで、Laravelの cors.php で内部的に行われていた処理の正体をインフラ側から理解することができました。また、EC2やNginxといったサーバーを1台も構築することなく、S3からHTMLを配信し、API Gatewayを通じてDynamoDBへデータを保存する完全なサーバーレスパイプラインを構築できたことも大きな収穫です。
次回があればDynamoDBに溜まったデータを、APIを使って一覧表示する「読み取り(Read)編」も行います。
お知らせ
技術ブログを週1〜2本更新中、ソーイをフォローして最新記事をチェック!
https://qiita.com/organizations/sewii







