
VPC Origin Now Supports Dual Stack. I Tried to See If CloudFront Origin Requests Can Reach ALB via IPv6
This page has been translated by machine translation. View original
Introduction
On October 15, 2026, dual-stack support was added for CloudFront VPC origins.
With this dual-stack support, IPv6 can now be used end-to-end from CloudFront all the way to ALBs, NLBs, and EC2 instances on private subnets. Previously, this segment of communication was IPv4 only.
In this article, we configured an internal ALB as a VPC origin in the Tokyo region and confirmed from the ALB access logs that CloudFront's IPv6 address is recorded as the connection source.
Architecture
We created two private subnets in a VPC with IPv6 enabled, and pointed the IPv6 default route (::/0) to an Egress-only IGW. Inside the subnets, we placed an internal ALB (dual-stack, returning a fixed 200 response on HTTP:80) and delivered the ALB access logs to CloudWatch Logs.
The ALB's SG allowed only TCP 80 from the IPv6 prefix list com.amazonaws.global.ipv6.cloudfront.origin-facing.
Creating a VPC origin requires attaching a regular IGW to the VPC and reserving at least one available IPv4 and IPv6 address in the private subnets. These available addresses are used for service-managed ENIs created by CloudFront.
No route to the regular IGW is created, and the IPv6 default route points to the Egress-only IGW.
Confirming IPv6 in the ALB Access Logs
When a request was made to the CloudFront distribution's domain name, the ALB's fixed response returned 200.
$ curl -sS -i https://dxxxxxxxxxxxxx.cloudfront.net/
HTTP/2 200
x-cache: Miss from cloudfront
hello from internal dualstack ALB
The ALB access log (CloudWatch Logs) recorded this request with the following entry.
{
"time": "2026-10-15T20:24:07.380120Z",
"user_agent": "Amazon CloudFront",
"client_port": "2001:db8:17bd:2f73:3ceb:b969:b646:3200:25216",
"elb_status_code": "200",
"ip_address": "192.168.28.87"
}
client_port is the source address and port. The address has been replaced with a documentation address for illustration purposes. In all 4 access log entries reviewed, the IP address portion of client_port (before replacement) was IPv6, user_agent was Amazon CloudFront, and elb_status_code was 200.
Summary
We confirmed that using a dual-stack VPC origin, CloudFront can connect via IPv6 to an internal ALB on a private subnet whose IPv6 default route points to an Egress-only IGW.
This is useful when building a network that uses end-to-end IPv6 communication, or when you want to consolidate external access through CloudFront. Since the origin can remain in a private subnet while being accessible only through CloudFront, it also reduces the risk of direct access due to security group misconfigurations.
CloudFormation Template
The template and stack creation command used for verification are as follows.
template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC origin dualstack verification (IPv6 VPC, internal dualstack ALB, CloudFront VPC origin, and ALB access logs)
Parameters:
ProjectName:
Type: String
Default: vpco-ds
CloudFrontIpv6PrefixListId:
Type: String
Default: pl-0f28cd4a128e7b13a
Description: com.amazonaws.global.ipv6.cloudfront.origin-facing
CloudFrontIpv4PrefixListId:
Type: String
Default: ''
Description: com.amazonaws.global.cloudfront.origin-facing (empty = do not allow IPv4)
Conditions:
AllowIpv4: !Not [!Equals [!Ref CloudFrontIpv4PrefixListId, '']]
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 192.168.0.0/18
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Ref ProjectName
IPv6CidrBlock:
Type: AWS::EC2::VPCCidrBlock
Properties:
VpcId: !Ref VPC
AmazonProvidedIpv6CidrBlock: true
PrivateSubnet1:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
CidrBlock: 192.168.0.0/20
AvailabilityZone: !Select [0, !GetAZs '']
Ipv6CidrBlock: !Select [0, !Cidr [!Select [0, !GetAtt VPC.Ipv6CidrBlocks], 2, 64]]
AssignIpv6AddressOnCreation: true
Tags:
- Key: Name
Value: !Sub '${ProjectName}-private-1'
PrivateSubnet2:
Type: AWS::EC2::Subnet
DependsOn: IPv6CidrBlock
Properties:
VpcId: !Ref VPC
CidrBlock: 192.168.16.0/20
AvailabilityZone: !Select [1, !GetAZs '']
Ipv6CidrBlock: !Select [1, !Cidr [!Select [0, !GetAtt VPC.Ipv6CidrBlocks], 2, 64]]
AssignIpv6AddressOnCreation: true
Tags:
- Key: Name
Value: !Sub '${ProjectName}-private-2'
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub '${ProjectName}-igw'
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
EgressOnlyIGW:
Type: AWS::EC2::EgressOnlyInternetGateway
Properties:
VpcId: !Ref VPC
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${ProjectName}-private-rt'
PrivateRouteIPv6:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationIpv6CidrBlock: '::/0'
EgressOnlyInternetGatewayId: !Ref EgressOnlyIGW
PrivateSubnet1Association:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
PrivateSubnet2Association:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable
AlbSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow CloudFront origin-facing prefix lists to the internal ALB
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourcePrefixListId: !Ref CloudFrontIpv6PrefixListId
Description: CloudFront origin-facing (IPv6)
Tags:
- Key: Name
Value: !Sub '${ProjectName}-alb-sg'
AlbSecurityGroupIngressIpv4:
Type: AWS::EC2::SecurityGroupIngress
Condition: AllowIpv4
Properties:
GroupId: !Ref AlbSecurityGroup
IpProtocol: tcp
FromPort: 80
ToPort: 80
SourcePrefixListId: !Ref CloudFrontIpv4PrefixListId
Description: CloudFront origin-facing (IPv4)
Alb:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub '${ProjectName}-alb'
Scheme: internal
Type: application
IpAddressType: dualstack
Subnets:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
SecurityGroups:
- !Ref AlbSecurityGroup
AlbListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref Alb
Port: 80
Protocol: HTTP
DefaultActions:
- Type: fixed-response
FixedResponseConfig:
StatusCode: '200'
ContentType: text/plain
MessageBody: hello from internal dualstack ALB
AccessLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/vendedlogs/${ProjectName}-alb-access'
RetentionInDays: 1
AccessLogSource:
Type: AWS::Logs::DeliverySource
Properties:
Name: !Sub '${ProjectName}-alb-access'
LogType: ALB_ACCESS_LOGS
ResourceArn: !Ref Alb
CwlDestination:
Type: AWS::Logs::DeliveryDestination
Properties:
Name: !Sub '${ProjectName}-cwl'
DeliveryDestinationType: CWL
OutputFormat: json
DestinationResourceArn: !GetAtt AccessLogGroup.Arn
AccessLogDelivery:
Type: AWS::Logs::Delivery
Properties:
DeliverySourceName: !Ref AccessLogSource
DeliveryDestinationArn: !GetAtt CwlDestination.Arn
VpcOrigin:
Type: AWS::CloudFront::VpcOrigin
DependsOn: InternetGatewayAttachment
Properties:
VpcOriginEndpointConfig:
Name: !Sub '${ProjectName}-origin'
Arn: !Ref Alb
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: http-only
IpAddressType: dualstack
Distribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Comment: !Sub '${ProjectName} VPC origin dualstack verification'
Enabled: true
HttpVersion: http2and3
IPV6Enabled: true
Origins:
- Id: VpcOriginAlb
DomainName: !GetAtt Alb.DNSName
VpcOriginConfig:
VpcOriginId: !Ref VpcOrigin
DefaultCacheBehavior:
TargetOriginId: VpcOriginAlb
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # Managed-CachingDisabled
Compress: true
Outputs:
DistributionId:
Value: !Ref Distribution
DistributionDomainName:
Value: !GetAtt Distribution.DomainName
VpcOriginId:
Value: !Ref VpcOrigin
LogGroupName:
Value: !Ref AccessLogGroup
:::
Save it as template.yaml and create the stack.
aws cloudformation create-stack --stack-name vpco-ds --template-body file://template.yaml
aws cloudformation wait stack-create-complete --stack-name vpco-ds
