Tony Wang9 分で読めますローカルでは動くのにサーバーでは403になるスクレイパー。その理由
ローカルでは動くのにサーバーからだと403? たいていはIPレピュテーション、TLSフィンガープリンティング、ヘッドレス検知のいずれかです。どれが原因かの見分け方と直し方を解説します。
あなたのスクレイパーはノートPC上では完璧に動きます。それをVPSやCIランナーにデプロイし、コードは何も変えていないのに、突然すべてのリクエストが 403 で返ってきます。コードは同一なので、バグのように感じられますが、たいていはバグではありません。アンチボットシステムは1つのリクエストを多数のシグナルで同時に判定しており、家庭用マシンからデータセンターへ移すと、そのうちのいくつかが同時に切り替わってしまうのです。
この記事では、どのシグナルが変化するのか、どれがあなたをブロックしているのかをどう見分けるか、そしてどう直すかを、公開データへの認可されたアクセス を前提に正確に解説します(この前提は最後まで誠実に守ります。ここで扱う内容は保護を突破することについてではありません)。
リクエストは2段階で判定される
検知が2段階で行われることを知っておくと役立ちます。
- 段階1 — HTMLが返される前。 IPレピュテーション、TLSハンドシェイク、HTTP/2の設定、ヘッダーの順序が、リクエストが完全に読み込まれる前に、接続そのものの上で受動的かつ低コストに調べられます。
- 段階2 — 段階1を通過した場合のみ。 JavaScriptがページ内で実行され、ブラウザのAPI、canvas/WebGL、挙動をチェックします。
データセンターからのリクエストは、たいてい 段階1 で息絶えます。だからこそ、CAPTCHAではなく チャレンジページを伴わないサイレントな403 が返ってくるのです。この1つの観察が、最初の診断として最も有効です。
チャレンジページのない403 → ネットワーク層(IPまたはTLS)で失敗している。 チャレンジページまたはCAPTCHAページ → ネットワーク層は通過し、ブラウザ/挙動チェックで失敗している。
なぜ同じコードがサーバーからだと403になるのか
1. IPレピュテーション — 最大にして唯一の理由
アンチボットシステムは、接続の ASN を最初の数ミリ秒で分類します。主要なクラウドとホスティングのネットワーク — AWS、GCP、Azure、Hetzner、DigitalOcean、OVH、そして GitHub Actions や Vercel の背後にあるCI/サーバーレスの範囲 — は、見た瞬間に事前フラグが付けられています。レジデンシャルやモバイルのIPはそうではありません。
IPレピュテーションは、あらゆる主要ベンダーが使う 唯一 のシグナルであり、いくつかのベンダーはJavaScriptチャレンジを返す 前に データセンターASNをブロックします。身も蓋もない事実として、クリーンなレジデンシャルIP上の素のHTTPクライアントは、フラグ付きのデータセンターIP上で完璧にパッチされたブラウザを、日常的に上回ります。あなたのノートPCはレジデンシャルIP上にあり、サーバーはそうではありません。それだけで、たいていの「ローカルでは動くのにサーバーだと403」は説明がつきます。
2. TLSフィンガープリンティング(JA3 / JA4)
TLSハンドシェイクそのものがクライアントを識別します。プロトコルは多くの有効な暗号スイート/拡張の組み合わせを許容しますが、各ブラウザは固定された、識別可能な組み合わせ — その JA3/JA4フィンガープリント — を使います。汎用クライアントには特徴的なハンドシェイクがあります。Pythonの requests/urllib はアンチボットベンダーが何百万回も見てきた静的なOpenSSLのClientHelloを送りますし、Goの net/http、Node、デフォルトの curl にもそれぞれ独自の、ブラウザではない形状があります。
決定打は 不一致 です。もし User-Agent ヘッダーが「Chrome 145」と言っているのに、TLSハンドシェイクが「Python」と言っているなら、その矛盾こそがそのリクエストが本物のブラウザではない決定的な証拠です。ノートPC上ではしばしば本物のブラウザ(あるいはTLSがたまたま一致するツール)でテストしますが、サーバーの素のクライアントはそうではありません。
3. HTTP/2フィンガープリントとヘッダーの順序
HTTP/2上では、クライアントは SETTINGS フレームの値、ストリームの優先度、そして 疑似ヘッダーの順序 によっても自らを露呈します。仕様は :method、:authority、:scheme、:path を最初に置くよう要求しますが、その順序は固定していないため、各ブラウザが1つを選びます(Chromeは m,a,s,p、Firefoxは m,p,a,s、Safariは m,s,p,a)。そしてほとんどのHTTPライブラリは どの ブラウザにも一致しない順序を使います。ヘッダーの 大文字小文字/順序 や、欠落した Sec-CH-UA / Sec-Fetch-* クライアントヒントが、このシグナルにさらに加わります。
4. ヘッドレス / 自動化の検知
これが、PlaywrightやPuppeteerがVPS上では良質なIPを使っても捕まる理由です。よく知られた navigator.webdriver やcanvasの手掛かり(今ではほとんど飽和状態)を超えて、根深いのは 自動化プロトコル です。Playwright、Puppeteer、SeleniumはいずれもDevTools Protocol経由でChromeを操作し、起動時に Runtime.enable を呼びます。これは数行のページ内JavaScriptで検知できます。決定的なことに、これは 完璧なレジデンシャルIPを持つ本物のマシン上でさえ 発火します。IPの問題ではなく、「ブラウザがどう操作されているか」の問題なのです。(nodriver のように、その起動時呼び出しなしで素のCDP経由でChromeを操作するツールは、これを回避します。)
5. 形状の一貫性 — 最も多くの人が見落とすもの
プロキシが書き換えるのは、あなたの 送信元IP だけです。TCPより上のすべては依然としてホスト上で発生します。TLS/JA4ハンドシェイク、HTTP/2の順序、navigator.platform とUA、canvas/WebGL/フォント(ホストのGPUとフォント)、画面サイズ。つまり、レジデンシャルプロキシの背後にあるLinux VPSは、「レジデンシャル」なIPからLinuxらしいブラウザを広告している — 本物のMacやWindowsの家庭用マシンが決して生み出さない矛盾です。実際には、プロキシ経由のLinuxマシンは、プロキシなしのノートPCよりも 厳しく ブロックされることがあります。これが「自分のマシンでは動くのにサーバーでは息絶える」の最もすっきりした説明です。
私が当たっているのはどのシステムか
ブロックページとクッキーが、たいてい対峙しているベンダーを教えてくれます。
| アンチボットシステム | 見分け方 | 主に何を手掛かりにするか |
|---|---|---|
| Cloudflare | CF-RAY ヘッダー、cf_clearance / __cf_bm クッキー | あなたのUAに一致するTLS/JA3 + HTTP/2、チャレンジ前にデータセンターASNをブロック |
| Akamai Bot Manager | _abck クッキー、akamai-bm-telemetry スクリプト | サーバー検証される挙動テレメトリ — コピーしたクッキーは効かない |
| DataDome | X-DataDome-* ヘッダー、datadome クッキー | リクエストごとのリアルタイムMLスコア、スライダーCAPTCHA |
| Imperva / Incapsula | reese84 チャレンジ、incap_ses_* クッキー | プルーフ・オブ・ワーク + 解く 必要のあるフィンガープリント(コピー不可) |
| PerimeterX / HUMAN | _px3 / _pxhd クッキー、px.js | まずバックエンドのIPスコア、次に挙動バイオメトリクス |
これらがウェブ全体でそれぞれどれくらい一般的かについては、私たちの Anti-Bot Adoption Index をご覧ください。到達可能なウェブの半分強がマネージドなアンチボットを稼働させており、その圧倒的多数がCloudflareです。
どの層がブロックしているかを見分ける方法
きれいな切り分けの階段です。各ステップはちょうど1つの変数だけを変えます。
- ベンダーを特定する — レスポンスのヘッダー/クッキーから(上の表)。それにより、どの層を疑うべきかがわかります。
- IPが原因か? 同じ スクリプトをノートPC(レジデンシャル)とサーバーから実行します。レジデンシャルでは動きサーバーで403 → IPレピュテーション。サーバーのリクエストをレジデンシャルプロキシ経由でルーティングして確認します。それで通るようになれば、IPが関門だったのです。
- TLSが原因か? 同じ IP上で、素のクライアントをブラウザのTLSを模倣するものに差し替えます。
# plain client — often 403 at the network layer
curl -s -o /dev/null -w "%{http_code}\n" https://example.com
# matched TLS fingerprint — passes where the plain client failed?
python -c "from curl_cffi import requests; \
print(requests.get('https://example.com', impersonate='chrome').status_code)"
模倣するクライアントが、requests/curl が403を受けた場所で通るなら、あなたの TLS/JA3フィンガープリント が関門でした。
- JSが必要か / ヘッドレスか? TLS模倣でもまだ失敗するなら、同じネットワークから 手動で本物のヘッドフル(画面ありの)ブラウザで ページを開きます。手動ブラウザは通るのに自動化は失敗する → IPやTLSではなく ヘッドレス / 自動化の検知 です。
- 形状の一貫性チェック。 レジデンシャルプロキシの背後のLinux VPSでまだブロックされている? 主張するUAとOSが一致するレジデンシャルマシンから同じジョブを実行します。それで通るなら、VPSの形状の不整合が問題だったのであり、修正は「別のプロキシを追加する」ではなく「レジデンシャルホスト上でブラウザを実行する」です。
どう直すか(ランク付け、誠実に)
- データセンターIPから抜ける。 レジデンシャル / ISP / モバイルのプロキシは、あらゆるベンダーがチェックする層、そしてほとんどのサーバーが最初に落ちる層を修正します。トレードオフはコストです(帯域はGB単位で従量課金されるため、JSの多いページは積み上がります)。信頼できるプロバイダーを使い、継続的なジョブではローテーションしましょう。
- 本物のブラウザのTLSフィンガープリントに一致させる。
curl_cffi(Python)、rnet/wreq(Rust)、tls-clientのようなライブラリは、ブラウザプロセスなしでブラウザと同一のClientHelloとHTTP/2設定を備えています — 高速かつ低コストです。制約として、JavaScriptエンジンがないため、JSでレンダリングされるページでは役に立たず、模倣もますます検知されるようになっています。ネットワーク層でのみ保護された 大多数 のページでは優秀ですが、万能薬ではありません。 - 本物のブラウザによるステルス + 賢いエスカレーション。 本当にJavaScriptが必要なページには、自動化の痕跡を取り除いた本物のChromeを操作し(例:
nodriver)、そして エスカレート します。まず安価なフィンガープリント付きのHTTPクライアントを試し、それを必要とする一部にだけステルスブラウザを立ち上げます。そのブラウザは、レジデンシャルIPを持つホスト上で実行しましょう(またしても形状の一貫性です)。 - チャレンジ。 ページがインタラクティブなチャレンジを提示する場合、誠実な道は、チャレンジを正当に実行する本物のブラウザでそれを解くことです。Akamaiの
_abckやImpervaのreese84は サーバー検証される ため、コピーしたトークンは効かず、レスポンスは本当に生成されなければなりません。これを公開データへの認可されたアクセスと位置づけましょう。決して 「CAPTCHAの突破」ではありません。 - マネージドAPIにオフロードする。 保守されたスクレイピング/アンブロッカーのAPIは、上記のすべてを1回の呼び出しの背後で処理します — レジデンシャルのローテーション、ブラウザに一致するTLS、本物のブラウザのフィンガープリント、セッションとチャレンジの処理。複数のベンダーに当たっているとき、スケールで動かしているとき、あるいは小さなチームで作業しているときには、これが正しい選択です。なぜなら、DIYの隠れたコストは 継続的なメンテナンス だからです。各ベンダーのチャレンジ更新が、あなたの回避策を一夜にして壊しかねません。(それが Crawlora が中心に据えて作られているトレードオフです — 成功時のみ課金され、アクセス経路はあなたのために保守されます。)
より大きな絵:ウェブは閉じつつある
この「ローカルでは動く」と「スケールで動く」の間の隔たりは、縮まっているのではなく 広がっています。2026年、CloudflareはデフォルトでAIクローラーをブロックし、ペイ・パー・クロールを展開しつつあり、到達可能なウェブのおよそ半分が今やマネージドなアンチボットを稼働させています。データセンターIPは有用性を失い続け、アンチボットのMLは適応し続け、DIYの回避策はより速く劣化していきます。誠実な選択肢は、これまでと同じ2つだけです — 本格的なインフラに投資する(レジデンシャルIP + 本物のChromeによるステルス + 賢いエスカレーション)か、マネージドサービスにオフロードするか。そしていずれにせよ、あなたがアクセスを認可された公開データのみを収集しましょう。
Sources
この記事が位置づけられる場所
作り始める前にチェック: 対象を無料の Anti-Bot Checker に通して、保護されているか、どのように保護されているかを確認し、各ベンダーがどれくらい一般的かを Anti-Bot Adoption Index で調べてみてください。これが一部を成すより広い変化については、なぜRedditは認証なしのJSONをブロックしたのか や、2026年にウェブスクレイピングは合法か をご覧ください。
プロキシ、TLS模倣、ヘッドレスのステルスを自分で保守するよりも任せたいときには、Crawlora は1つのキーからクリーンな構造化JSONを返し、サイトが締め付けを強めてもアクセス経路を機能させ続けます — Playground でエンドポイントを試し、料金ページ でクレジットのコストを確認してみてください。
よくある質問
スクレイパーがローカルでは動くのに、サーバーでは403になるのはなぜですか?
ノートPCからデータセンターへ移すと、いくつものシグナルが一度に切り替わります。アンチボットシステムはリクエストを多くの層で判定します — IPレピュテーション、TLSフィンガープリント、HTTP/2の形状、ヘッダー、ブラウザの操作のされ方 — そのいずれか1つでも通らなければ403を返します。家庭用マシンが通るのは、すべての層が本物のレジデンシャルブラウザと一貫しているからです。サーバーは少なくともIP(フラグ付きのデータセンター範囲)を変え、多くの場合TLS/JSフィンガープリントも変えてしまい、その不整合が捕まる原因になるのです。
ブロックしているのはIP、TLSフィンガープリント、それともヘッドレス検知のどれですか?
一度に1つの変数だけを変えて切り分けます。同じスクリプトをレジデンシャルIPとサーバーから実行します。レジデンシャルで動きサーバーで403なら、IPレピュテーションです。同じIP上で、素のクライアントをブラウザのTLSを模倣するもの(例: curl_cffiでimpersonate='chrome')に差し替えます。それで通れば、TLS/JA3フィンガープリントが原因でした。模倣でもまだ失敗するなら、手動で本物のヘッドフルブラウザでページを開きます。それは動くのに自動化は動かないなら、ヘッドレス/自動化の検知です。近道として、チャレンジページのない403はネットワーク層(IP/TLS)を意味し、CAPTCHAページはそれを通過してブラウザチェックで失敗したことを意味します。
レジデンシャルプロキシはサーバーでの403を直しますか?
多くの場合は直します — データセンターIPからレジデンシャルやISPのIPへ移すことは、あらゆるアンチボットベンダーがチェックする唯一のシグナル、そしてほとんどのサーバーが最初に落ちるシグナルを修正します。ただし、必要ではあっても常に十分とは限りません。TLSやヘッドレスのフィンガープリントが依然としてライブラリや自動化されたブラウザのように見えるなら、クリーンなIPだけでは通りません。またプロキシはIPを変えるだけなので、レジデンシャルプロキシの背後のLinux VPSは依然としてLinuxらしいTLS/JSフィンガープリントを漏らします。そのため、JSの多い対象では、レジデンシャルホスト上で本物のブラウザを実行する必要もあるかもしれません。
User-Agentヘッダーを追加しても403が直らないのはなぜですか?
User-Agentは最も弱いシグナルの1つであり、偽装するとかえって悪化しかねません。ヘッダーが「Chrome」と言っているのに、TLSハンドシェイク、HTTP/2の設定、ヘッダーの順序が「Python」と言っているなら、その不一致こそがあなたが本物のブラウザではない決定的な証拠です。ブロックはUser-Agent文字列ではなく、IPレピュテーションとフィンガープリントの層にあります — だから「User-Agentを追加する」「レートを落とす」では、実際に検知されているものに対処できないのです。
PlaywrightはなぜVPS上では良質なIPを使ってもブロックされるのですか?
わかりやすい手掛かり(navigator.webdriver、canvasの癖)を超えて、根深いのは自動化プロトコルです。Playwright、Puppeteer、SeleniumはDevTools Protocol経由でChromeを操作し、起動時にRuntime.enableを呼びます。これは数行のページ内JavaScriptで検知でき、完璧なレジデンシャルIPを持つ本物のマシン上でさえ発火します。IPの問題ではなく「ブラウザがどう操作されているか」の問題だからです。その呼び出しなしで素のCDP経由でChromeを操作するツール(nodriverなど)はこれを回避します。
このやり方で公開データをスクレイピングするのは合法ですか?
公開アクセス可能なデータの収集は広く擁護できますが、法律は何を収集しどう使うかで変わります — 各サイトの利用規約とrobotsディレクティブを尊重し、大規模な個人データや著作権コンテンツを避け、これらの技術をログインやペイウォールの突破に決して使わないでください。すべてを公開データへの認可されたアクセスと位置づけましょう。これは法的助言ではありません。