目次
- IPv6アドレスの構造と表記・省略規則
- 128ビットを16ビットずつ8つに区切って16進数で書く
- プレフィックスとインターフェースID:/64が基本単位
- アドレスの種類:リンクローカルとグローバル
- 1つのインターフェースが複数のアドレスを持つのが普通
- IPv4との思想の違い:NATを前提にしない
- すべての端末がグローバルアドレスを持つ
- デュアルスタック運用とAAAAレコード
- IPv4とIPv6を同時に提供するのが現実解
- クラウドと社内ネットワークでの現状
- コードでのIPv6対応:落とし穴と正しい扱い方
- IPv4だけを想定したコードが壊れる典型パターン
- トラブル事例:AAAAレコードを追加したら一部ユーザーが遅くなった
- 症状
- 原因
- 対処
- まとめ
「アクセスログに2400:4050:...のようなコロン区切りのアドレスが混ざり始め、IPで集計するスクリプトが壊れた」「管理画面のIP制限にIPv6のユーザーが引っかかって入れない」。IPv6は“いつか来る未来”ではなく、日本ではモバイル回線や光回線の多くで既に標準になっており、Webサービス側が知らないうちにIPv6のトラフィックを受けています。
この記事では、IPv6の128ビットアドレスの表記と省略規則、リンクローカル・グローバルといったアドレスの種類、NATを前提としない設計思想、IPv4と併用するデュアルスタック運用とAAAAレコードの設定、そしてアプリケーションコードでIPv6を正しく扱うための落とし穴を、開発者の目線で整理します。IPアドレスの基礎とDNSレコードの設定はエンジニアのためのネットワーク基礎とドメインとDNS設定の完全ガイドを前提にしています。
IPv6アドレスの構造と表記・省略規則
128ビットを16ビットずつ8つに区切って16進数で書く
IPv4が32ビット(約43億個)だったのに対し、IPv6は128ビットです。アドレス数は2の128乗、およそ340澗(かん)個で、事実上無限と扱える規模です。表記は16ビットずつコロンで8つに区切り、各ブロックを16進数4桁で書きます。たとえば2001:0db8:0000:0000:0000:ff00:0042:8329のようになります。
このままでは長すぎるため、2つの省略規則があります。
- 各ブロックの先頭のゼロは省略できる。
0db8はdb8、0042は42、0000は0 - ゼロだけのブロックが連続する箇所を、1回だけ
::に置き換えられる。複数箇所にあるときは最も長い連続部分を置き換える(同じ長さなら最初のもの)。2回使うと復元できなくなるため禁止
上の例は2001:db8::ff00:42:8329になります。ループバックアドレス(IPv4の127.0.0.1に相当)は0000:...:0001なので::1、すべてのアドレスで待ち受ける「未指定アドレス」(IPv4の0.0.0.0に相当)は::と書きます。RFC 5952では「16進数は小文字」「省略は最大限行う」と表記の正規形が定められており、ログの突き合わせや比較をするときは、この正規形に揃えてから扱うのが鉄則です。
プレフィックスとインターフェースID:/64が基本単位
IPv6でもCIDRと同じく/nでネットワーク部の長さを表しますが、慣習的に上位64ビットがネットワークプレフィックス、下位64ビットがインターフェースID(端末を識別する部分)です。つまり、1つのサブネットは原則として/64で、その中に2の64乗個のアドレスがあります。IPv4のようにホスト数を数えてサブネットの大きさを決める作業は不要になり、「サブネットは/64で切る、組織や拠点には/48や/56をまとめて割り当てる」という単位で考えます。プレフィックス計算の考え方自体はIPv4と共通なので、不安があればサブネットマスクとCIDR表記の計算方法を参照してください。
アドレスの種類:リンクローカルとグローバル
1つのインターフェースが複数のアドレスを持つのが普通
IPv4では1つのNICに1つのアドレスが基本でしたが、IPv6では1つのインターフェースが用途の異なる複数のアドレスを同時に持ちます。ip -6 addrを打つと、最低でも次の2種類が見えるはずです。
| 種類 | 範囲 | 特徴 | IPv4での相当物 |
|---|---|---|---|
| リンクローカル | fe80::/10 | 同一リンク(同じスイッチ配下)内でのみ有効。ルーターを越えない。すべてのインターフェースが自動で持つ | 169.254.0.0/16(APIPA) |
| ユニークローカル(ULA) | fc00::/7(実際はfd00::/8) | 組織内でのみ使うプライベートアドレス。インターネットには経路がない | 10.0.0.0/8 など |
| グローバルユニキャスト | 2000::/3 | インターネット上で一意。ISPやクラウドから割り当てられる | グローバルIP |
| ループバック | ::1/128 | 自分自身 | 127.0.0.1 |
| ドキュメント用 | 2001:db8::/32 | 記事や設定例で使う予約範囲 | 192.0.2.0/24 など |
リンクローカルアドレスには「どのインターフェースの話か」を示すゾーンIDを%で付けることがあり、fe80::1%eth0のように書きます。ログにこの形式が出てきて正規表現が壊れる、というのはIPv6対応で最初に踏む落とし穴の1つです。また、IPv6にはブロードキャストがなく、ARPの代わりにNDP(近隣探索プロトコル)がマルチキャストで隣接端末を見つけます。
IPv4との思想の違い:NATを前提にしない
すべての端末がグローバルアドレスを持つ
IPv4の世界では、アドレス不足のために家庭やオフィスの端末はプライベートIPを持ち、ルーターのNATを通じて1つのグローバルIPを共有していました。IPv6ではアドレスが十分にあるため、原則としてすべての端末がグローバルアドレスを直接持ち、エンドツーエンドで通信します。NATがないので、ポートフォワーディングもポート枯渇も存在しません。NATの仕組みそのものはNATとポートフォワーディングの仕組みで解説しています。
ここで注意すべきは、「NATが外部からの接続を遮っていた」というIPv4の暗黙の安全装置がなくなる点です。IPv6ではファイアウォールがその役割を明示的に担います。家庭用ルーターは通常IPv6の着信をデフォルトで遮断していますが、クラウドのサーバーでIPv6アドレスを付与した場合、セキュリティグループやufwでIPv6のルールも書かなければ、IPv4では閉じていたポートがIPv6側で開いている、という事態になり得ます。
デュアルスタック運用とAAAAレコード
IPv4とIPv6を同時に提供するのが現実解
すべてのユーザーがIPv6を使えるわけではないため、サーバー側はIPv4とIPv6の両方で待ち受ける「デュアルスタック」が基本です。DNSにはIPv4向けのAレコードに加えてIPv6向けのAAAAレコードを登録し、クライアントは両方を引いて到達できる方を使います。現代のブラウザやOSは「Happy Eyeballs」という仕組みで、IPv6とIPv4への接続をほぼ同時に試し、先につながった方を採用するため、片方が不通でも致命的にはなりにくい設計です。ただし、IPv6が「届くがタイムアウトする」状態だと数百ミリ秒の遅延が毎回発生します。
サーバー側の対応は、待ち受けアドレスにIPv6を含めることと、ファイアウォールにIPv6のルールを追加することの2点です。Nginxの例を示します。
# Nginx: IPv4 と IPv6 の両方で待ち受ける
server {
listen 443 ssl;
listen [::]:443 ssl; # IPv6。これがないとAAAAレコードを引いた接続は失敗する
http2 on;
server_name example.com;
# ...
}
# 確認コマンド
dig AAAA example.com +short # AAAAレコードが返るか
ss -tlnp | grep ':443' # [::]:443 で LISTEN しているか
curl -6 -sI https://example.com/ # IPv6を強制して疎通確認
curl -4 -sI https://example.com/ # IPv4を強制
ping -6 example.com # ping6 とも
Linuxではデフォルトで[::]で待ち受けるとIPv4の接続も同じソケットで受け付ける(IPv4射影アドレス)ため、Node.jsなどでserver.listen(3000)とホストを省略した場合、::で待ち受けてIPv4からの接続を::ffff:203.0.113.5という形式で受け取ります。ログに「::ffff:」付きのアドレスが出るのはこのためで、バグではありません。
クラウドと社内ネットワークでの現状
AWSでは、VPCにIPv6のCIDRを追加し、サブネットに/64を割り当て、EC2やALBにIPv6アドレスを付与することでデュアルスタックにできます。AWSがパブリックIPv4アドレスに課金を始めたことをきっかけに、IPv6化を検討する企業も増えています。一方、社内ネットワークやVPNがIPv4のみという環境も依然多く、「外向きはデュアルスタック、内部はIPv4」という組み合わせが現実的な落としどころになるケースがほとんどです。
コードでのIPv6対応:落とし穴と正しい扱い方
IPv4だけを想定したコードが壊れる典型パターン
アプリケーション側でIPv6が問題になるのは、「IPアドレスは数字とドットでできている」という暗黙の前提が壊れるときです。代表的なものを挙げます。
- 正規表現:
\d{1,3}(\.\d{1,3}){3}のようなパターンはIPv6にマッチしない。ログ解析でIPv6の行が捨てられる、バリデーションで正当な入力が拒否される - DBのカラム長:
VARCHAR(15)にIPを保存していると、最長39文字(IPv4射影アドレス::ffff:255.255.255.255を含めると45文字)のIPv6が入らない。PostgreSQLならinet型を使うのが最善 - URLの書き方:ポート番号と区別するため、URL中のIPv6アドレスは角括弧で囲む。
http://[2001:db8::1]:8080/。ここを忘れると接続文字列のパースが壊れる - アクセス制限・レート制限の単位:IPv6ではユーザー1人が/64(2の64乗個)を持つため、「同じアドレス」で数えるとアドレスを変えるだけで制限を回避される。/64単位で集約して数える
- 表記の揺れ:
2001:DB8::1と2001:db8:0:0::1は同じアドレス。文字列比較で許可リストを判定していると一致しない。必ず正規化してから比較する
これらは、文字列としてIPを扱うのをやめ、言語標準のIPアドレス型に一度パースしてから処理することでほぼ解決します。
import ipaddress
def normalize(ip_str: str) -> str:
"""表記揺れを正規形に揃える。ゾーンIDやIPv4射影も吸収する"""
ip = ipaddress.ip_address(ip_str.split('%')[0])
if isinstance(ip, ipaddress.IPv6Address) and ip.ipv4_mapped:
ip = ip.ipv4_mapped # ::ffff:203.0.113.5 -> 203.0.113.5
return str(ip)
def rate_limit_key(ip_str: str) -> str:
"""IPv6は /64 単位でまとめてカウントする"""
ip = ipaddress.ip_address(normalize(ip_str))
if ip.version == 6:
return str(ipaddress.ip_network(f"{ip}/64", strict=False))
return str(ip)
allow = [ipaddress.ip_network(n) for n in ["203.0.113.0/24", "2001:db8:abcd::/48"]]
def is_allowed(ip_str: str) -> bool:
ip = ipaddress.ip_address(normalize(ip_str))
return any(ip in n for n in allow)
print(normalize("2001:DB8:0:0::1")) # 2001:db8::1
print(rate_limit_key("2001:db8:abcd:12::99")) # 2001:db8:abcd:12::/64
print(is_allowed("::ffff:203.0.113.7")) # True
レート制限の設計全般についてはAPIレート制限の設計と実装も参考にしてください。
トラブル事例:AAAAレコードを追加したら一部ユーザーが遅くなった
症状
IPv6対応のためにALBではなくEC2直下のNginxにIPv6アドレスを付与し、DNSにAAAAレコードを追加した。翌日から「サイトの表示が数秒待たされる」「たまにつながらない」という問い合わせが、特にスマートフォンのユーザーから入り始めた。IPv4で確認している社内からは再現しない。
原因
Nginxはlisten [::]:443を追加していたが、セキュリティグループのインバウンドルールは送信元0.0.0.0/0(IPv4のみ)のままで、IPv6の送信元::/0を許可していなかった。AAAAレコードを引いたモバイル回線のユーザーはまずIPv6で接続を試み、パケットが黙って捨てられてタイムアウトするまで待ってからIPv4にフォールバックしていた。Happy Eyeballsのおかげで「完全に見えない」事態は避けられたが、毎回の遅延と、フォールバックがうまく働かない一部端末での接続失敗を生んでいた。
対処
セキュリティグループに::/0からのTCP 443を追加し、curl -6で疎通を確認したところ即座に解消した。再発防止として、「IPv6を有効にする変更では、待ち受け設定・ファイアウォール・DNSの3点をセットで確認し、IPv6のみの回線(モバイルテザリングなど)から動作確認する」をリリース手順に追加した。監視でもcurl -6とcurl -4の両方で外形監視を行うよう変更している。
まとめ
IPv6は128ビットのアドレスを16進数8ブロックで表記し、先頭ゼロの省略と::による圧縮で短く書きます。1つのインターフェースがリンクローカルとグローバルなど複数のアドレスを持ち、サブネットは/64が基本単位です。NATを前提としないためファイアウォールで明示的に守る必要があり、サーバーはIPv4と併用するデュアルスタックで[::]での待ち受け・IPv6のファイアウォールルール・AAAAレコードを3点セットで整えます。コードでは文字列でIPを扱わず、標準のIPアドレス型で正規化してから比較・集計・制限を行うことで、正規表現やカラム長、レート制限の落とし穴を避けられます。まずは自分のサービスのアクセスログにIPv6がどれくらい混ざっているかを確認するところから始めてみてください。
IPv6対応やネットワーク構成の見直しについてお困りの際は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方
ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。
- プログラミング
localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します