Short answers
How does pip select a proxy?
pip accepts a proxy URL through its proxy option or PIP_PROXY environment setting. Configuration files and other inherited settings can affect a command, so check the Python interpreter and process you actually run.
Does pip index versions install a package?
No. It queries package-index information. A successful query does not verify a package download, build dependency, installation or your application’s proxy settings.
Is a proxy password also a PyPI token?
No. The gateway and package index authenticate different things. This example queries public PyPI without index credentials; it is not a private-index authentication recipe.
Should I add trusted-host for certificate errors?
No. Keep HTTPS certificate verification enabled. Confirm the hostname, certificate chain and approved trust configuration before retrying.
If your task also fetches an HTTPS Git repository, check that operation separately with the Git proxy guide. A working package-index connection does not establish that Git uses the same proxy or authentication.
Select the interpreter and pip together
python3 -I -m pip --version
python3 -I -m pip index --helpUse the interpreter for the environment you intend to check. Python’s -I option keeps the current directory and user site out of its import path; it is different from pip’s --isolated option. Do not add pip --isolated to the sample: that would ignore the PIP_PROXY environment setting it deliberately supplies. If pip is available only in your user site, choose an environment with pip installed instead.
Use a supported Python 3.10 or newer with stable pip 26.2.1 or newer. The sample checks that environment before asking for credentials or querying the index; it does not upgrade it. Our current offline checks use Python 3.12.14 and pip 26.2.1. See pip compatibility and pip index.
Old pip releases can bundle vulnerable HTTP clients. The Requests redirect advisory describes proxy credentials being forwarded to an HTTPS destination on a redirect. Our old pip 21.2.4 fixture reproduced that behaviour; it is deliberately rejected. Upgrading an unrelated Requests installation does not replace the copy bundled inside pip.
Run one temporary index check
Save this as pip-proxy-check.py and run python3 pip-proxy-check.py in a macOS or Linux terminal. Enter the original username and password at the hidden prompts; the code encodes their URL components once. This sample accepts printable ASCII credentials, preserves spaces in the password and rejects a colon in the username. The gateway placeholder is filled from the site’s current connection settings.
import getpass
import os
import subprocess
import sys
import tempfile
import warnings
from urllib.parse import quote, urlsplit
def require_runtime():
# Check the same isolated interpreter before asking for any credential.
probe = (
"import sys, pip; from pip._vendor.packaging.version import Version; "
"v=Version(pip.__version__); "
"sys.exit(0 if sys.version_info >= (3, 10) and "
"v >= Version('26.2.1') and not v.is_prerelease and "
"not v.is_devrelease else 1)"
)
try:
result = subprocess.run(
[sys.executable, "-I", "-c", probe],
env={"PATH": os.defpath}, stdin=subprocess.DEVNULL,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
timeout=5, check=False,
)
except (OSError, subprocess.TimeoutExpired):
raise ValueError("Runtime check failed") from None
if result.returncode != 0:
raise ValueError("Use Python 3.10+ with stable pip 26.2.1+")
def check_config(server, username, password):
if any(ord(char) <= 32 or ord(char) == 127 for char in server):
raise ValueError("Invalid gateway")
proxy = urlsplit(server)
if (proxy.scheme != "http" or not proxy.hostname or
proxy.port is None or not 1 <= proxy.port <= 65535 or
proxy.username is not None or proxy.password is not None or
proxy.path not in ("", "/") or proxy.query or proxy.fragment):
raise ValueError("Use an HTTP gateway with a port and no credentials")
if (not username or not password or ":" in username or
any(ord(char) < 32 or ord(char) > 126
for char in username + password)):
raise ValueError("This example requires printable ASCII credentials")
address = "http://{}:{}@{}".format(
quote(username, safe=""), quote(password, safe=""), proxy.netloc)
# Start fresh: no inherited index tokens, proxy exclusions, pip logs,
# certificate overrides, Python paths, or package-manager settings.
child_env = {
"PATH": os.defpath,
"PIP_CONFIG_FILE": os.devnull,
"NETRC": os.devnull,
"PIP_PROXY": address,
"PIP_KEYRING_PROVIDER": "disabled",
"PYTHON_KEYRING_BACKEND": "keyring.backends.null.Keyring",
}
command = [
sys.executable, "-I", "-m", "pip", "index", "versions", "pip",
"--index-url", "https://pypi.org/simple",
"--disable-pip-version-check", "--no-cache-dir", "--no-input",
"--retries", "0", "--timeout", "10",
]
return command, child_env
def run_check(server, username, password):
require_runtime()
command, child_env = check_config(server, username, password)
with tempfile.TemporaryDirectory(prefix="pip-proxy-check-") as directory:
try:
result = subprocess.run(
command, env=child_env, cwd=directory,
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, timeout=30, check=False,
)
except subprocess.TimeoutExpired:
print("Index check stopped after its process timeout.")
return 1
except OSError:
print("Could not start the selected Python interpreter.")
return 1
if result.returncode != 0:
print("Index check failed. Raw pip output was not displayed.")
return 1
print("Index query completed. No package was installed.")
return 0
def main():
try:
require_runtime()
except ValueError:
print("Use a supported Python 3.10+ environment with stable pip 26.2.1+.")
return 1
if os.name != "posix" or not sys.stdin.isatty():
print("Use a macOS or Linux terminal for hidden credential input.")
return 1
server = os.environ.get("PROXY_SERVER", "http://gw.portproof.org:7000")
with warnings.catch_warnings():
warnings.simplefilter("error", getpass.GetPassWarning)
username = getpass.getpass("Full proxy username: ")
password = getpass.getpass("Proxy password: ")
return run_check(server, username, password)
if __name__ == "__main__":
try:
sys.exit(main())
except (ValueError, EOFError, KeyboardInterrupt, OSError, getpass.GetPassWarning):
print("Check cancelled or setup invalid; credentials were not displayed.")
sys.exit(1)
Only run this if access to public PyPI is permitted in your environment. The starting index is fixed to https://pypi.org/simple, with no extra index or package source inherited. pip can follow index redirects: this is not a network destination allowlist. An organisation requiring strict egress restrictions should enforce them at its approved gateway. A private index needs a separately reviewed authentication and trust setup; do not paste its token into this example.
The pip configuration rules allow PIP_CONFIG_FILE set to the operating system’s null device to disable configuration-file loading. The fresh child environment also drops inherited pip settings, proxy exclusions and certificate overrides. NETRC points to the null device; keyring lookup and interactive pip prompts are disabled. Credentials remain in the child environment while it runs, so use a trusted machine and do not dump its environment.
The pip command options distinguish a socket timeout from a total run limit. Here connection retries are zero and the socket timeout is ten seconds; the Python parent stops and waits for the pip child when its thirty-second process timeout expires. Cache and version checks are disabled. No saved pip configuration is changed. Raw child output is discarded on both success and failure so it cannot copy configuration details into the terminal.
Separate gateway, index and certificate failures
| Observation | Next step |
|---|---|
| Index query completed | The chosen pip process completed its metadata query. Installation and application traffic remain separate checks. |
| Generic failure from the wrapper | Confirm pip is present with the version/help commands, then verify the gateway using the bounded curl guide. The wrapper intentionally does not classify suppressed pip output. |
| A separately observed 407 or CONNECT refusal | Check gateway credentials and the HTTP gateway port. Follow the 407 or CONNECT diagnostic; do not substitute an index token. |
| A separately observed index 401 or 403 | Check the index’s access policy. This public-index sample supplies no index credentials and cannot validate a private index. |
| Certificate verification failure | Check the selected Python/pip trust store and approved certificate chain. Do not add trusted-host or change the index URL to HTTP. |
| Thirty-second process timeout | Stop the diagnostic and check connectivity or policy. A timeout is not evidence that the password alone is wrong. |
See the bounded curl check, 407 authentication guide and CONNECT failures. pip’s certificate documentation explains how trust handling varies by Python and pip version. Keep verification on and have an administrator review any additional CA trust.
Configure the actual workflow separately
After the diagnostic, use your approved secret injection and package-index policy for normal commands. pip authentication describes index authentication; it does not make a gateway password an index token. Avoid credential-bearing command arguments, saved shell history and copied verbose logs. Do not change a team’s private index to public PyPI merely to make a test pass.
Installing a Python library does not configure its network requests. Set up Python Requests, HTTPX or your other client explicitly. For a permitted workflow using shared proxy traffic, review trial evaluation and current proxy options before scaling its traffic.
What is not allowed
Use only approved gateways and package indexes, respect their access rules and follow the acceptable-use policy.
Read next
Python Requests proxy setup
A complete Python check with HTTP and SOCKS5, predictable settings and a practical failure checklist.
Read the guide
Fix 407 Proxy Authentication Required
Read the gateway code, check credentials and balance, then separate a rejected HTTPS tunnel from an HTTP response.
Read the guide