スクレイピング

古いユーザーエージェントのコスト:ブラウザバージョンのドリフトを測定する

スクレイパーはブラウザバージョンをハードコードしたまま放置することが多い。我々は最大6.5年前のChromeバージョンを名乗り、上位397サイトにリクエストを送った。拒否率は年数が経つごとに上昇した。

Chris Collins

2026年10月5日 · 5 分で読める

Every scraper that sets a User-Agent header picks a browser version, and almost none of them ever change it. The string goes into a config file the day the project starts, and the project keeps announcing itself as that browser for years. Meanwhile real browsers move on: Chrome shipped 13 major versions between the end of September 2025 and the end of September 2026.

Does the drift matter? Anti-bot systems know which browser versions real visitors use, so a version nobody has run for years stands out. We measured how much, by requesting the homepages of 397 top sites while claiming five different Chrome versions, from the current release to one six and a half years old.

Key takeaways

  • Older claimed versions were refused more often, every time. 17.1% of homepages challenged or blocked the current Chrome version; 19.9% refused a one-year-old version and 22.7% a six-and-a-half-year-old one.
  • The effect only went one way. 22 sites that served the current version refused the oldest one; no site refused the current version and served an old one.
  • Two identical requests for the current version produced identical outcomes on all 397 sites, so the differences are not noise.
  • Sites behind Akamai reacted first: 9 of the 11 sites that refused a version just one year old were Akamai-protected. Cloudflare-protected sites mostly reacted only to the oldest version.
  • Keep the version current, keep it consistent with the rest of the request, and check it on a schedule, as you would any dependency.

How we measured

We took 400 sites at random from the homepages in our earlier anti-bot stack lookup of the top 1,000 domains in the Tranco ranking. On 5 October 2026 we requested each homepage six times, a second apart and in a random order per site, with a standard Windows Chrome User-Agent string claiming these versions:

Claimed versionStable releaseAge at test
Chrome 154, requested twice22 September 2026Current
Chrome 14130 September 2025About 1 year
Chrome 12917 September 2024About 2 years
Chrome 1107 February 2023About 3.5 years
Chrome 804 February 2020About 6.5 years

The repeated request for the current version is the control: if a site varies on its own, it shows up as a difference between those two. All requests came from the same Python HTTP client and the same connection; only the version number in the User-Agent changed. Three sites failed to respond to at least one request and were excluded, leaving 397.

What we found

Claimed versionServedChallengedBlockedChallenged or blocked
Chrome 154, first request329422617.1%
Chrome 154, repeat329422617.1%
Chrome 141318433619.9%
Chrome 129315433920.7%
Chrome 110314434020.9%
Chrome 80307523822.7%

現在のバージョンに対する2回のリクエストはすべてのサイトで一致しており、そのおかげで残りの表の見方は単純になる。違いはすべてバージョン番号によるものだ。

現在のバージョンを両方とも受け入れたサイトのみを数えると、古いバージョンを拒否したサイトの数はそのバージョンが古くなるほど着実に増えた。

Claimed versionSites that served the current version but refused this one
Chrome 141, 1 year old11
Chrome 129, 2 years old14
Chrome 110, 3.5 years old15
Chrome 80, 6.5 years old22

逆のケースは1件もなかった。古いバージョンが有利に働くことは一度もなかった。

Who reacts, and how

サイトと先の調査で検出した防御システムを突き合わせると、各ベンダーが線引きする場所が異なることがわかる。

  • 1年でAkamaiには十分だった。 Chrome 141を拒否した11サイトのうち9サイトがAkamaiで保護されており、11サイトすべてがHTTP 403を返し、そのうち10サイトはチャレンジではなく単純なブロックだった。
  • Cloudflareは非常に古いバージョンに反応した。 Chrome 80を拒否した22サイトのうち10サイトがCloudflareで保護されておりチャレンジを返し、さらに9サイトがAkamaiだった。
  • 訪問者に伝えるページはほとんどなかった。 Chrome 80に対してのみ「ブラウザを更新してください」といったメッセージを表示し、現在のバージョンには表示しなかったサイトはわずか2サイトだった。ほとんどは単に拒否するだけだった。

これはボット管理の仕組みと整合している。1年前のブラウザを使う実際の訪問者は珍しく、6年前のブラウザとなるとさらに稀であるため、古いバージョンはそれ単独では弱いシグナルだが、リクエストの他の不自然な点と組み合わさるとより強いシグナルになる。

The code

以下の関数は任意のURLに対して同じ比較を実行する。各バージョンを名乗ってランダムな順序でページをリクエストし、それぞれについてステータス、結果、サイズを返し、さらにページが訪問者にブラウザが古いと伝えているかどうかも返す。同じバージョンを2回リストに入れればノイズの対照に使える。

import random
import re
import time

import requests

CHROME = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{v}.0.0.0 Safari/537.36"
OUTDATED = re.compile(r"(browser (is|you are using is) (not supported|out of date|outdated|no longer supported)"
                      r"|update your browser|upgrade your browser|unsupported browser|outdated browser)", re.I)


def classify(response):
    """served, challenged or blocked, from the status and well-known challenge markers."""
    body = response.text[:20000].lower()
    if (response.headers.get("cf-mitigated", "").lower() == "challenge"
            or response.headers.get("x-amzn-waf-action", "").lower() == "challenge"
            or "captcha-delivery.com" in body):
        return "challenged"
    if response.status_code in (401, 403, 405, 429) or response.status_code >= 500:
        return "blocked"
    return "served"


def ua_drift_check(url, versions, pause=1.0, seed=None):
    """Request the same URL claiming each Chrome version, in random order, and compare what comes back.
    A version may be listed twice, as a control for how much the site varies on its own."""
    order = list(enumerate(versions))
    random.Random(seed).shuffle(order)
    results = [None] * len(versions)
    for i, v in order:
        headers = {"User-Agent": CHROME.format(v=v), "Accept": "text/html,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.9"}
        try:
            r = requests.get(url, headers=headers, timeout=20)
            results[i] = {"version": v, "status": r.status_code, "outcome": classify(r), "bytes": len(r.content),
                          "outdated_notice": bool(OUTDATED.search(r.text))}
        except requests.RequestException as e:
            results[i] = {"version": v, "error": type(e).__name__}
        time.sleep(pause)
    return results

User-Agentを変更する前後で、依存しているサイトに対してこれを実行し、結果を記録しておこう。もし対象サイトが送信しているバージョンを拒否し始めたら、そのバージョンが原因かどうかがわかる。

Limits of the measurement

  • ホームページのみ、単一のネットワーク、単一の時点。 より深いページ、他の国、他の時間帯では挙動が異なる可能性がある。
  • ブラウザそのものではない。 当社のクライアントはPythonのHTTPライブラリからChromeのUser-Agentを送信したため、TLSとHTTP/2フィンガープリンティングで説明したように、どのリクエストもすでにChromeと一致しないネットワークフィンガープリントを持っていた。この比較はバージョン番号の影響を切り分けているが、各バージョンの実際のブラウザがどう扱われるかを示すものではない。
  • 基準値はゼロではない。 このクライアントから送った現在のバージョンでさえ、ホームページの17%が拒否した。これは正しいUser-Agentが必要条件ではあっても十分条件ではないことを思い出させる。

ここで自分たち自身についての発見も認めておくべきだろう。今週先に行った当社のアンチボット調査では、Chrome 129のUser-Agentを使っていた。これは古いスクリプトからコピーされた、2年前の文字列だ。今回の測定によれば、これはホームページの数パーセントポイント分のコストになった。

Keeping your User-Agent current

  • 依存関係として扱う。 ブラウザバージョンを1か所にまとめ、スケジュールに従って更新する。Chromeはおよそ4週間ごとに新しいメジャーバージョンをリリースしている。
  • 現在のバージョンから数バージョン以内に留める。 今回のデータでは、1年前のバージョンはすでに現在のバージョンより多く拒否されていた。
  • すべてを一貫させる。 現在のChromeバージョンであっても、古いヘッダー順序、欠落したクライアントヒント、ブラウザらしくないネットワークフィンガープリントがあれば、やはり一貫性がない状態だ。正しいヘッダーとUser-Agentの設定で全体の設定項目を扱っている。
  • 1セッションにつき1つのアイデンティティを使う。 セッション内でバージョンをローテーションすることは、単一のバージョンを使うことよりも不審に見える。
  • CIでテストする。 主要な対象サイトに対して現在の設定と以前の設定で定期的にチェックすれば、データに現れる前に劣化を発見できる。CI/CDでのスクレイパーの実行でその設定方法を紹介している。
  • 拒否率を監視する。 403やチャレンジの緩やかな増加は劣化の典型的な兆候であり、ターゲットヘルススコアに組み込むべき項目だ。

The bottom line

ブラウザバージョンは一度決めれば終わりという設定項目ではない。397の主要サイトを対象とした今回の測定では、申告するChromeバージョンが1年古くなるごとに拒否が増え、この効果が逆方向に働くことは一度もなく、一部の防御サービスはわずか1年古いバージョンにも反応した。

バージョンを最新の状態に保ち、リクエストの残りの部分もそれと一貫させ、自分にとって重要な対象サイトでその効果を測定しよう。それはスクレイピングプロジェクトが実施できる中で最も安価な信頼性改善の1つだ。

Sources and references

  • Chromium Dash, release schedule、安定版リリース日と現在のバージョンについて。
  • Tranco, list Q2K34。
  • 2026年10月5日にShifterが上記のコードを使って行ったリクエスト。

始める準備はできていますか?

Shifterのレジデンシャルプロキシをお試しください。IP 205M+件、195+カ国、$0.10/GBから。

始める