Python requestsのプロキシ: 認証、タイムアウト、エラー

14分

PythonのRequestsをHTTPまたはSOCKS5プロキシ経由で出す方法です。proxiesのdict、ユーザー名とパスワード、環境変数、タイムアウト、そしてProxyError、407、SSLErrorの実際の意味を扱います。

PythonのRequestsをプロキシ経由で出し、プロキシが荒れたときも動き続ける必要がある開発者向けの案内です。例はプレースホルダのホストとダミーの資格情報です。Top ProxyはIPを売りません。無料の一覧も渡しません。売っている供給元を比べます。

スニペットはPyPIのRequests 2.34.2向けです。Python 3.10以上が必要です。以下のプロキシの動きは、Requestsのadvanced usageに従います。主要なケースは2026-10-14に、固定したループバックのフィクスチャ(Python 3.14.6、Requests 2.34.2、urllib3 2.8.0、PySocks 1.7.1、TLS検証オン)で再実行しました。この実験が示すのはプロトコルの動きだけです。供給元の速度、拠点、IPの品質ではありません。

01最小のPython requestsプロキシ例

どのリクエストメソッドも proxies dictを受けます。キーは対象URLのスキーム、値はプロキシURLです。

import requests

proxies = {
    "http": "http://203.0.113.10:8080",
    "https": "http://203.0.113.10:8080",
}

r = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=(3.05, 27))
print(r.status_code, r.json())

つまずきは二つです。第一に、"https" キーはたいてい http:// のプロキシURLを指します。それで正しいです。クライアントはプロキシへ CONNECT トンネルを頼み、TLSはその中で走ります。プロキシのポートで供給元がTLSを終端するときだけ、そこに https:// を使ってください。第二に、プロキシURLには明示的なスキーム(http://、https://、socks5://、socks5h://)を書いて、プロトコルを曖昧にしないでください。Requests 2.34.2は、スキーム無しの普通の host:port の前に http:// を付けます。それは無効なプロキシURLではありません。InvalidProxyURL が上がるのは、解析のあともホストが無いときです。スキームを省略したからではありません。

httpbin.org/ip は origin 欄のあるJSONを返します。サーバーが見たアドレスです。契約で供給元が書いた出口IPまたはレンジと比べ、直接のアドレスは自分のIPで控えてください。アドレスが変わらないことだけでは、プロキシを迂回した証明にはなりません。座っているのと同じ網から出る構成もあります。端点が応答するかどうかだけ知りたいなら、プロキシチェッカーか、長いプロキシが動くかの確認を使ってください。

scheme://hostname のキーは、そのホストだけ別経路にします。

proxies = {
    "https": "http://203.0.113.10:8080",
    "https://api.internal.example": "http://198.51.100.5:3128",
}

ホストの一致は完全一致です。同じドメインのすべての名前には効きません。

02Sessionのプロキシ設定(と環境の罠)

Session は接続を再利用し、既定を一箇所に置きます。

session = requests.Session()
session.proxies.update(proxies)
session.get("https://httpbin.org/ip", timeout=10)

Requestsの文書は、session.proxies が期待どおりに動かないことがあると警告しています。そこに置いた値は、環境のプロキシに上書きされることがあります(urllib.request.getproxies() が返すもの)。引き継いだ HTTPS_PROXY が、黙って勝ちます。選択を明示する方法は二つです。

# Option 1: pass proxies on every call (the documented fix)
session.get(url, proxies=proxies, timeout=10)

# Option 2: stop the session from reading the environment
session.trust_env = False
session.proxies.update(proxies)

trust_env = False は、セッションが .netrc や REQUESTS_CA_BUNDLE のようなCAバンドル変数を読むのも止めます。

紙の警告ではありません。2026-10-14のフィクスチャでは、Session.proxies が一つのループバックプロキシを指し、HTTP_PROXY / HTTPS_PROXY が別のプロキシを指していました。GETを受けたのは環境のプロキシです。同じセッションで呼び出しに proxies= を渡すと、セッションのプロキシに当たりました。trust_env = False の二番目のセッションは Session.proxies を使い、環境を無視しました。スクリプトが「ときどき」CIのプロキシを使うなら、URLを書き換える前にここを見てください。

03Python requestsのプロキシ環境変数

proxies をまったく渡さないとき、Requestsは標準の http_proxy、https_proxy、no_proxy、all_proxy を読みます。普通のプロセスでは、大文字の名前も動きます。curlのHTTP規則ではありません。curlではHTTP向けの変数は小文字の http_proxy です。CPythonの例外が一つあります。REQUEST_METHOD が付いているとき(CGIプロセス)、urllib.request.getproxies_environment() は HTTP_PROXY 由来のHTTP対応を落とします。クライアントの Proxy: ヘッダーで注入できないようにするためです。小文字の http_proxy はまだ効きます。

export HTTP_PROXY="http://203.0.113.10:8080"
export HTTPS_PROXY="http://203.0.113.10:8080"
export NO_PROXY="localhost,127.0.0.1,.internal.example"
python my_script.py

NO_PROXY が無いと、localhost や内部サービスへの呼び出しが環境プロキシを通り、タイムアウトすることがあります。NO_PROXY が支配するのは、環境から拾ったプロキシだけです。明示的な proxies= dictがそのホストを覆うなら、NO_PROXY に一致してもプロキシ経由になります。

フィクスチャは両方を確認しました。NO_PROXY=127.0.0.1,localhost で proxies= 引数なしのとき、GETはローカルオリジンへ直行し、実験のプロキシはどちらも見ませんでした。同じホストに明示的な proxies= dictを付けると、渡したプロキシを通りました。シェルでは動いてワーカーでは動かないバグが、別ライブラリではなく別の設定経路であることが多い理由です。

設定の出所適用範囲使うとき
リクエストの proxies= 引数その一回。環境プロキシを上書きする特定の出口が必要なスクリプト。例: リクエストごとのローテーション
Session.proxiesそのセッションのすべての呼び出し。環境が上書きしない限り長く動くクライアント。理想は trust_env = False
HTTP_PROXY / HTTPS_PROXY / ALL_PROXY環境をまだ信頼するプロセス内の、すべてのRequests呼び出しコードを直せない第三者ツールとCI
NO_PROXY環境プロキシを飛ばすホスト(明示的な proxies= はまだ覆える)localhost、メタデータ端点、内部API

04Python requestsのプロキシ認証(ユーザー名とパスワード)

有料プロキシの多くはログインが要ります。Requestsは、URLそのものを通してプロキシのHTTP Basic認証に対応します。http://user:***@host:port を proxies dict、またはどの環境変数へも置けます。

import os
from urllib.parse import quote

user = quote(os.environ["PROXY_USER"], safe="")
password = quote(os.environ["PROXY_PASS"], safe="")
host = "gate.example-provider.com:7000"

proxy_url = f"http://{user}:{password}@{host}"
proxies = {"http": proxy_url, "https": proxy_url}

資格情報は実行時にシークレットマネージャから読んでください。Requestsの文書は、環境変数や版管理されたファイルへ置くことをセキュリティリスクと呼んでいます。ユーザー名とパスワードの両方をパーセントエンコードしてください。RequestsはURLから取った両方をアンクォートするので、どちらかに @、:、/、# があるなら quote(..., safe="") を通す必要があります。そうしないとURLの解析がずれ、渡したつもりだった資格情報をプロキシは見ません。デコードのあと、HTTP Basicのuser-idにコロンは入れられません(RFC 7617 第2節)。パスワードには入れられます。ユーザー名のコロンをパーセントエンコードしても合法にはなりません。Requestsがコロンへ戻すからです。userinfoの余分なエンコード無しコロンは、パスワードの開始として解析されます(alice:bob:secret はユーザー alice、パスワード bob:secret になります)。

ユーザー名の国やスティッキーセッションの旗(user-country-us-session-abc123)は供給元の構文であり、Requestsの機能ではありません。それだけで地理や口座は得られません。

古い回答では requests.auth.HTTPProxyAuth も見ます。リクエストオブジェクトへ Proxy-Authorization ヘッダーを足します。ループバック試験では、素のHTTPリクエストは認証できました(200)。HTTPSリクエストは407に触れる ProxyError で失敗しました。ヘッダーが、プロキシが見る CONNECT ではなくリクエストに乗っている動きと合います。資格情報はURLへ入れてください。

Requestsが扱うプロキシ認証はBasicだけです。NTLMやKerberos/Negotiateのプロキシには、第三者の認証ライブラリか、ローカルのヘルパープロキシが要ります。

入力した問いが「407は何か」だけなら、そのステータスには専用ページがあります。407 Proxy Authentication Required。この記事は、Requestsがそれをどう出すかに留まります。

05requestsのSOCKS5プロキシ

SOCKS対応は任意の追加で、PySocksを引きます(執筆時点のPyPIは1.7.1)。

python -m pip install "requests[socks]"

そのあと、同じ proxies dictでSOCKS URLが動きます。資格情報も含みます。

proxies = {
    "http": "socks5h://user:***@203.0.113.10:1080",
    "https": "socks5h://user:***@203.0.113.10:1080",
}

h に注意してください。socks5:// ではDNS解決は自分のマシンで行われ、できたIPだけがプロキシへ行きます。socks5h:// では、プロキシがホスト名を解決します。curlと同じ分かれです。

フィクスチャは https://example.com/ でそれを見せました。どちらのスキームも200を返しました。SOCKSサーバーは、socks5:// ではATYP 1(IPv4、172.66.147.243)、socks5h:// ではATYP 3(ホスト名 example.com)を記録しました。127.0.0.1 へのHTTP呼び出しでは差は見えません。IPはすでにアドレスだからです。ローカルのリゾルバに照会を見せたくないときは socks5h を使ってください。カタログの言葉でのプロトコル比較は、SOCKS5とHTTPプロキシを見てください。

プロキシURLのスキーム必要な追加DNSを解決する側典型的な用途
http://なしプロキシ(要求したホスト名)ほとんどの有料HTTP(S)ゲートウェイ
https://なしプロキシプロキシのポートでTLSを終端する供給元
socks5://requests[socks]自分のマシンローカルDNSでよいSOCKSプロキシ
socks5h://requests[socks]プロキシ照会をプロキシ側に残すSOCKSプロキシ

端点が何を話すか分からないときは、プロキシチェッカーに通してください。

06Python requestsのプロキシタイムアウト

Requestsには既定のタイムアウトがありません。止まったプロキシは、呼び出しを何分も吊るすことがあります。必ず timeout を渡してください。理想は (connect, read) のタプルです。

r = requests.get(url, proxies=proxies, timeout=(3.05, 27))

connectの値はTCP接続を覆います。プロキシがあるときは、プロキシへの接続です。文書は、TCP再送に合わせるため、3の倍数より少し大きい値を勧めています。readの値は、サーバーからのバイト間の最大間隔であり、ダウンロード全体の上限ではありません。遅延は供給元、出口、対象に依存するので、readは実際の端点で測った応答時間から決めてください。

押さえておくべき点です。プロキシ自身がTCP接続を時間内に受けないとき、Requests 2.34.2はトップレベルの ConnectTimeout ではなく、urllib3の ConnectTimeoutError を包んだ ProxyError を上げました。フィクスチャでは、192.0.2.1:8080 に対する timeout=(1.0, 5) が、その包みを出しました。動くプロキシが接続したあと、遅いオリジンに timeout=(3.05, 1.0) を付けると ReadTimeout が上がりました。一般の ConnectionError より先に ProxyError と ReadTimeout を捕捉し、「プロキシが答えなかった」と「プロキシは答えたが、そのあとサイトが黙った」を分けるなら、メッセージまたは __cause__ を見てください。

一時的な失敗には、手でループを書かず、上限付きの再試行アダプタを載せてください。

from requests.adapters import HTTPAdapter
from urllib3.util import Retry

retry = Retry(total=3, backoff_factor=0.5, status_forcelist=[429, 502, 503, 504])
session.mount("https://", HTTPAdapter(max_retries=retry))
session.mount("http://", HTTPAdapter(max_retries=retry))

ステータスの再試行は allowed_methods に従います。urllib3 2.8.0の既定集合はGET、HEAD、PUT、DELETE、OPTIONS、TRACEです。POSTは足さない限り除外されます。二度送っても安全な操作にだけ、再送を有効にしてください。total=3 が上限にするのは再試行回数であり、壁時計の時間ではありません。応答に Retry-After があり、respect_retry_after_header が真(既定)なら、urllib3はその遅れを待ちます。

再試行では、407も、壊れたCAも、間違ったゲートウェイを指す trust_env も直りません。失敗が本当に一時的なときだけ助けます。

07Python requestsのプロキシエラー: 各例外の意味

except の順に気を付けてください。ProxyError、SSLError、ConnectTimeout はすべて ConnectionError のサブクラスで、ConnectTimeout は Timeout でもあります。具体的なものを先に捕捉します。

from requests.exceptions import (
    ProxyError, SSLError, ConnectTimeout, ReadTimeout,
    ConnectionError, InvalidProxyURL, RequestException,
)

try:
    r = session.get(url, proxies=proxies, timeout=(3.05, 27))
    r.raise_for_status()
except ProxyError as e:
    ...  # proxy unreachable, CONNECT refused, or connect-timeout to the proxy; check the message for 407
except SSLError as e:
    ...  # certificate problem; fix trust, don't disable verification
except ConnectTimeout:
    ...  # could not connect in time (more often seen without a proxy)
except ReadTimeout:
    ...  # connected, but the server went quiet
except InvalidProxyURL:
    ...  # malformed proxy URL
except ConnectionError:
    ...  # DNS failure, reset, other network errors
except RequestException:
    ...  # anything else from requests
症状ありそうな原因試すこと
メッセージに407が無い ProxyErrorそのホスト/ポートで接続を受けるものが無い、プロキシ停止、網/許可リストの遮断、またはプロキシへの接続タイムアウトホストとポートを確認し、端点をプロキシチェッカーで試す。資格情報は最初の容疑ではない
HTTPS URLで407に触れる ProxyErrorプロキシが受けた CONNECT に、受け入れる資格情報が無かった。未送信、HTTPProxyAuth だけ、エンコード誤り、形式誤り、またはプラン無効エンコードした資格情報をプロキシURLへ入れる。ユーザー名の形式とプラン状態を確認する
HTTP URLでステータス407の応答プロキシが資格情報を求めている。資格情報なしで送った要求では、ただのチャレンジプロキシURLへ資格情報を足す。すでに入っているなら、エンコードと形式を見る
ステータス401の応答プロキシではなく宛先が認証を求めているサイト自身の認証を直す。プロキシ側は動いた
Requestsが Session.proxies を無視する環境プロキシが上書きしている呼び出しごとに proxies= を渡すか、trust_env = False にする
InvalidSchema: Missing dependencies for SOCKS supportrequests[socks] 未導入python -m pip install "requests[socks]"
SSLError: CERTIFICATE_VERIFY_FAILED傍受プロキシ、または既定バンドルが信頼しない実験/自己署名証明書verify= または REQUESTS_CA_BUNDLE を、そのプロキシのCAファイルへ向ける
無限に止まるtimeout 未設定必ず (connect, read) タプルを渡す
origin が想定した出口と一致しないそのスキームにプロキシが当たっていない、環境プロキシ頼みのときに NO_PROXY が一致した、または想定と違う経路http と https の両方のキーを置く。環境プロキシなら NO_PROXY を見る。供給元が書いた出口IP/レンジと比べる

ローカル試験が示したこと

制御したローカルプロキシフィクスチャで観測したRequestsの試験結果

これらの型を、2026-10-14にループバックの試験プロキシへ当てました。Python 3.14.6、Requests 2.34.2、urllib3 2.8.0、certifi 2026.07.22、PySocks 1.7.1、特記がなければ timeout=(3.05, 10)、TLS検証オン。ダミーユーザーは demo。パスワードは句読点を含み、URLではパーセントエンコードし、ログでは伏せました。HTTPSの確認は https://example.com/ と、実験CA付きのローカルサーバーです。下の行は、そのフィクスチャから取った観測であり、自動試験の合格数ではありません。プロトコルの動きだけを示します。

ケースHTTP対象HTTPS対象
プロキシ資格情報なし407応答407に触れる ProxyError
プロキシURLのパーセントエンコードした資格情報200200(example.com と、verify= を実験CAにした実験TLS)
HTTPProxyAuth のみ200407に触れる ProxyError
プロキシのポートが閉じているProxyError、407なし未試験
正しいプロキシ認証、サイトが401を返すサイトからの401応答未試験
既定CA対実験の自己署名証明書該当なしSSLError: CERTIFICATE_VERIFY_FAILED。実験CAを信頼したあと200
Session.proxies 対 HTTP_PROXY環境プロキシがGETを受けた未試験
呼び出しの proxies=、環境ありセッション/開いたプロキシがGETを受けた未試験
trust_env = FalseSession.proxies を使用。環境は無視未試験
NO_PROXY がホストに一致オリジンへ直接。プロキシは未通過未試験
NO_PROXY ホストへの明示的な proxies=それでもプロキシ経由未試験
プロキシへの接続タイムアウトConnectTimeoutError を包む ProxyError未試験
動くプロキシ越しの読み取りタイムアウトReadTimeout未試験

エンコードした資格情報付きのSOCKS5は、HTTPと https://example.com/ の両方で200を返しました。DNSの分かれ: socks5:// はIPv4アドレスをプロキシへ渡し、socks5h:// はホスト名を渡しました。

証明書、傍受プロキシ、verify

法人のTLS検査ゲートウェイや、ローカルの傍受プロキシは、HTTPSを自前の認証局で再署名します。Requestsはそれを拒みます。検証を切るのではなく、そのCAを明示的に信頼してください。

r = requests.get(
    url,
    proxies={"https": "http://127.0.0.1:8080"},
    verify="/path/to/proxy-ca.pem",
    timeout=10,
)

またはプロセス全体なら export REQUESTS_CA_BUNDLE=/path/to/ca.pem。DERの書き出しは先にPEMへ変換します。実験の自己署名オリジンも、同じ種類の失敗を再現しました。既定の verify=True は SSLError(certificate verify failed: self-signed certificate)を上げました。同じ要求で verify= を実験CAへ向けると200が返りました。verify=False は、Requestsに任意の証明書を受けさせ、ホスト名の不一致も無視させます。そのクライアントが送るすべてのTLSを無効にします。「動かない」プロキシの普通の直しではありません。

08Pythonでのローテーションプロキシ

商用供給元の多くは、一つのゲートウェイホストの裏で回してくれます。料金によっては、出口はリクエストごと、またはセッション間隔で変わります。ユーザー名のセッションIDは、たいていしばらく一つの出口を固定し、毎回新しい出口を渡すわけではありません。規則は供給元の設定なので、文書を見てください。製品の言葉はスティッキーとローテーションのプロキシにあります。端点の一覧を自分で持つなら、リクエストごとに一つ選び、proxies= で渡してください。

import itertools

pool = itertools.cycle(
    [
        "http://user:***@203.0.113.10:8080",
        "http://user:***@203.0.113.11:8080",
    ]
)

for url in urls:
    p = next(pool)
    session.get(url, proxies={"http": p, "https": p}, timeout=(3.05, 27))

プロキシは呼び出しごとに渡してください。環境プロキシは Session.proxies を上書きできます。出口の種類は別の選択です。データセンター、レジデンシャル、モバイル。価格と、対象サイトがトラフィックをどう扱うかは、料金、対象、IPの評判に依存します。種類のラベルだけでは決まりません。プロキシは、それだけで地理や口座の資格を与えません。

買う前に、選ぶプランがコードに必要なプロトコル(HTTPまたはSOCKS5)と認証方式(ログインまたはIP許可リスト)に対応していることを確認してください。ブラウザ自動化は別のスタックです。PlaywrightのコンテキストはPlaywrightのプロキシを見てください。カタログでプロキシゲートウェイがある供給元です。

Bright Data — レジデンシャルとデータセンタープロキシ
Bright Data — レジデンシャルとデータセンタープロキシ4.41442

Bright Dataはデータ収集向けの大規模プロキシ網です。レジデンシャル、モバイル、ISP、データセンターのIPが約195か国にあります。レジデンシャルトラフィックは定期契約なしの従量払いができ、データセンターとISPアドレスは10 IPからのパックです。レジデンシャルとモバイルの利用には本人確認が必要です。

10か国
$5.00
1GBあたり
Oxylabs — レジデンシャルとデータセンタープロキシ
Oxylabs — レジデンシャルとデータセンタープロキシ4.0771

Oxylabsはレジデンシャル、モバイル、ISP、データセンタープロキシを、月$30からのひとつの月次契約で売っています。支払いはカード、電信送金、AliPay、PayPalです。

10か国
$0.45
1GBあたり
SOAX — レジデンシャルとモバイルプロキシ
SOAX — レジデンシャルとモバイルプロキシ3.8218

SOAXは共有クレジットプール上のローテーションするレジデンシャルとモバイルを売り、データセンターとISPも同じプランに入ります。Builderは月$200 + VAT(200クレジット)から。Builderのティア1トラフィックは1GBあたり$3です。

85か国
$3.00
1GBあたり
Decodo(Smartproxy) — レジデンシャルとデータセンタープロキシ
Decodo(Smartproxy) — レジデンシャルとデータセンタープロキシ4.22118

Decodo(旧Smartproxy)はレジデンシャルとデータセンタープロキシを売っています。レジデンシャルは月次プランなしで1GBあたり$4、または3 GBから1GBあたり$3.75の月次パックです。支払いはカード、PayPal、Alipay、Google/Apple Pay、暗号資産(制限あり)です。

83か国
$0.38
1GBあたり

Pythonのrequestsでプロキシを使うには?

{"http": "http://host:port", "https": "http://host:port"} のようなdictをproxies引数へ渡してください。キーは対象URLのスキーム、値はプロキシURLです。HTTPとSOCKSを混ぜないよう、値には明示的なスキームを付けてください。スキーム無しの普通のhost:portは、Requests 2.34.2では先頭にhttp://が付きます。InvalidProxyURLとしては拒まれません。

requestsのプロキシへユーザー名とパスワードを足すには?

プロキシURL(http://user:***@host:port)へ入れ、両方をurllib.parse.quote(value, safe="")でパーセントエンコードし、ハードコードせずシークレットマネージャから読んでください。デコードのあと、HTTP Basicのuser-idにコロンは入れられません(RFC 7617)。パスワードには入れられます。ユーザー名へコロンを入れるために、パーセントエンコードしたコロンに頼らないでください。

Pythonのrequestsが407 Proxy Authentication Requiredを返すのはなぜ?

プロキシが、受け取らなかった、または受け入れなかった資格情報を求めています。資格情報なしで送った要求では、407はただのチャレンジです。そうでなければエンコード、ユーザー名の形式、プラン状態を見てください。HTTPS対象は407に触れるProxyErrorを上げます。HTTP対象は407応答を返します。閉じたポートなど、407が無いProxyErrorは接続の問題です。HTTPProxyAuthは実験室のHTTPでは認証できましたが、HTTPS CONNECTではできませんでした。

requestsはSOCKS5プロキシに対応しますか?

はい。python -m pip install "requests[socks]" のあとです。ホスト名をプロキシに解決させるならsocks5h://、ローカルで解決するならsocks5://を使ってください。実験では、socks5はexample.comのIPv4アドレスを渡し、socks5hはホスト名を渡しました。

requestsはHTTP_PROXY環境変数を使いますか?

はい。proxiesを渡さないとき、http_proxy、https_proxy、all_proxy、no_proxyを読みます。普通のプロセスでは大文字の名前も動きます。REQUEST_METHODが付いている(CGI)と、urllibはHTTP向けのHTTP_PROXYを無視するので、Proxyヘッダーで注入できません。小文字のhttp_proxyはまだ効きます。curlの小文字専用HTTP規則ではありません。環境プロキシはSession.proxiesを上書きできるので、呼び出しごとにproxiesを渡すか、session.trust_env = Falseにしてください。NO_PROXYが飛ばすのは環境プロキシだけです。明示的なproxies= dictは、それらのホストもまだ覆えます。

プロキシ経由のrequestsには、どのタイムアウトを設定すべきですか?

必ず設定してください。Requestsに既定はありません。(3.05, 27)のようなタプルから始め、実際のプロキシ端点で測った応答時間のあとに合わせてください。プロキシへの接続タイムアウトは、ConnectTimeoutErrorを包むProxyErrorとして出ることがあります。接続後にオリジンが黙るのはReadTimeoutです。

プロキシがHTTPSを壊すならverify=Falseにすべきですか?

いいえ。傍受プロキシのCAをverify=またはREQUESTS_CA_BUNDLEで信頼してください。verify=Falseは任意の証明書を受け、ホスト名の不一致も無視します。実験では、自己署名オリジンは既定の検証に失敗し、実験CAをverify=へ渡したときだけ成功しました。