iCloud Private Relayを使ったアクセスは、Webサイトから見ると通常の家庭回線やモバイル回線とは少し違って見える。ユーザー本来のIPアドレスではなくPrivate Relayの出口IPが見え、同じ出口IPを複数のユーザーが共有することもある。

「小説家になろう」で遭遇した403を調べたときも、同じiPhone、同じSafari、同じURLでありながら、Private Relayの出口IPによって正常表示と403が分かれた。

Webサービスでは接続元IPをWAF、Rate Limit、不正検知、GeoIP、アクセス解析など幅広い用途で使っている。そのためPrivate Relayへの対応も、単に特定のIPを許可する話では終わらない。IPをどの用途で使っているかを分けて確認し、それぞれの判定方法をPrivate Relayの共有IPに合わせて調整する必要がある。

AppleはWebサイト運営者向けにPrivate Relayの出口IPと位置情報を公開しており、IPベースの不正検知やGeoIPの扱いについても案内している。Appleのサポート文書では、IPフィルタリング、監視、Rate Limitを前提とするWebサイトでPrivate Relay利用中のアクセスが阻止される場合があることも説明されている。

Private Relayへの対応は2つに分かれる

Webサービス側の方針は、Private Relayを共有IPとして受け入れる場合と、サービス要件としてPrivate Relayを経由しない接続元IPを必要とする場合に分かれる。

Private Relayを識別
        ↓
サービスの方針
  ├─ 利用を受け入れる
  │    → 共有IPとして扱う
  │    → WAF / Rate Limit / GeoIPを調整
  │
  └─ Private Relayを経由しない接続元IPが必要
       → 403 Forbidden
       → 専用の説明ページ
       → Safariの「IPアドレスを公開」を案内

どちらの方針でも、最初に必要なのはPrivate Relayの出口IPを識別できることだ。そのうえで、受け入れる通信では共有IPとして判定方法を調整し、利用させない範囲では理由と対処方法を示した403を返す。

Private Relayを受け入れる場合は共有IPとして扱う

Private Relayを受け入れる場合も、通常のWAFやRate Limitはそのまま適用できる。違いは、出口IPを1人のユーザーとして扱わず、複数ユーザーが共有するネットワーク属性として判定する点にある。

AppleもPrivate RelayのIPをCarrier-Grade NATや企業ネットワークのような大規模共有IPとして扱うよう案内している。Private Relayであること自体を許可・拒否の結論にするのではなく、共有IPという属性として判定に加えると、WAF、Rate Limit、不正検知、アクセス解析でIPをどう使い分けるかが見えてくる。

Private Relayの出口IP
        ↓
共有IPとして認識
        ↓
IP単独ではなく
Session / Account / Request Pattern などと組み合わせる

Webサイトから見えるのはPrivate Relayの出口IP

Private Relayでは、Safariから送られた通信が2つのリレーを経由してWebサイトへ届く。Webサイト側で見えるIPの意味を考えるには、この経路を押さえておく必要がある。

iPhone / iPad / Mac
        ↓
1つ目のリレー
        ↓
2つ目のリレー
        ↓
Webサイト

Webサイトから見えるのは2つ目のリレーから割り当てられたIPアドレスで、ユーザー本来のIPアドレスは見えない。Appleによると、このリレーIPは同じ地域にいる複数のPrivate Relayユーザーで共有される場合があり、ブラウジングセッション中はWebサイトに対して安定したIPが使われる。

従来のIPベースの実装では、1つのIPを1人の利用者に近い単位として扱っていることがある。

203.0.113.10
└─ 1ユーザー

Private Relayでは1つの出口IPを複数ユーザーが共有するため、同じ前提でRate Limitや不正検知を行うと閾値の意味が変わる。

リレーIP
├─ User A
├─ User B
├─ User C
└─ ...

AppleはPrivate Relayの出口IPを公開している

AppleはPrivate Relayの出口IPと位置情報をCSVで公開している。CSVにはIPプレフィックスと国・地域・都市に関する情報が含まれており、Webサービス側では接続元IPがPrivate Relayに含まれるかを確認できる。

Appleが公開しているPrivate Relayの出口IP一覧(CSV)

接続元IP
  ↓
AppleのPrivate Relay IP一覧
  ↓
一致
  ↓
Private Relayとして扱う

Cloudflare、Akamai、FastlyなどのASNをPrivate Relay判定としてハードコードするより、Appleが公開するリストを使う方が範囲を正確に絞れる。これらのネットワークはPrivate Relay以外の用途でも使われるため、ASN全体をPrivate Relayとして扱うと対象が広がりすぎる。

手元で1つのIPを確認する

指定したIPがAppleのPrivate Relay一覧に含まれるかを確認するだけなら、Pythonの標準ライブラリで試せる。次のコードは手元での確認用で、HTTPリクエストごとに実行する用途ではない。

#!/usr/bin/env python3

import csv
import io
import ipaddress
import sys
import urllib.request

URL = "https://mask-api.icloud.com/egress-ip-ranges.csv"

if len(sys.argv) != 2:
    print(f"usage: {sys.argv[0]} <ip-address>", file=sys.stderr)
    raise SystemExit(2)

target = ipaddress.ip_address(sys.argv[1])

with urllib.request.urlopen(URL, timeout=30) as response:
    reader = csv.reader(io.TextIOWrapper(response, encoding="utf-8"))

    for row in reader:
        if not row:
            continue

        try:
            network = ipaddress.ip_network(row[0], strict=False)
        except ValueError:
            continue

        if target.version != network.version:
            continue

        if target in network:
            country = row[1] if len(row) > 1 else ""
            region = row[2] if len(row) > 2 else ""
            city = row[3] if len(row) > 3 else ""

            print("private_relay=true")
            print(f"network={network}")
            print(f"country={country}")
            print(f"region={region}")
            print(f"city={city}")
            raise SystemExit(0)

print("private_relay=false")

このコード例のCIDR照合部分はサンプルデータで動作確認している。Appleの配布形式は固定されたAPIスキーマとして公開されているわけではないため、本番の取り込み処理では列数とCIDR形式を検証し、形式が変わった場合は前回の正常データを維持する構成にしたい。Appleのエンドポイントから実データを取得するところまで含めたE2E確認は未実施。

例えば 203.0.113.10 を確認する場合は、次のように実行する。

python3 check_private_relay.py 203.0.113.10

Appleの一覧には多数のIPv4・IPv6プレフィックスが含まれるため、本番ではリクエストのたびにCSVをダウンロードして全件走査する構成にはしない。判定用データを定期更新し、WebやWAFから高速に参照できる形へ変換しておく。

本番ではIPリストを定期更新する

本番環境では、AppleのCSVを定期ジョブで取得し、CIDRを検証したうえでPrefix TreeやRadix Treeなど検索しやすい形式へ変換する。更新したデータは一度に切り替え、取得や検証に失敗した場合は前回の正常データを使い続ける構成にしておく。

定期ジョブ
  ↓
AppleのCSVを取得
  ↓
形式とCIDRを検証
  ↓
IPv4 / IPv6のPrefix Indexを生成
  ↓
新しいデータへAtomic Swap
  ↓
Web / WAF / API Gatewayから参照

取得や検証に失敗した場合は、新しいデータへ切り替えず前回の正常データを維持する。更新ジョブの失敗は監視へ通知し、判定データの欠落と切り離して扱う。

新しいCSVの取得に失敗
        ↓
前回の正常なデータを継続利用
        ↓
監視へ通知

更新頻度はシステム要件に合わせればよい。例えば1日1回取り込み、取得時刻やデータの世代も記録しておけば、判定に使っているPrivate Relayリストがいつのものか後から確認できる。

GeoIPサービスを利用している場合は、自前でCSVを処理せず、そのサービスがAppleのPrivate Relay情報を取り込んでいるかを確認する方法もある。AppleもGeoIPデータベース事業者へ最新のマッピングを確認するよう案内しており、MaxMindはAppleが公開するPrivate RelayのGeoIPフィードを自社データへ取り込んでいると説明している。

WAFではPrivate Relayを1つの属性として扱う

WAFでは、Hosting Provider、Anonymous Proxy、ASN、IP Reputationなどをアクセス制御の条件に使うことがある。Private Relayの出口もこうした分類に該当する場合があり、AppleはGeoIPデータベースによって is_relay、is_hosting、privacy_service、privacy_proxy などの属性が付くことがあると説明している。

Private Relayへの対応では、これらを単独のBlock条件にせず、リクエストの内容や頻度と組み合わせる。既知の攻撃パターンは従来どおり止め、Private Relayという属性はRate Limitや追加判定の材料として使う。

既知の脆弱性パターン
→ Block

明らかなProtocol違反
→ Block

Private Relay
+ 短時間の大量リクエスト
+ 同一セッション
+ 同一エンドポイント
→ Rate Limit

Private Relay
+ 通常の閲覧
→ 通常アクセス

この形なら、Private Relay利用者も通常のセキュリティ制御の対象にしたまま、共有IPという性質だけを判定へ反映できる。

Private Relayを利用させない方針なら専用の403を返す

サービスの設計上、Private Relayを経由しない接続元IPを確認する必要があり、Private Relay経由の利用を受け付けないケースもある。Appleも、IPフィルタリング、監視、Rate Limitを前提とするWebサイトではPrivate Relay利用中の閲覧が阻止される場合があることを案内しており、Safariにはそのサイトに一時的にIPアドレスを公開する機能が用意されている。

2026年9月時点のAppleの公開仕様では、WebサイトがHTTPレスポンスヘッダーやJavaScriptからSafariへ「このサイトにIPアドレスを公開しますか」といったネイティブの許可UIを要求する仕組みは確認できない。Appleがネットワーク運用者向けに案内しているDNSベースのPrivate Relay無効化通知はネットワーク単位の仕組みで、Webサイト単位の「IPアドレスを公開」とは別になる。

そのためWebサイト側でPrivate Relayを拒否する場合は、AppleのPrivate Relay出口IP一覧で判定したうえでHTTPとして 403 Forbidden を返し、そのレスポンス内でSafariの操作方法まで案内する。Nginxの標準エラーページだけを表示するより、Private Relayを利用できない理由と、Safariの「IPアドレスを公開」で再読み込みできることを同じ403レスポンス内で案内すると、利用者側でも原因を判断できる。

Request
  ↓
AppleのPrivate Relay出口IPに一致?
  ├─ No
  │    ↓
  │  通常処理
  │
  └─ Yes
       ↓
     403 Forbidden
       ↓
     Private Relayを利用できない理由
     「IPアドレスを公開」の案内

ここでは別URLへ302で飛ばすより、元のリクエストに対するステータスを403のまま維持する方がHTTP上の意味も明確になる。説明ページを表示していても、そのURLへのアクセスが現在の接続条件では拒否されていることをサーバー、ブラウザ、監視側で同じように扱える。

Nginxで専用の403ページを返す例

NginxにはCIDRをもとに変数を設定する geo ディレクティブがあり、大量のネットワークは外部ファイルから include できる。AppleのCSVを定期取得する処理で、Private RelayのCIDRを次の形式へ変換しておく。

# /etc/nginx/private-relay-ranges.conf

192.0.2.0/24 1;
198.51.100.0/24 1;
2001:db8::/48 1;

上のCIDRは設定形式を示すための例で、実際の値にはAppleが公開している最新の出口IP一覧を使う。

Nginx側では、Private Relayに一致したリクエストだけを内部用のステータスへ振り分け、最終レスポンスを403へ変換できる。

http {
    geo $is_private_relay {
        default 0;
        include /etc/nginx/private-relay-ranges.conf;
    }

    server {
        error_page 418 =403 /private-relay-blocked.html;

        location / {
            if ($is_private_relay) {
                return 418;
            }

            proxy_pass http://app;
        }

        location = /private-relay-blocked.html {
            internal;
            root /usr/share/nginx/html;
            add_header Cache-Control "no-store" always;
        }
    }
}

ここで418はクライアントへ返すステータスではなく、Private Relay用のエラー処理へ入れるための内部的な分岐に使っている。判定は通常のアプリケーションへ流す location / の中で行い、内部的に表示する説明ページでは同じ判定を繰り返さない。最終的にクライアントへ返るのは403なので、通常の403までPrivate Relay用ページへ置き換えずに済む。

この設定例はNginx 1.26.3で nginx -t による構文確認を行った。実際のCDNやReverse Proxyを含む構成での通信確認は行っていない。

Nginxの error_page はエラー時のURIを内部的に切り替えながらレスポンスコードを指定でき、internal を付けたlocationは外部から直接アクセスできない。 geo はCIDRによるIP判定とファイルの include に対応している。

専用403ページがCSSや画像を別URLから読み込む場合、そのリクエストも通常の location / に入りPrivate Relay判定の対象になる。説明ページを単一HTMLで自己完結させるか、403ページ専用の静的アセットを判定対象から外すlocationを用意しておく。

CDNやReverse Proxyの後ろにNginxがある場合は、Private Relay判定に使うIPが実際のクライアントIPになっていることも確認する。Nginxから見た $remote_addr がCDNやロードバランサーのIPのままなら、Appleの出口IP一覧と照合しても判定できない。信頼するProxyとクライアントIPの受け渡しを先に設定したうえで、その値を判定に使う。

サイト全体ではなく必要な機能だけ拒否する方法もある

Private Relayを経由しない接続元IPが必要なのが決済、不正利用対策の強い操作、社内向け機能など一部だけなら、その範囲だけPrivate Relayを拒否する構成も取れる。

商品閲覧
→ Private Relayでも利用可能

ログイン
→ Private Relayでも利用可能

決済
→ Private Relayを経由しない接続元IPが必要
→ Private Relayなら403 + 案内

一方、サービス全体でPrivate Relayを経由しない接続元IPの確認が要件になっているなら、入口でPrivate Relayを判定して全ページを対象にできる。どちらの場合も、ASNやCloudflareなどの事業者単位で広く拒否するより、Appleが公開するPrivate RelayのCIDRを対象にした方が、運用ポリシーと実際の制御範囲を合わせやすい。

専用403ページには、少なくとも「Private Relay経由では利用できないこと」「SafariでIPアドレスを公開すると利用できること」を書いておく。端末全体のPrivate Relayを無効にする手順まで要求せず、Appleが用意しているサイト単位の切り替えを案内すれば、他サイトではPrivate Relayを有効なまま利用できる。

Rate LimitはIP以外の単位も持つ

Private Relayでは複数ユーザーが同じ出口IPを共有することがあるため、100 requests / minute / IP のようなIP単位のRate Limitだけでは巻き込みが起きやすい。1人のアクセス量が増えたとき、同じリレーIPを使っている別のユーザーも同じ上限を共有するためだ。

ログイン済みのAPIなら、Account、Session、IP、Endpointを組み合わせてRate Limitを持てる。

Account
+
Session
+
IP
+
Endpoint

匿名アクセスでも、Session Cookie、IP、Request Pattern、Endpointなどを組み合わせられる。

Session Cookie
+
IP
+
Request Pattern
+
Endpoint

IP単位の制限そのものは、サービス全体を守る粗い上限として残せる。その上で、アカウントやセッションにはより細かい上限を設定し、共有IPにだけ過度な負担が集中しないようレイヤーを分ける。

1アカウントの細かい上限
1セッションの上限
1 IPの緩い上限
サービス全体の上限

Bot・不正検知ではIP Reputationを補助情報にする

Botや不正利用の判定では、IP Reputationだけでなく、アカウント、セッション、リクエスト頻度、HTTPメソッド、アクセス先、直前までの操作、Challengeの結果など複数の情報を組み合わせる。

Private Relayかどうかも、その中の1つの属性として扱える。IPを主役にするのではなく、ネットワーク側の状況を示す補助情報として位置づける。

判定材料役割
IP / ASN / Reputationネットワーク側の傾向を見る
Account / Session同じ利用主体の継続した動きを見る
Request Rate短時間の異常な増加を見る
URL / Method操作内容を見る
Challenge自動化されたアクセスかを追加確認する
Private Relay共有IPとして扱うための補助情報にする

Appleは、IPアドレスだけに頼らず正当なデバイスからのリクエストを確認する方法としてPrivate Access Tokensも案内している。

Private Access TokensはPrivacy Passを基盤としており、ユーザーを追跡する識別子をWebサイトへ渡さずに、正当なデバイスからのアクセスであることを確認する仕組みだ。これはWAFやRate Limitを置き換えるものではなく、IP以外の判定材料を追加する選択肢になる。

GeoIPではIPの所有者とユーザーの地域を分ける

Private Relayの出口IPをWHOISやASN情報で調べると、Cloudflareなど出口ネットワークを運用している事業者の情報が出てくる。ここで得られる事業者の所在地と、Private Relay利用者の地域は別の情報になる。

例えば、出口ネットワークの事業者情報とPrivate Relayが示す利用地域は別々に扱う。

接続元IP
↓
ASN / Organization
↓
Cloudflare
↓
米国企業
同じ接続元IP
↓
Apple Private Relayの位置情報
↓
JP

AppleはPrivate RelayでWebサーバーへ提示するIPについて、ユーザーの地域を維持するよう設計している。マーケティングや地域別コンテンツで知りたいのが「どの国の利用者か」であれば、ASNの登録国よりPrivate Relay向けに割り当てられた地域情報を使う。

GeoIP DBとAppleの情報が違う場合

古いGeoIPデータやIPの登録情報をそのまま使う仕組みでは、Private Relayの出口IPを利用者とは別の国へ分類する場合がある。

client_ip
↓
既存のGeoIP DB
↓
US

同じIPがAppleのPrivate Relayフィードでは日本向けに割り当てられているなら、地域別コンテンツやマーケティング集計ではApple側の情報を反映する。

client_ip
↓
Apple Private Relay feed
↓
JP

このとき、元のIPアドレスそのものは変更せず、観測した値と解析用の値を分けて持つ。元データを残しておけば、GeoIPのルールを変更したあとでも再集計できる。

client_ip = 実際に観測したIP
geo_country = 解析で利用する国
geo_source = apple_private_relay

これなら、「なぜこのアクセスを日本として集計したのか」までログから追跡できる。

ログでは観測値と解析値を分ける

Private Relay対応後の検証や再集計を考えると、ログには接続時に観測した値と、解析用に補正した値の両方を残しておきたい。ネットワーク属性とマーケティング用の地域情報を同じフィールドへ押し込まず、由来まで追える形にする。

client_ip
network_asn
network_organization

is_private_relay

raw_geo_country
resolved_geo_country
resolved_geo_region
resolved_geo_city
geo_source

Private Relayとして一致した場合は、例えば次のようになる。

client_ip:            実際にWebサーバーが見たIP
is_private_relay:     true

raw_geo_country:      既存GeoIPの結果
resolved_geo_country: Appleの割り当てを反映した結果
geo_source:           apple_private_relay

セキュリティ側では実際の接続元IPやASNを参照でき、データ分析では補正後の地域を使える。Private Relay経由のアクセスだけを抽出したり、GeoIPロジックを変更したあとに生ログから再集計したりする場合にも、この分離が役立つ。

地域情報は粒度も確認する

Private Relayはユーザーの正確な位置をWebサイトへ知らせる仕組みではない。AppleではIPアドレスの位置情報について「おおよその位置情報を保持」と「国と時間帯を使用」を選択でき、後者を選んだ場合は地域情報がさらに広い単位になる。

そのため、国別アクセス数のような集計と、都市単位のマーケティング分析では期待できる精度が異なる。Private Relayに限らず、IP GeoLocationはGPSのような位置情報ではないため、分析側ではデータの粒度を明示して使う方がよい。

ユニークユーザー(UU)はIP以外の識別方法を使う

Private Relayでは1つのIPを複数ユーザーが共有するため、ユニークIP数をそのままユニークユーザー数として扱うと誤差が大きくなる。

これはNAT、携帯キャリア、企業ネットワークでも以前からある問題だが、Private Relayでは共有IPという性質がより明確になる。UUを集計する場合は、ログインアカウント、セッション、ファーストパーティCookie、Analytics側のユーザー識別など、用途に合った方法を使う。

マーケティング基盤には、例えば次のようにPrivate Relay経由であることも残せる。

country = JP
is_private_relay = true

通常アクセスとPrivate Relay経由を分けて確認できれば、GeoIP補正の影響や比率も後から追える。

既存サービスで確認する箇所

既存サービスでは、IPアドレスを参照している箇所を洗い出すとPrivate Relayの影響範囲が見えてくる。

確認箇所Private Relay対応で見る内容
利用ポリシーPrivate Relayを受け入れるか、拒否する機能やURLがあるか
Firewall / WAFASN、Hosting、Proxy属性をどの条件で使っているか
Rate LimitIP単位の閾値と、Session / Account単位の閾値
Bot対策IP Reputation以外に利用している判定材料
GeoIPPrivate Relayの地域情報を反映できるデータか
Analytics利用者の国・地域をどのデータから決めているか
UU集計IPをユーザー識別に使っていないか
ログPrivate Relay判定とGeoIPの取得元を追跡できるか

WAFとアクセス解析は担当部署が分かれていることも多い。インフラ側でPrivate Relayのアクセスを正しく扱えていても、分析基盤が古いGeoIP DBを使っていれば、地域別集計には別の問題が残る。

最初に対応するなら3点

既存サービスへ段階的に取り込む場合は、最初に次の3点を決めておくと後続の実装を分けやすい。

  1. Appleのリストまたは対応済みGeoIP DBでPrivate Relayを識別できる状態を作る
  2. Private Relayを共有IPとして受け入れる範囲と、通常経路の接続元IPを必要とする範囲を決める
  3. ログへ is_private_relay、適用したポリシー、GeoIPの取得元を残す

受け入れる範囲ではWAF、Rate Limit、Bot対策、GeoIPの判定方法を調整し、利用させない範囲では専用403ページへつなぐ。この3点が入っていれば、問題が起きたときに「Private Relayだったか」「どの方針が適用されたか」「どの地域情報を使ったか」を追跡できる。

Private Relay対応ではIPの役割を分けて考える

IPアドレスは、攻撃元の調査、Rate Limit、地域判定など今でも多くの用途で使える。一方で、Private Relayのような共有IPを前提とすると、同じIPをすべての用途で同じ意味として扱うのは難しい。

Security
→ 実際の接続元IP、ASN、リクエストの挙動を見る

Rate Limit
→ IPに加えてSessionやAccountも見る

GeoIP
→ Appleが割り当てた地域情報を使う

Analytics
→ 観測値と解析用の値を分ける

AppleはPrivate Relayの出口IPと位置情報を公開しているため、Webサービス側でもPrivate Relayを識別できる。WAF、Rate Limit、GeoIP、アクセス解析をそれぞれ同じIP依存のロジックで処理するのではなく、用途ごとに必要な情報を使い分けることが、Private Relay対応の基本になる。