{"id":1071,"date":"2026-10-12T17:37:40","date_gmt":"2026-10-12T14:37:40","guid":{"rendered":"https:\/\/mexela.com\/blog\/python-aiohttp-proxy\/"},"modified":"2026-10-13T20:19:37","modified_gmt":"2026-10-13T17:19:37","slug":"python-aiohttp-proxy","status":"publish","type":"post","link":"https:\/\/mexela.com\/blog\/python-aiohttp-proxy\/","title":{"rendered":"Python aiohttp Proxy Setup: Sessions, Authentication and Timeouts"},"content":{"rendered":"<p class=\"mexela-answer\">For an aiohttp request, pass the HTTP proxy through <code>session.get(..., proxy=proxy_url)<\/code>. Keep a session alive across related requests, set bounded timeouts, and verify the destination observation. aiohttp 3.14.5 was tested here; it ignores proxy environment variables by default.<\/p>\n<p><strong>Reviewed and locally tested: October 12, 2026.<\/strong> Version and route observations below describe the stated test environment. A working local route does not establish a provider&#8217;s public exit, country, anonymity, speed, or uptime.<\/p>\n<h2 id=\"install\">Start with the tested version<\/h2>\n<pre><code class=\"language-bash\">python -m pip install &quot;aiohttp==3.14.5&quot;<\/code><\/pre>\n<p>The local tests ran with Python 3.12.14 on Windows. Check your application&#8217;s interpreter and dependency constraints before adopting the pin. HTTPX and aiohttp expose different proxy APIs: copying a client constructor from another library can create a plausible configuration that never affects the request.<\/p>\n<h2 id=\"request\">Make one request with explicit proxy and TLS settings<\/h2>\n<pre><code class=\"language-python\">import asyncio, os, ssl\nimport aiohttp\n\ndef tls_context(ca_file=None):\n    return ssl.create_default_context(cafile=ca_file)\n\nasync def aiohttp_request(url, proxy_url=None, ca_file=None):\n    timeout=aiohttp.ClientTimeout(total=5, connect=2)\n    async with aiohttp.ClientSession(timeout=timeout, trust_env=False) as session:\n        async with session.get(url, proxy=proxy_url, ssl=tls_context(ca_file),\n                               allow_redirects=False) as response:\n            return response.status, await response.json()\n\nstatus, body = asyncio.run(aiohttp_request(\n    os.environ[&quot;CHECK_URL&quot;], os.environ[&quot;PROXY_URL&quot;]))\nprint({&quot;status&quot;: status})<\/code><\/pre>\n<p>Use a trusted endpoint returning JSON, and supply credentials through an environment value or secret manager. This helper reads the body before leaving the response context. It does not treat a JSON response or successful status as proof of a public proxy exit; compare the destination&#8217;s recorded observation with the direct call using <code>proxy_url=None<\/code>. Redirects are disabled to keep the check on one endpoint.<\/p>\n<p>The session in this small diagnostic closes after the request. In a long-running application, create one session for a suitable lifetime and pass it to request functions. The <a href=\"https:\/\/docs.aiohttp.org\/en\/stable\/client_quickstart.html\">aiohttp client quickstart<\/a> explains response handling and connection reuse.<\/p>\n<h2 id=\"auth\">Choose authentication for the installed release<\/h2>\n<p>The tested example uses a proxy URL containing username\/password credentials. Percent-encode each credential component and keep the resulting URL out of source control, logs and screenshots. Proxy authentication is separate from an API token used by the destination.<\/p>\n<pre><code class=\"language-python\">from urllib.parse import quote\n\nproxy_url = &quot;http:\/\/{}:{}@proxy.example:8080&quot;.format(\n    quote(os.environ[&quot;PROXY_USER&quot;], safe=&quot;&quot;),\n    quote(os.environ[&quot;PROXY_PASSWORD&quot;], safe=&quot;&quot;))<\/code><\/pre>\n<p>Current 3.14 documentation deprecates the older <code>proxy_auth<\/code> argument, with removal planned for 4.0, and describes <code>encode_basic_auth<\/code> with <code>proxy_headers<\/code> for explicit headers. The URL form above was the form exercised in our fixture. For a migration, consult the installed version&#8217;s <a href=\"https:\/\/docs.aiohttp.org\/en\/stable\/client_advanced.html#proxy-support\">proxy support reference<\/a> rather than silently carrying older BasicAuth examples forward.<\/p>\n<h2 id=\"timeouts\">Separate total and connection budgets<\/h2>\n<p><code>ClientTimeout(total=5, connect=2)<\/code> sets a total request budget and a connection budget. Pool waiting can affect the connection budget when many requests share a connector. A timeout does not tell you whether authentication was correct or whether the API would have accepted the request. Use <a href=\"https:\/\/docs.aiohttp.org\/en\/stable\/client_reference.html#aiohttp.ClientTimeout\">the ClientTimeout reference<\/a> to choose values for your workload.<\/p>\n<p>Avoid retrying every exception immediately. A failed credential needs a configuration fix; a bounded transient connection failure may justify a limited retry; a destination rate limit needs the destination&#8217;s policy. Record a sanitized failure class and attempt count without printing the proxy URL.<\/p>\n<h2 id=\"environment\">Enable environment inheritance only when you intend it<\/h2>\n<pre><code class=\"language-python\">async with aiohttp.ClientSession(trust_env=True) as session:\n    async with session.get(os.environ[&quot;CHECK_URL&quot;],\n                           timeout=aiohttp.ClientTimeout(total=5)) as response:\n        print({&quot;status&quot;: response.status})<\/code><\/pre>\n<p>This is an alternative configuration path, not an addition to the explicit proxy helper. Our five-case matrix showed default sessions using the direct route, and sessions with <code>trust_env=True<\/code> honoring the proxy and matching NO_PROXY rule. Read <a href=\"\/blog\/http-proxy-https-proxy-no-proxy\/\">the environment-variable matrix<\/a> before applying shell-wide settings.<\/p>\n<h2 id=\"measured\">Measured HTTP and CONNECT behavior<\/h2>\n<table>\n<thead>\n<tr>\n<th>Case<\/th>\n<th>Local observation<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Authenticated HTTP proxy<\/td>\n<td>Owned destination recorded the proxy route<\/td>\n<\/tr>\n<tr>\n<td>HTTPS through HTTP CONNECT<\/td>\n<td>Request passed with the disposable destination CA explicitly trusted<\/td>\n<\/tr>\n<tr>\n<td>Untrusted destination certificate<\/td>\n<td>Request rejected while certificate verification remained enabled<\/td>\n<\/tr>\n<tr>\n<td>Proxy credentials at destination<\/td>\n<td>Proxy-Authorization header absent<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The fixture connected only to owned loopback endpoints. It did not test an HTTPS connection to the proxy itself, TLS-in-TLS, SOCKS support, commercial exits, or throughput. An HTTP proxy tunneling an HTTPS destination is a different case from an <code>https:\/\/<\/code> proxy endpoint; verify your exact transport against the current support reference.<\/p>\n<h2 id=\"diagnose\">Resolve the error at the responsible endpoint<\/h2>\n<p>A 407 points to proxy authentication; a destination 401 points to API authentication; a connection refusal points to reachability; a certificate failure points to trust or hostname validation. Confirm that all requests use the intended session. Compare <a href=\"\/blog\/python-httpx-proxy\/\">HTTPX async setup<\/a> when choosing a client, and use <a href=\"\/blog\/common-proxy-errors-fixes\/\">the error checklist<\/a> for a repeatable incident record.<\/p>\n<h2 id=\"faq\">Frequently asked questions<\/h2>\n<div class=\"mexela-faq\">\n<h3>Can I put proxy credentials in the destination Authorization header?<\/h3>\n<p>No. Destination Authorization authenticates to the API. Keep proxy credentials in the proxy configuration and the API token in the destination request configuration.<\/p>\n<h3>Does a session route every request automatically?<\/h3>\n<p>Verify the configuration path used by each call. This guide supplies proxy= at request level, while environment inheritance is a separate, deliberate choice.<\/p>\n<\/div>\n<h2 id=\"next-step\">Verify one client before increasing workload<\/h2>\n<p>Return to <a href=\"\/blog\/proxy-setup-developer-guides\/\">Proxy Setup and Developer Guides<\/a> for adjacent clients. Use <a href=\"\/blog\/test-if-your-proxy-is-working\/\">the proxy verification checklist<\/a> to record a direct baseline and the destination&#8217;s observation. When that is repeatable, compare <a href=\"\/proxy-locations\/\">Mexela proxy locations<\/a> against the actual destination and location requirements.<\/p>\n<p><!-- mexela-developer-kit-20261012:start --><\/p>\n<h2 id=\"developer-examples-download\">Download the developer examples<\/h2>\n<p><a href=\"\/blog\/wp-content\/uploads\/2026\/10\/mexela-developer-examples-20261012-v1.zip\">Download the developer examples ZIP<\/a> for the locally tested Python clients, environment checks, CI route-check files and native PHP cURL helper. The n8n workflow is a separate illustrative reference: it was not imported or executed in n8n. Read the included README for runtime versions, setup and the limits of each check.<\/p>\n<p>ZIP SHA-256: <code>3a43f7d1e6cbb9b2917dbcf88aaa8fe1a2733c33326187cb89c9a9119aad3e20<\/code>. Keep real proxy credentials and destination secrets in your approved secret store; they are absent from this archive. Start with one approved destination and compare its observation with a direct baseline.<\/p>\n<p><!-- mexela-developer-kit-20261012:end --><\/p>\n<h2 id=\"versioned-source-repository\">Versioned Python aiohttp source files<\/h2>\n<p>Use the <a href=\"\/blog\/proxy-code-examples-repository\/#python\">Python aiohttp folder in the public source repository<\/a> for the example, setup instructions, exact dependency versions and a sanitized test summary. The repository is a dated snapshot of owned examples; its loopback checks do not certify a commercial endpoint or every application configuration.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Use aiohttp request proxies with a reusable session, explicit timeouts and verified TLS. Includes current authentication changes and local route observations.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[189],"tags":[],"_links":{"self":[{"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/posts\/1071"}],"collection":[{"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/comments?post=1071"}],"version-history":[{"count":0,"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/posts\/1071\/revisions"}],"wp:attachment":[{"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/media?parent=1071"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/categories?post=1071"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mexela.com\/blog\/wp-json\/wp\/v2\/tags?post=1071"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}