[アップデート] Application Load Balancer のリスナールールでトランスフォームを構成し、ターゲットにルーティングする前にホストヘッダーや URL パスを書き換えれるようになりました

いわさです。

みなさんよく Application Load Balancer を使われていると思います。
負荷分散や TLS/SSL終端させる目的で導入することが多いと思いますが、リクエストの内容に応じてターゲットへのリクエスト転送内容や転送先をカスタマイズすることもあると思います。

Apache や Nginx でリバースプロキシを構成していた場合でも Application Load Balancer のみで代用できることがあるのですが、それでも仕様上 Application Load Balancer のみで対応できないケースがあって、場合によっては結局 Apache や Nginx でリバースプロキシを構成することも稀にありました。

先日のアップデートでリスナールールでカスタマイズできる項目が増えまして、Application Load Balancer のみで対応できる項目が増えました。
リスナールール上でトランスフォームというものを設定することで、ターゲットへ転送する前に URL パスとホストヘッダーを正規表現ベースで置換できるようになりました。

https://aws.amazon.com/about-aws/whats-new/2025/10/application-load-balancer-url-header-rewrite/

これによってバックエンドでバーチャルホストを構成している場合とか、パスまで書き換えの必要がある場合とか、今までよりも ALB 単体で対応できるケースが増えそうです。
今回は設定方法の確認と、実際に設定した前後での動作確認を行ってみましたので紹介します。

設定方法

ロードバランサーのコンソールにアクセスしてみると「Application Load Balancer の URL リライトの紹介」というバナーが表示されていますね。これのことみたいだ。

EC81AA4F-6486-4FF3-AE66-17DCA4BF720A.png

で、設定方法なのですが、ALB のリスナールール上で今回のアップデート機能が利用できます。注意点としてデフォルトルールではトランスフォームを追加することができないので、カスタムルールの追加が必要になります。

3A08FD20-7264-49E6-A238-FB2617BCC1DE.png

ルール編集時に以下からトランスフォームが設定できるようになっています。

D38280D8-CCD5-4477-9609-A992FD8ACAF2.png

本日時点でトランスフォームで設定できるのは「ホストヘッダー」と「URLパス」の2つです。

EAA6E107-14F6-415B-B4C2-6817EB85FD91_4_5005_c.jpeg

ホストヘッダーははリクエスト内のホストヘッダーを書き換えてターゲットに転送するおちうものです。正規表現を使ってホストヘッダーのパターンを照合して、そこから置換を行うというもの。
URLパスは同じように正規表現で URL パスの置換を行います。パス構成を帰ることもできますし、クエリ文字列の編集に介入することもできます。
よくクエリ文字列をパスに変換することとかあると思うのですが、あれが ALB だけでできちゃいますね。
URLパスではプロトコルやポートまでは変更できないので注意してください。

D305BDAC-DEDA-45AE-8052-394581B5B375.png

このあたりのトランスフォームの詳細は以下の公式ドキュメントに情報が記載されているのでこちらもご確認ください。

https://docs.aws.amazon.com/elasticloadbalancing/latest/application/rule-transforms.html

転送先サーバーのバーチャルホスト設定にあわせてホストヘッダーを変換する

ということで試してみましょう。
色々なシナリオ考えられると思いますが、今回は EC2 にセットアップされたアプリケーションでバーチャルホストが構成されていて、それを ALB が許可するホストヘッダーと EC2 が期待するホストヘッダーで異なる場合に、このトランスフォームを使って変換してみたいと思います。

本題から逸れますが事前に以下の VPC + ALB + EC2 の CloudFormation スタックをデプロイ済みです。Amazon Q Developer ちゃんに作ってもらいました。

			
			AWSTemplateFormatVersion: '2010-09-09'
Description: 'ALB with EC2 backend and Apache virtual hosts'

Resources:
  
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: ALB-VPC

  
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: ALB-IGW

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: ap-northeast-1a
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Public-Subnet-1

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: ap-northeast-1c
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: Public-Subnet-2

  
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: Public-Route-Table

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociation1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetRouteTableAssociation2:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable

  
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ALB
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: ALB-SecurityGroup

  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for EC2
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref ALBSecurityGroup
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: EC2-SecurityGroup

  
  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Tags:
        - Key: Name
          Value: EC2-SSM-Role

  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2Role

  
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-070e0d4707168fc07  
      InstanceType: t3.micro
      SubnetId: !Ref PublicSubnet1
      SecurityGroupIds:
        - !Ref EC2SecurityGroup
      IamInstanceProfile: !Ref EC2InstanceProfile
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          
          
          mkdir -p /var/www/hoge
          mkdir -p /var/www/fuga
          
          
          cat > /var/www/hoge/pages/index.html !DOCTYPE html>
          >
          >
              >Hoge Site>
          >
          >
              >
              

>This is the content for hoge.example.com

> > > EOF cat > /var/www/fuga/index.html !DOCTYPE html> > > >Fuga Site> > > >

>This is the content for fuga.example.com

> > > EOF cat > /etc/httpd/conf.d/virtual-hosts.conf *:80> ServerName hoge.example.com DocumentRoot /var/www/hoge ErrorLog logs/hoge_error.log CustomLog logs/hoge_access.log combined > *:80> ServerName fuga.example.com DocumentRoot /var/www/fuga ErrorLog logs/fuga_error.log CustomLog logs/fuga_access.log combined > EOF systemctl restart httpd Tags: - Key: Name Value: Web-Server ApplicationLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Name: WebServer-ALB Scheme: internet-facing Type: application Subnets: - !Ref PublicSubnet1 - !Ref PublicSubnet2 SecurityGroups: - !Ref ALBSecurityGroup Tags: - Key: Name Value: WebServer-ALB TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Name: WebServer-TG Port: 80 Protocol: HTTP VpcId: !Ref VPC HealthCheckPath: / HealthCheckProtocol: HTTP HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 2 UnhealthyThresholdCount: 3 Targets: - Id: !Ref EC2Instance Port: 80 Listener: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref TargetGroup LoadBalancerArn: !Ref ApplicationLoadBalancer Port: 80 Protocol: HTTP Outputs: LoadBalancerDNS: Description: DNS name of the load balancer Value: !GetAtt ApplicationLoadBalancer.DNSName Export: Name: !Sub "${AWS::StackName}-LoadBalancerDNS" VPCId: Description: VPC ID Value: !Ref VPC Export: Name: !Sub "${AWS::StackName}-VPC"

上記を東京リージョンにデプロイすると Amazon Linux 2023 上で Web ページがホスティングされるのですが、バーチャルホストを構成しているのでホストヘッダーの内容によってレスポンスが変わります。

			
			% curl http://13.231.188.58/
html>body>h1>It works!/h1>/body>/html>
% curl http://13.231.188.58/ -H "Host:hoge.example.com"
html>body>h1>It works!/h1>/body>/html>
% curl http://13.231.188.58/ -H "Host:fuga.example.com"
!DOCTYPE html>
html>
head>
    title>Fuga Site/title>
/head>
body>
    h1>Welcome to Fuga Site/h1>
    p>This is the content for fuga.example.com/p>
/body>
/html>

		

良いですね。
つづいて、ALB のターゲットに EC2 を設定しているのでこちらでも試してみます。
ALB ではexample.comホストヘッダー形式でのリクエストを想定しています。

			
			% curl http://WebServer-ALB-2001862097.ap-northeast-1.elb.amazonaws.com/ -H "Host:hoge.com"
html>body>h1>It works!/h1>/body>/html>
% curl http://WebServer-ALB-2001862097.ap-northeast-1.elb.amazonaws.com/ -H "Host:fuga.com"
html>body>h1>It works!/h1>/body>/html>

		

デプロイされたロードバランサーに新しいリスナールールを追加します。
その中でトランスフォームの設定をするのですが、今回は次のようにトランスフォームを1件追加し、ホストヘッダー正規表現と置換後の値を設定しました。

5C7ACEAE-F33C-4803-9E80-96C0130FA09C.png

設定箇所の右側の矢印を選択すると変換のテストを行うことができますので試してみましょう。
hoge.comを入力するとhoge.example.comに変換されました。

C2B9F860-4F4A-4694-B1AE-3D8937218FEF.png

期待した変換の挙動をしていそうですね。
設定後はリスナールール一覧からは次のように確認ができます。
なお、ひとつのルールのトランスフォームに対して複数のホストヘッダー書き換えルールを設定することは出来ませんのでうまく正規表現一本で行う必要があります。

82896E0E-E068-4204-A0D3-1BEEBF013906.png

設定後、また cURL でアクセスしてみましょう。ホストヘッダーを指定します。

			
			% curl http://WebServer-ALB-2001862097.ap-northeast-1.elb.amazonaws.com/ -H "Host:hoge.com"
html>body>h1>It works!/h1>/body>/html>
% curl http://WebServer-ALB-2001862097.ap-northeast-1.elb.amazonaws.com/ -H "Host:fuga.com"
!DOCTYPE html>
html>
head>
    title>Fuga Site/title>
/head>
body>
    h1>Welcome to Fuga Site/h1>
    p>This is the content for fuga.example.com/p>
/body>
/html>

		

ALB 経由でホストヘッダーがリライトされターゲットに転送されていることが確認出来ましたね。

さいごに

本日は Application Load Balancer のリスナールールでトランスフォームを構成し、ターゲットにルーティングする前にホストヘッダーや URL パスを書き換えれるようになったので試してみました。

パス、クエリ文字列、ホストヘッダーの変更などを ALB にオフロードできるようになりました。
これまで細かい調整が必要でアプリケーションコードの実装やリバースプロキシの追加導入をしていた場合は今回の機能のみで今後は対応できるようになりそうですね。


元の記事を確認する

関連記事