Lambda@Edge で Node.js 24 を試してみた

Lambda@Edge で Node.js 24 を試してみた

2025年11月サポート開始のNode.js 24ランタイムをLambda@Edgeで試しました。CloudFormationで環境を構築し、Viewer/OriginのRequest/Response全イベントでの動作を確認できました。
2025.11.28

2025年11月25日、AWS Lambda が Node.js 24 のサポートを開始しました。通常のLambda関数での動作については、後述の記事で紹介しましたが、
今回は Lambda@Edge での動作を検証しました。

https://dev.classmethod.jp/articles/aws-lambda-node-js-24/

Lambda@Edge は CloudFront のエッジロケーションで実行される Lambda 関数で、通常の Lambda とは異なる制約があります。
Node.js 24 では callback 形式の廃止 という破壊的変更が含まれますが、この変更が Lambda@Edge の基本機能(4つのイベントフックやヘッダー操作)の互換性を損なっていないことを、実機検証を通じて確認しました。

検証環境

  • リージョン: us-east-1
  • ランタイム: nodejs24.x
  • デプロイ方法: CloudFormation
  • 検証項目: 4つのイベントタイプすべて(viewer-request, origin-request, origin-response, viewer-response)

Lambda@Edge 関数の実装

4つのトリガー用の Lambda@Edge 関数を用意しました。

4つのトリガー
Amazon CloudFront & Lambda@Edge で画像をリサイズする

1. Viewer Request ハンドラー

Viewer Request は CloudFront がビューワーからリクエストを受け取った直後に実行されます。リクエストヘッダーの追加や URI の書き換えが可能です。

exports.handler = async (event) => {
  const request = event.Records[0].cf.request;

  // カスタムヘッダーを追加
  request.headers['x-custom-header'] = [{ 
    key: 'X-Custom-Header', 
    value: 'Node24-Edge' 
  }];

  // URI書き換えの例
  if (request.uri === '/old-path') {
    request.uri = '/new-path';
  }

  return request;
};

制約:

  • タイムアウト: 5秒
  • メモリ: 128MB

2. Origin Request ハンドラー

Origin Request は CloudFront がオリジンにリクエストを転送する直前に実行されます。CloudFront が追加したヘッダー(地理情報など)を読み取ることができます。

exports.handler = async (event) => {
  const request = event.Records[0].cf.request;

  // オリジンへのヘッダーを追加
  request.headers['x-origin-header'] = [{ 
    key: 'X-Origin-Header', 
    value: 'from-edge' 
  }];

  // CloudFrontヘッダーの読み取り
  console.log('CloudFront-Viewer-Country:', 
    request.headers['cloudfront-viewer-country']);

  return request;
};

制約:

  • タイムアウト: 30秒
  • メモリ: 128MB

3. Origin Response ハンドラー

Origin Response は CloudFront がオリジンからレスポンスを受け取った直後に実行されます。セキュリティヘッダーの追加などに使用します。

exports.handler = async (event) => {
  const response = event.Records[0].cf.response;

  // セキュリティヘッダーを追加
  response.headers['strict-transport-security'] = [{
    key: 'Strict-Transport-Security',
    value: 'max-age=63072000'
  }];

  response.headers['x-content-type-options'] = [{
    key: 'X-Content-Type-Options',
    value: 'nosniff'
  }];

  return response;
};

制約:

  • タイムアウト: 30秒
  • メモリ: 128MB

4. Viewer Response ハンドラー

Viewer Response は CloudFront がビューワーにレスポンスを返す直前に実行されます。キャッシュ制御ヘッダーの追加などに使用します。

exports.handler = async (event) => {
  const response = event.Records[0].cf.response;

  // キャッシュ制御ヘッダーを追加
  response.headers['cache-control'] = [{
    key: 'Cache-Control',
    value: 'public, max-age=3600'
  }];

  // 注意: Viewer Responseではステータスコードの変更は不可

  return response;
};

制約:

  • タイムアウト: 5秒
  • メモリ: 128MB
  • ステータスコード変更不可

CloudFormation テンプレート

4つの Lambda@Edge 関数と CloudFront ディストリビューションを一括でデプロイする CloudFormation テンプレートを用意しました。

テンプレート全文
AWSTemplateFormatVersion: '2010-09-09'
Description: Lambda@Edge with Node.js 24 - All Event Types

Resources:
  EdgeFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
                - edgelambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  ViewerRequestFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: node24-edge-viewer-request
      Runtime: nodejs24.x
      Handler: index.handler
      Role: !GetAtt EdgeFunctionRole.Arn
      Timeout: 5
      MemorySize: 128
      Code:
        ZipFile: |
          exports.handler = async (event) => {
            const request = event.Records[0].cf.request;
            request.headers['x-custom-header'] = [{ key: 'X-Custom-Header', value: 'Node24-Edge' }];
            if (request.uri === '/old-path') {
              request.uri = '/new-path';
            }
            return request;
          };

  ViewerRequestVersion:
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref ViewerRequestFunction

  OriginRequestFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: node24-edge-origin-request
      Runtime: nodejs24.x
      Handler: index.handler
      Role: !GetAtt EdgeFunctionRole.Arn
      Timeout: 30
      MemorySize: 128
      Code:
        ZipFile: |
          exports.handler = async (event) => {
            const request = event.Records[0].cf.request;
            request.headers['x-origin-header'] = [{ key: 'X-Origin-Header', value: 'from-edge' }];
            console.log('CloudFront-Viewer-Country:', request.headers['cloudfront-viewer-country']);
            return request;
          };

  OriginRequestVersion:
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref OriginRequestFunction

  OriginResponseFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: node24-edge-origin-response
      Runtime: nodejs24.x
      Handler: index.handler
      Role: !GetAtt EdgeFunctionRole.Arn
      Timeout: 30
      MemorySize: 128
      Code:
        ZipFile: |
          exports.handler = async (event) => {
            const response = event.Records[0].cf.response;
            response.headers['strict-transport-security'] = [{
              key: 'Strict-Transport-Security',
              value: 'max-age=63072000'
            }];
            response.headers['x-content-type-options'] = [{
              key: 'X-Content-Type-Options',
              value: 'nosniff'
            }];
            return response;
          };

  OriginResponseVersion:
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref OriginResponseFunction

  ViewerResponseFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: node24-edge-viewer-response
      Runtime: nodejs24.x
      Handler: index.handler
      Role: !GetAtt EdgeFunctionRole.Arn
      Timeout: 5
      MemorySize: 128
      Code:
        ZipFile: |
          exports.handler = async (event) => {
            const response = event.Records[0].cf.response;
            response.headers['cache-control'] = [{
              key: 'Cache-Control',
              value: 'public, max-age=3600'
            }];
            return response;
          };

  ViewerResponseVersion:
    Type: AWS::Lambda::Version
    Properties:
      FunctionName: !Ref ViewerResponseFunction

  OriginBucket:
    Type: AWS::S3::Bucket
    Properties:
      # ※注意: 123456789012 はダミーのAWSアカウントIDです。自身の環境に合わせて変更してください。
      BucketName: !Sub 'node24-edge-origin-${AWS::AccountId}'

  OriginAccessControl:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig:
        Name: node24-edge-oac
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        DefaultCacheBehavior:
          TargetOriginId: S3Origin
          ViewerProtocolPolicy: redirect-to-https
          CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
          LambdaFunctionAssociations:
            - EventType: viewer-request
              LambdaFunctionARN: !Ref ViewerRequestVersion
            - EventType: origin-request
              LambdaFunctionARN: !Ref OriginRequestVersion
            - EventType: origin-response
              LambdaFunctionARN: !Ref OriginResponseVersion
            - EventType: viewer-response
              LambdaFunctionARN: !Ref ViewerResponseVersion
        Origins:
          - Id: S3Origin
            DomainName: !GetAtt OriginBucket.RegionalDomainName
            OriginAccessControlId: !Ref OriginAccessControl
            OriginShield:
              Enabled: true
              OriginShieldRegion: us-east-1
            S3OriginConfig: {}

  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref OriginBucket
      PolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action: s3:GetObject
            Resource: !Sub '${OriginBucket.Arn}/*'
            Condition:
              StringEquals:
                AWS:SourceArn: !Sub 'arn:aws:cloudfront::${AWS::AccountId}:distribution/${CloudFrontDistribution}'

Outputs:
  DistributionDomainName:
    Value: !GetAtt CloudFrontDistribution.DomainName
  ViewerRequestVersion:
    Value: !Ref ViewerRequestVersion
  OriginRequestVersion:
    Value: !Ref OriginRequestVersion
  OriginResponseVersion:
    Value: !Ref OriginResponseVersion
  ViewerResponseVersion:
    Value: !Ref ViewerResponseVersion
  OriginBucket:
    Value: !Ref OriginBucket

オリジンシールドの採用理由:
Lambda@Edge は各エッジロケーションで実行されるため、ログが世界中のリージョンに分散します。オリジンシールドを有効化することで、Origin Request/Response イベントのログを us-east-1 に集約できるため、ログの確認や分析が容易になります。

CloudFormation ZipFile での注意点:
CloudFormation の ZipFile プロパティを使用する場合、package.json を含めることができないため、CommonJS 形式(exports.handler)が必須です。ES Modules 形式(export const handler)を使用する場合は、別途 ZIP ファイルを作成して S3 にアップロードし、Code.S3Bucket / Code.S3Key を使用する必要があります。

デプロイ後、作成したCloudFrontのBehavior設定で、Lambda@Edgeが関連付けされていることを確認しました。

CloudFront_Behavior設定

デプロイ手順

CLIを利用して、CloudFormation スタックの作成を行いました。

aws cloudformation create-stack \
  --stack-name node24-lambda-edge \
  --template-body file://cloudformation-edge.yaml \
  --capabilities CAPABILITY_IAM \
  --region us-east-1

動作確認

1. テストファイルのアップロード

# テスト用HTMLファイルを作成
cat > test.html << 'EOF'
<!DOCTYPE html>
<html>
<head>
    <title>Lambda@Edge Node.js 24 Test</title>
</head>
<body>
    <h1>Lambda@Edge Node.js 24 Test Page</h1>
    <p>This page tests all four Lambda@Edge event types.</p>
</body>
</html>
EOF

# S3にアップロード
# ※注意: node24-edge-origin-123456789012 はご自身の環境で作成されたバケット名に置き換えてください
aws s3 cp test.html s3://node24-edge-origin-123456789012/test.html --region us-east-1

2. CloudFront 経由でアクセス

curl -I https://d1234567890abc.cloudfront.net/test.html

3. 実行結果

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 451
Date: Fri, 28 Nov 2025 11:39:40 GMT
Last-Modified: Fri, 28 Nov 2025 11:39:30 GMT
ETag: "b4ec68bdc1ddae9aa15ce5803a10c28f"
Strict-Transport-Security: max-age=63072000
X-Content-Type-Options: nosniff
Cache-Control: public, max-age=3600
Via: 1.1 7bb66c5fc1e732675b1f05b324f80096.cloudfront.net (CloudFront)
X-Cache: Miss from cloudfront

以下、Lambda@Edge 関数の動作 が確認できました。

イベントタイプ 追加されたヘッダー
Origin Response Strict-Transport-Security max-age=63072000
Origin Response X-Content-Type-Options nosniff
Viewer Response Cache-Control public, max-age=3600
Viewer Request X-Custom-Header Node24-Edge(※)
Origin Request X-Origin-Header from-edge(※)

※ リクエストヘッダーはレスポンスに表示されないため、CloudWatch Logs で確認

まとめ

今回の検証を通じて、Lambda@Edge 環境下でも Node.js 24 ランタイムが正常に動作し、基本機能が利用できることを確認できました。

Node.js 24 は 2028年4月までの長期サポート(LTS)が予定されており、エッジロケーションでの実行パフォーマンスも向上しています。今後の新規開発においては、第一候補とすることをおすすめします。

既存の Lambda@Edge 関数で callback 形式を採用していた場合、移行には async/await への書き換えが必須となります。ただし、Lambda@Edge の関数は一般的にコード量が少ない傾向にあるため、改修コストは比較的少なく済む可能性が高いです。
とはいえ、EoL 直前のアップデートは予期せぬリスクを伴います。余裕を持った Node.js 24 への移行計画をご検討ください。

この記事をシェアする

FacebookHatena blogX

関連記事