HTTP カスタム統合の API Gateway でバックエンドのステータスコードと異なるコードが返ってくるのはなぜでしょうか

困っていた内容

バックエンドが EC2 の HTTP カスタム統合 の APIGateway を通してリクエストを行うと、全てのレスポンスが HTTP ステータスコード 「200」で返ってきます。

例えば、バックエンドの EC2 インスタンスに直接リクエストすると、想定通り「500」のステータスコードが返ってきます。しかしながら、API Gateway を経由する場合は、「200」が返ってきてしまいます。

どうすれば、バックエンドのサーバ が返したステータスコードを、APIGateway のステータスコードとして返せるのでしょうか。

どう対応すればいいの?

HTTP カスタム統合の場合、バックエンドから返ってきたレスポンスを API Gateway に対応するレスポンス統合をメソッドレスポンスに関連付けてあげないと、どのステータスコードでも、デフォルトで設定されているステータスコード「200」が API Gateway からクライアントへ返ってしまいます。

AWS 公式では HTTP カスタム統合を以下のように記載しています。

HTTP: このタイプの統合は、API がバックエンドの HTTP エンドポイントを公開することを可能にします。HTTP 統合 (HTTP カスタム統合とも呼ばれます) では、統合リクエストと統合レスポンスの両方を設定する必要があります。メソッドリクエストから統合リクエストへの、また統合レスポンスからメソッドレスポンスへの、データマッピングを設定する必要があります。
API ゲートウェイ API 統合タイプの選択 - Amazon API Gateway

後半に記述のように、バックエンドからのレスポンスを API Gateway 側でよしなにクライアントへ返してはくれませんので、注意しましょう。

準備

では、実際に上記のような挙動となるか検証してみましょう。 まず、バックエンドから返ってきたレスポンスをそのままクライアントへ返すように API Gateway とバックエンドの EC2 インスタンス を設定してみます。

構成

今回の検証の以下の図のような構成で進めます。

API Gateway の設定

以下の様に、API Gateway のリソース、メソッドを作成します。

また、EC2 へ HTTP 統合するように、EC2 の バブリック IP を取得しておき、ポートが 8080 でアクセスするように、
API Gateway の統合リクエストを設定します。

EC2 インスタンスの設定

EC2 は Amazon Linux 2 を利用し、EC2 上にインストールして Doker に PHP をデプロイする前提で話を進めます。
それでは、まず EC2 インスタンスへ SSH して、Docker と docker-compose をインストールします。

まず、Docker のインストールします。

$ sudo yum -y install docker
$ sudo systemctl start docker.service
$ sudo systemctl enable docker
$ sudo usermod -a -G docker ec2-user

次に、docker-compose のインストールします。

sudo curl -L "https://github.com/docker/compose/releases/download/1.26.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

それぞれインストールされたか確認します。

$ docker --version
Docker version 20.10.4, build d3cb89e

$ docker-compose --version
docker-compose version 1.26.0, build d4451659

次に、ec2-user のカレントディレクトリで以下のディレクトリ構造のファイル群を作成します。

$ tree
.
├── Dockerfile
├── docker-compose.yml
└── src
    └── index.php

Dockerfile は以下の様に、最新の PHP と Apache の公式イメージを利用します。
適宜、Dockerhub で確認しましょう。

FROM php:8.0.7-apache

Dockerdocker-compose.yml は、以下を利用します。
ポート 8080 でホスト側が待ち受けるように設定します。コンテナ側のポートは 80 にします。

version: '3'
services:
  php:
    build: .
    volumes:
      - ./src:/var/www/html
    ports:
      - 8080:80

最後に、クライアントがアクセスした際に表示する、index.php を作成します。

<?php
echo "Hello Nakano"

それでは、準備ができたので、イメージを構築後、コンテナを起動します。

$ docker-compose build
$ docker-compose up

ここで、ブラウザで直接 EC2 へアクセスして正常に表示されるか確認しましょう。

$ curl -i http://XX.XX.XX.XX:8080
HTTP/1.1 200 OK
Date: Tue, 22 Jun 2021 08:59:17 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/8.0.7
Content-Length: 12
Content-Type: text/html; charset=UTF-8

Hello Nakano

検証してみた

想定外の挙動になることを確認

上述の例にも挙げたように、API Gateway にリクエストした際にバックエンドから 500 エラーが返ってくる場合の検証をしてみましょう。
まず、意図的に 500 エラーをバックエンドからレスポンスするように、index.php で http_response_code を使ってレスポンスステータスを書き換えます。

<?php
http_response_code(500);
echo "Hello Nakano";

再度 EC2 の IP をブラウザに入力して表示すると、レスポンスコードが「500」で返っていることが確認できました。

$ curl -i http://XX.XX.XX.XX:8080
HTTP/1.1 500 Internal Server Error
Date: Tue, 22 Jun 2021 09:01:17 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/8.0.7
Content-Length: 12
Connection: close
Content-Type: text/html; charset=UTF-8

Hello Nakano

それでは、API Gateway 経由でレスポンスがどうなるか確認してみましょう。

$ curl -i https://XXXXX.execute-api.ap-northeast-1.amazonaws.com/test/hello
HTTP/2 200
date: Tue, 22 Jun 2021 09:02:14 GMT
content-type: application/json
content-length: 12
x-amzn-requestid: 7a7a3197-9842-49dd-a455-d3f23e2a3900
x-amz-apigw-id: BUcLiH3ANjMFcrA=
x-amzn-trace-id: Root=1-60d1a716-1bc3c65959cc464e455af33c

Hello Nakano

curl から返ってきたレスポンスを見てもわかるように、ステータスコード「500」で返ってこないといけないところが、「200」として返ってきています。
やはり、「困っていた内容」の通りの挙動となることが確認されました。

レスポンスをマッピング設定する

それでは、API Gateway にバックエンドから返ってきたレスポンスをクライアントへ返すように、マッピング設定を行います。
まず、メソッドレスポンスのコンソールで、HTTP ステータス「500」を追加します。

次に、統合レスポンスのコンソールで、以下の図のようにステータスコード「500」の場合の設定を追加します。

以上で、バックエンドから返ってきた HTTP ステータスコード「500」がクライアントへ返るようになりました。

想定通りの挙動になったか確認

最後に、本当にステータスコード「500」が返るようになったか検証してみます。

ブラウザで、API Gateway のエンドポイントへアクセスすると、HTTP ステータスコード「500」が返っていることが確認できました。
これで想定通りの挙動となりました。

$ curl -i https://XXXXX.execute-api.ap-northeast-1.amazonaws.com/test/hello
HTTP/2 500
date: Tue, 22 Jun 2021 09:05:43 GMT
content-type: application/json
content-length: 12
x-amzn-requestid: b3fddbb3-e0bd-4ab0-85b3-867b9fcd07b9
x-amz-apigw-id: BUcsRGjItjMFtSQ=
x-amzn-trace-id: Root=1-60d1a7e7-5fe400d14ca1728c581290ac

Hello Nakano

参考資料

API ゲートウェイ API 統合タイプの選択 - Amazon API Gateway

API Gateway で統合レスポンスを設定する - Amazon API Gateway

EC2(Amazon Linux 2)に docker と docker-compose をインストールしてみる - Qiita

docker-compose で PHP 開発環境 - Qiita

PHP: http_response_code - Manual

【Docker】docker-compose コマンド早見表 - Qiita