Lambda Web Adapter とは
Lambda Web Adapter は、FastAPI / Express / Next.js / React Routerなどの
HTTP サーバとして動くアプリを、ほとんどコードの修正要らずで Lambdaに載せることができる仕組みです。
アプリ側は通常の Web サーバとして起動し、Lambda 側で HTTP リクエストとのブリッジを行います。
AWS公式
https://aws.amazon.com/jp/builders-flash/202301/lambda-web-adapter/
この記事でやること / 結論
Lambda Web Adapter を使って、Web アプリケーションを Lambda上でホスティングできることを検証しました。
また、その前段に CloudFront を配置できることも確認しました。
- Lambda Web Adapter を使って Lambda(コンテナ)+ Function URL でホスティングできること
- その前段に CloudFrontを構えられること(そこにLambda@edgeも取り付けられること)
Amplify 構成との違い
AWSAmplify Hosting は非常に手軽なサーバーレスアプリケーションですが、
CloudFront は Amplify 管理下であるため、CloudFrontに自由にLambda@Edgeを差し込めません。
認証(Cognito)は主に アプリケーション層側で行う設計のようで、
- CloudFront レイヤーでリクエストを制御したい
- オリジンに届く前に弾きたい
- CloudFront の設定(OAC / Lambda@Edge)を自分で管理したい
といったことがしたい場合はなかなか難しいです。
Lambda Web Adapterであればサーバーレス構成でありながらこれらを制御できるという話でしたので試してみました。
検証
create-react-router
まずは通常通りにcreate-react-routerを叩きます。
npx create-react-router@latest
lambda web adapterの設定
Dockerfile に Lambda Web Adapter を追加
Dockerfile に以下を追加するだけで、Lambda Web Adapter が有効になります。
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.0 /lambda-adapter /opt/extensions/lambda-adapter
もともとcreate-react-routerしたときに払い出されたdockerfileがあるので、最終的なDockerfileは以下のようになりました。
FROM node:20-alpine AS development-dependencies-env
COPY . /app
WORKDIR /app
RUN npm ci
FROM node:20-alpine AS production-dependencies-env
COPY ./package.json package-lock.json /app/
WORKDIR /app
RUN npm ci --omit=dev
FROM node:20-alpine AS build-env
COPY . /app/
COPY --from=development-dependencies-env /app/node_modules /app/node_modules
WORKDIR /app
RUN npm run build
FROM node:20 AS runtime
COPY ./package.json package-lock.json /app/
COPY --from=production-dependencies-env /app/node_modules /app/node_modules
COPY --from=build-env /app/build /app/build
# Lambda Web Adapter
COPY --from=public.ecr.aws/awsguru/aws-lambda-adapter:0.9.0 /lambda-adapter /opt/extensions/lambda-adapter
WORKDIR /app
CMD ["npm", "run", "start"]
CDKでの宣言
IaCで宣言できた方が嬉しいのでCDKでデプロイできるかも検証しました。
Lambda はコンテナ形式で作成し、Function URL を付与します。
AWS_LWA_PORT は必ず設定する必要があります。
import { CfnOutput, Stack, StackProps } from "aws-cdk-lib";
import { DockerImageAsset, Platform } from "aws-cdk-lib/aws-ecr-assets";
import {
DockerImageCode,
DockerImageFunction,
FunctionUrlAuthType,
} from "aws-cdk-lib/aws-lambda";
import { Construct } from "constructs";
import * as path from "path";
export class CdkStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const frontend = new DockerImageFunction(this, "frontend", {
functionName: `lambda-web-adapter-test`,
code: DockerImageCode.fromImageAsset(
path.join(__dirname, "../../remix/"),
{
platform: Platform.LINUX_AMD64,
file: "Dockerfile",
},
),
environment: {
AWS_LWA_PORT: "3000",
},
});
const funtionUrl = frontend.addFunctionUrl({
cors: {
allowedOrigins: ["*"],
},
authType: FunctionUrlAuthType.AWS_IAM,
});
new CfnOutput(this, "endpoint", {
value: funtionUrl.url,
});
}
}
このようにしてデプロイすると関数URLが払い出されて、
Function URL に直接アクセスすると、アプリが正常に表示されました!
CloudFront
OACで設定・CloudFrontからしかアクセスできないように設定可能でした。
lambda@edge
今回は"lambda@edgeで弾いたときに静的ファイルが表示されないこと"を検証できればいいのでcognitoなどの設定は行わず、
lambda@edgeで必ず403を返すようにしましたが、lambda@edgeで弾かれた場合静的ファイルもみれないことを確認しています。
$ http https://xxx.cloudfront.net/favicon.ico
HTTP/1.1 403 Forbidden
Cache-Control: max-age=0
Connection: keep-alive
Content-Length: 9
Content-Type: text/plain; charset=UTF-8
Date: Thu, 07 Aug 2025 09:18:31 GMT
Server: CloudFront
X-Cache: LambdaGeneratedResponse from cloudfront
Forbidden
=> lambda@edgeを通れなければ静的ファイルを参照できないことを確認できました!
終わりに
コールドスタートがきになりますが、非常に便利でいろいろ使えそうだと思いました!
もしよりよい使い方をご存知のかたいればコメントください。
