[アップデート] Amazon CloudFront が VPC オリジンの WebSocket をサポートしました

[アップデート] Amazon CloudFront が VPC オリジンの WebSocket をサポートしました

CloudFrontのVPCオリジンがWebSocketをサポートしたことで、WebSocketを扱う場合でもパブリックサブネットを廃止できるようになりました。
2026.05.25

CloudFrontのVPCオリジンを使ってパブリックサブネットを完全廃止したいけど、WebSocketを使いたいから完全廃止できない

こんにちは、のんピ(@non____97)です

皆さんはCloudFrontのVPCオリジンを使ってパブリックサブネットを完全廃止したいけど、WebSocketを使いたいから完全廃止できないとなったことはありますか? 私はあります。

CloudFrontのVPCオリジンを用いることによってGlobal Acceleratorを用いずにインターナルなALBやNLB、EC2インスタンスをCloudFrontのオリジンとして用いることができます。

https://dev.classmethod.jp/articles/cloudfront-vpc-origins-internal-alb/

これにより、オリジンに直接アクセスされる経路を排除することはもちろん、簡単に指定したCloudFrontディストリビューションとのみオリジンと通信できるようになったり、パブリックIPv4アドレスのコストを抑えることができるようになったりと嬉しいことは多くあります。

しかし、VPCオリジンGA当初、WebSocket通信はサポートされていませんでした。

そのため、オリジンにて一部パスはWebSocketを用いる場合、ALB/NLBなどオリジンとなるリソースをパブリックサブネットに配置する必要がありました。これは困ります。

今回、Amazon CloudFront が VPC オリジンの WebSocket をサポートしました。

https://aws.amazon.com/jp/about-aws/whats-new/2026/05/amazon-cloudfront-websockets-vpc-origins/

これにより、WebSocketトラフィックを流すオリジンであってもパブリックサブネットで構成をすることができるようになりました。

実際に試してみました。

やってみた

検証環境

検証環境は以下のとおりです。

検証環境構成図.png

パブリックサブネットが存在しないVPCにおいて、ターゲットにWebSocketをおしゃべりするアプリケーションが動作しているECS Fargateに振り分けるインターナルALBをVPCオリジンとして指定します。

ちなみに、CloudFrontでWebSocketを使用する場合については以下AWS公式ドキュメントで言及されています。

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/distribution-working-with.websockets.html

全てのリソースはAWS CDKでデプロイしました

aws_cloudfront_originsというL2 Constructもあるので、非常にお手軽に実装できます。

使用したコードは以下GitHubリポジトリに保存しています。

https://github.com/non-97/aws-cdk-cloudfront-websockets-vpc-origins

リソース確認

作成されたリソースの確認です。

VPCのリソースマップを確認すると、IGWへのルートを持つルートテーブルが割り当てられたサブネット = パブリックサブネットが存在しないことが分かります。

1.VPCリソースマップ.png

VPCオリジンの設定は以下のとおりです。

「オリジンのWebSocket用に」という設定は特段ありません。

2.VPCオリジン.png
3.VPCオリジン2.png

CloudFrontディストリビューション側のオリジン設定も同じくです。

4.CloudFront distribution.png
5.オリジンを編集.png

動作確認

それでは動作確認です。

ブラウザで3つウィンドウを開き、さらにターミナルからもWebSocketで接続し、メッセージのやり取りをしました。

6.デモチャット.png

> pnpm dlx wscat -c wss://d1fhfqyui9h81a.cloudfront.net/websocket
Connected (press CTRL+C to quit)
< {"type":"welcome","id":"a83b55ae-92ba-4b01-8c0e-b4079569f24a","clientCount":4}
> こんにちは a83b55ae-92ba-4b01-8c0e-b4079569f24a です
< {"type":"echo","from":"a83b55ae-92ba-4b01-8c0e-b4079569f24a","message":"こんにちは a83b55ae-92ba-4b01-8c0e-b4079569f24a です"}
< {"type":"broadcast","from":"cdeed963-7774-4afb-a565-b9344ddfc327","message":"元気ですか?"}

特に問題なく、リアルタイムに投稿した文字列が各クライアントに反映されました。

また、最後にメッセージを投稿してからALBに設定した接続タイムアウト時間経過後、コネクションが切断されました。

7.切断- code=1006 reason=(none).png

一連の処理についてECS Fargateで動作しているアプリケーションが出力した一部ログは以下のとおりです。

{
    "level": "info",
    "time": "2026-05-25T13:02:54.569Z",
    "pid": 6,
    "hostname": "ip-10-10-8-174.ec2.internal",
    "id": "11e254eb-5557-40ca-8832-3c37f5ed37ea",
    "url": "/websocket",
    "userAgent": "<ユーザーエージェント>",
    "forwardedFor": "<クライアントのIPv6アドレス>, 130.176.119.40",
    "cloudfrontId": "snx71Wifama59uLy3vfwcHaHwHmRrcVUi-e8vZtyU1FpHh069dBYQA==",
    "clientCount": 1,
    "msg": "ws connected"
}
{
    "level": "info",
    "time": "2026-05-25T13:03:08.958Z",
    "pid": 6,
    "hostname": "ip-10-10-8-174.ec2.internal",
    "id": "cdeed963-7774-4afb-a565-b9344ddfc327",
    "url": "/websocket",
    "userAgent": "<ユーザーエージェント>",
    "forwardedFor": "<クライアントのIPv6アドレス>, 130.176.116.164",
    "cloudfrontId": "MraeHrTsMC2y5IHzWfDEVIKfI0tO2Pe47ev-C75yV822i2ANbcsmUw==",
    "clientCount": 2,
    "msg": "ws connected"
}
{
    "level": "info",
    "time": "2026-05-25T13:03:17.351Z",
    "pid": 6,
    "hostname": "ip-10-10-8-174.ec2.internal",
    "id": "3c237116-9d54-4647-8e56-464c1025a9f5",
    "url": "/websocket",
    "userAgent": "<ユーザーエージェント>",
    "forwardedFor": "<クライアントのIPv6アドレス>, 130.176.118.52",
    "cloudfrontId": "WEHsZ_fwFs8GOmrQbrighqlVkRAaRtURlpeKO0a9QPbvly5e8Pjdbw==",
    "clientCount": 3,
    "msg": "ws connected"
}
{
    "level": "info",
    "time": "2026-05-25T13:03:46.975Z",
    "pid": 6,
    "hostname": "ip-10-10-8-174.ec2.internal",
    "id": "11e254eb-5557-40ca-8832-3c37f5ed37ea",
    "bytes": 31,
    "isBinary": false,
    "msg": "ws message received"
}
{
    "level": "info",
    "time": "2026-05-25T13:03:46.976Z",
    "pid": 6,
    "hostname": "ip-10-10-8-174.ec2.internal",
    "id": "11e254eb-5557-40ca-8832-3c37f5ed37ea",
    "broadcastCount": 2,
    "msg": "ws message broadcasted"
}
{
    "level": "info",
    "time": "2026-05-25T13:04:04.623Z",
    "pid": 6,
    "hostname": "ip-10-10-8-174.ec2.internal",
    "id": "cdeed963-7774-4afb-a565-b9344ddfc327",
    "bytes": 31,
    "isBinary": false,
    "msg": "ws message received"
}
{
    "level": "info",
    "time": "2026-05-25T13:04:04.623Z",
    "pid": 6,
    "hostname": "ip-10-10-8-174.ec2.internal",
    "id": "cdeed963-7774-4afb-a565-b9344ddfc327",
    "broadcastCount": 2,
    "msg": "ws message broadcasted"
}
{
    "level": "info",
    "time": "2026-05-25T13:08:50.946Z",
    "pid": 6,
    "hostname": "ip-10-10-8-174.ec2.internal",
    "id": "71ca940a-8e32-4471-9938-9b9af4c18687",
    "code": 1006,
    "reason": "",
    "clientCount": 0,
    "msg": "ws closed"
}

また、接続時時に全てのヘッダーを出力するようにした場合は以下のようなログが出力されました。

{
    "level": "info",
    "time": "2026-05-25T13:42:28.172Z",
    "pid": 8,
    "hostname": "ip-10-10-8-98.ec2.internal",
    "id": "4ddd7969-9270-4ff6-b7e5-eadb48066e43",
    "method": "GET",
    "httpVersion": "1.1",
    "url": "/websocket",
    "headers": {
        "x-forwarded-for": "<クライアントのIPv6アドレス>, 130.176.114.52",
        "x-forwarded-proto": "http",
        "x-forwarded-port": "80",
        "host": "d1fhfqyui9h81a.cloudfront.net",
        "x-amzn-trace-id": "Root=1-6a1451c4-4e3233e1196d4b12222f32ce",
        "upgrade": "websocket",
        "connection": "upgrade",
        "sec-websocket-key": "Rnhou3qbnI2nVKLvsTWapg==",
        "sec-websocket-extensions": "permessage-deflate; client_max_window_bits",
        "cache-control": "no-cache",
        "pragma": "no-cache",
        "user-agent": "<ユーザーエージェント>",
        "via": "1.1 ece5d4a731ece5ff46c564ab2b946ede.cloudfront.net (CloudFront)",
        "x-amz-cf-id": "3QinF8qURdlfjvILoXcZZqalpUXpxOCEI4nLyjxNpODDTK1thF1D7w==",
        "sec-websocket-version": "13",
        "origin": "https://d1fhfqyui9h81a.cloudfront.net",
        "accept-language": "ja,en-US;q=0.9,en;q=0.8",
        "accept-encoding": "gzip, deflate, br, zstd"
    },
    "remoteAddress": "10.10.8.96",
    "remotePort": 10026,
    "remoteFamily": "IPv4",
    "clientCount": 1,
    "msg": "ws connected"
}

X-Forwarding-Forヘッダーの130.176.114.52はCloudFrontのオリジンのIPアドレスです。

> whois 130.176.114.52
% IANA WHOIS server
% for more information on IANA, visit http://www.iana.org
% This query returned 1 object

refer:        whois.arin.net

inetnum:      130.0.0.0 - 130.255.255.255
organisation: Administered by ARIN
status:       LEGACY

whois:        whois.arin.net

changed:      1993-05
source:       IANA

# whois.arin.net

NetRange:       130.175.0.0 - 130.176.255.255
CIDR:           130.176.0.0/16, 130.175.0.0/16
NetName:        AMAZO-4
NetHandle:      NET-130-175-0-0-1
Parent:         NET130 (NET-130-0-0-0-0)
NetType:        Direct Allocation
OriginAS:
Organization:   Amazon.com, Inc. (AMAZO-4)
RegDate:        2021-01-28
Updated:        2022-06-22
Ref:            https://rdap.arin.net/registry/ip/130.175.0.0

OrgName:        Amazon.com, Inc.
OrgId:          AMAZO-4
Address:        Amazon Web Services, Inc.
Address:        P.O. Box 81226
City:           Seattle
StateProv:      WA
PostalCode:     98108-1226
Country:        US
RegDate:        2005-09-29
Updated:        2026-04-17
Comment:        For details of this service please see
Comment:        http://ec2.amazonaws.com
Ref:            https://rdap.arin.net/registry/entity/AMAZO-4

OrgRoutingHandle: IPROU3-ARIN
OrgRoutingName:   IP Routing
OrgRoutingPhone:  +1-206-555-0000
OrgRoutingEmail:  aws-routing-poc@amazon.com
OrgRoutingRef:    https://rdap.arin.net/registry/entity/IPROU3-ARIN

OrgNOCHandle: AANO1-ARIN
OrgNOCName:   Amazon AWS Network Operations
OrgNOCPhone:  +1-206-555-0000
OrgNOCEmail:  amzn-noc-contact@amazon.com
OrgNOCRef:    https://rdap.arin.net/registry/entity/AANO1-ARIN

OrgRoutingHandle: ARMP-ARIN
OrgRoutingName:   AWS RPKI Management POC
OrgRoutingPhone:  +1-206-555-0000
OrgRoutingEmail:  aws-rpki-routing-poc@amazon.com
OrgRoutingRef:    https://rdap.arin.net/registry/entity/ARMP-ARIN

OrgDNSHandle: DNS1131-ARIN
OrgDNSName:   DNS
OrgDNSPhone:  +1-202-555-0000
OrgDNSEmail:  ipmanagement+dns@amazon.com
OrgDNSRef:    https://rdap.arin.net/registry/entity/DNS1131-ARIN

OrgAbuseHandle: AEA8-ARIN
OrgAbuseName:   Amazon EC2 Abuse
OrgAbusePhone:  +1-206-555-0000
OrgAbuseEmail:  trustandsafety@support.aws.com
OrgAbuseRef:    https://rdap.arin.net/registry/entity/AEA8-ARIN

OrgTechHandle: ANO24-ARIN
OrgTechName:   Amazon EC2 Network Operations
OrgTechPhone:  +1-206-555-0000
OrgTechEmail:  amzn-noc-contact@amazon.com
OrgTechRef:    https://rdap.arin.net/registry/entity/ANO24-ARIN

CloudFrontのVPCオリジンがWebSocketをサポートしたことで、WebSocketを扱う場合でもパブリックサブネットを完全に廃止できるようになった

CloudFront が VPC オリジンの WebSocket をサポートしたアップデートを紹介しました。

CloudFrontのVPCオリジンがWebSocketをサポートしたことで、WebSocketを扱う場合でもパブリックサブネットを完全に廃止できるようになりました。

コスト的にもセキュリティ的にもパブリックサブネットを廃止できるなら廃止したいところです。もし、WebSocket利用をしているがためにVPCオリジンを採用できなかった場合は今一度検討してみましょう。

AWS BlogsにパブリックオリジンからVPCオリジンに移行するステップを紹介した記事も参考になるかと思います。

https://aws.amazon.com/jp/blogs/news/migrate-amazon-cloudfront-public-origins-to-private-vpc-origins/

推奨は以下記事でも紹介されているCloudFrontの継続的デプロイを用いる方式のようです。

https://dev.classmethod.jp/articles/amazon-cloudfront-continuous-deployment/

この記事が誰かの助けになれば幸いです。

以上、クラウド事業本部 コンサルティング部の のんピ(@non____97)でした!

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事