Authentication, Proxies & Certificates

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the Python Reference bibliography.

A private index wants credentials, a corporate network wants a proxy, and a TLS-inspecting proxy wants its own certificate authority trusted. This page shows how pip 26.2.2 handles each. The examples ran on CPython 3.14.6 on 2026-10-22 against a second birdwatch pypiserver on localhost:8081 that requires HTTP basic authentication for every request, and against PyPI through the TLS-inspecting egress proxy of the machine that ran them. Credentials are read from the environment variables BIRDWATCH_USER and BIRDWATCH_PASSWORD; no real credential appears on this page, and none should appear in yours.

Credentials in the index URL

pip accepts HTTP basic authentication credentials in any index URL, https://${BIRDWATCH_USER}:${BIRDWATCH_PASSWORD}@host/simple (with the values from the environment). Many services use a token as the password, or as the user name with an empty or fixed password; follow the service’s documentation. Characters such as @, : and / must be percent-encoded (a password he//o becomes he%2F%2Fo).

Without credentials and with --no-input, the server’s 401 makes every page unusable and pip reports the package as missing:

python -m pip download --no-deps --no-input --index-url http://localhost:8081/simple birdtally
Looking in indexes: http://localhost:8081/simple
ERROR: Could not find a version that satisfies the requirement birdtally (from versions: none)
ERROR: No matching distribution found for birdtally

With -vv the reason is visible: Could not fetch URL http://localhost:8081/simple/pip/: 401 Client Error: Unauthorized …. Without --no-input, pip prompts for a user name and password on a terminal. With credentials from the environment:

python -m pip download --no-deps --index-url "http://${BIRDWATCH_USER}:${BIRDWATCH_PASSWORD}@localhost:8081/simple" birdtally
Looking in indexes: http://birdwatch:****@localhost:8081/simple
Collecting birdtally
  Downloading birdtally-0.1.0-py3-none-any.whl (3.7 kB)
Saved ./dl/birdtally-0.1.0-py3-none-any.whl
Successfully downloaded birdtally

pip redacts the password in its own output. It does not redact it in your shell history, CI logs that echo commands, a committed requirements.txt or a pip.conf. Keep the URL in an environment variable set by your secret store (PIP_INDEX_URL), or use ${VAR} in a requirements file; never commit it.

.netrc

pip also reads ~/.netrc (%HOME%\_netrc on Windows, or the file named by NETRC) through the requests library. With the URL left without credentials:

machine localhost
login birdwatch
password <the password>
chmod 600 ~/.netrc
python -m pip download --no-deps --index-url http://localhost:8081/simple birdtally
  Downloading birdtally-0.1.0-py3-none-any.whl (3.7 kB)
Saved ./dl/birdtally-0.1.0-py3-none-any.whl
Successfully downloaded birdtally

Keyring

pip can ask the keyring library for credentials, so that they live in the operating system’s credential store or come from a cloud provider’s plugin. --keyring-provider (PIP_KEYRING_PROVIDER, pip 23.1) selects how:

Value Behaviour

auto (default)

Try import, then subprocess, then disabled, and use the first that works; but only when user input is allowed.

disabled

Never use keyring.

import

Import the keyring module from pip’s environment.

subprocess

Run the keyring command found on PATH. The index URL must then contain a user name, which keyring’s command line requires.

The trap is in the first row: auto does not query keyring at all under --no-input, because a backend might prompt. Tools that call pip for you (pipx, Pipenv, IDEs) pass --no-input, and CI runs should too, so credentials that "work in my shell" silently disappear there. Choose import or subprocess explicitly:

pipx install keyring                               # keyring as an application, outside your project
pipx inject keyring artifacts-keyring              # Azure Artifacts backend
pipx inject keyring keyrings.google-artifactregistry-auth   # Google Artifact Registry backend
python -m pip config set --user global.keyring-provider subprocess
python -m pip install birdtally --index-url https://oauth2accesstoken@pypi.internal.example/simple/

(Not run here: it needs a real Azure or Google index.) Since pip 26.2 the subprocess provider can also return the user name, and pip 26.2.1 again allows a keyring installed in a non-activated venv to be used with the import provider while build dependencies are installed. When pip is called from another tool, forcing a provider can make that tool look as if it hangs while a backend waits for input that nobody sees.

Proxies

pip uses an HTTP proxy from, in order of precedence:

  • --proxy scheme://[user:passwd@]proxy.server:port (PIP_PROXY, or proxy in pip.conf);

  • the standard http_proxy, https_proxy and no_proxy environment variables (and their upper-case forms).

--no-proxy-env (pip 26.2) ignores the environment variables and other non-pip proxy configuration while still honouring an explicit --proxy; --proxy "" does the same. PIP_USER_AGENT_USER_DATA adds a JSON string to pip’s User-Agent header, which some proxies and index servers use to route or log requests. A wrong proxy fails with its own error reference:

python -m pip download --no-deps --retries 0 --proxy http://127.0.0.1:9 "tomli-w @ https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl"
Collecting tomli-w @ https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl
error: proxy-connection-failed

× Failed to connect to proxy http://127.0.0.1:9 while fetching https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl
╰─> ('Unable to connect to proxy', NewConnectionError("HTTPSConnection(host='127.0.0.1', port=9): Failed to establish a new connection: [Errno 111] Connection refused"))

hint: This is likely a proxy configuration issue.

Certificates

pip verifies every HTTPS connection. Which certificate authorities it trusts:

  • Since pip 24.2, the system certificate store on Python 3.10 and later, through the truststore library (before that it was opt-in with --use-feature=truststore). Certificates your operating system or your company’s device management trusts, such as the CA of a TLS-inspecting proxy, therefore work without configuration.

  • --use-deprecated=legacy-certs goes back to the bundled certifi Mozilla bundle only. It exists for compatibility and will disappear.

  • --cert <path> (PIP_CERT), or REQUESTS_CA_BUNDLE / CURL_CA_BUNDLE, replaces the store with one PEM bundle. Since 26.2, pip debug reports the configuration level a cert setting comes from.

The machine that ran these examples reaches PyPI through a proxy that re-signs TLS connections with its own CA, which is in the system store but not in certifi. With the variables pointing at a bundle removed, pip’s default works and the legacy mode fails:

python -m pip download --no-cache-dir --no-deps --retries 0 tomli-w                       # system store: works
python -m pip download --no-cache-dir --no-deps --retries 0 --use-deprecated=legacy-certs \
    "tomli-w @ https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl"
Saved ./dl6/tomli_w-1.2.0-py3-none-any.whl
Successfully downloaded tomli-w
…
Could not fetch URL https://pypi.org/simple/pip/: There was a problem confirming the ssl certificate: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1082) - skipping
Collecting tomli-w @ https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl
error: ssl-verification-failed

× Failed to establish a secure connection to files.pythonhosted.org while fetching https://files.pythonhosted.org/packages/c7/18/c86eb8e0202e32dd3df50d43d7ff9854f8e0603945ff398974c1d91ac1ef/tomli_w-1.2.0-py3-none-any.whl
╰─> [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain (_ssl.c:1082)

hint: You may need to use --cert or check your proxy/firewall configuration

For index pages the failure is only a Could not fetch URL … - skipping warning followed by (from versions: none); for a file download it is the ssl-verification-failed diagnostic. The fix is to trust the proxy’s CA (system store, or --cert with a bundle that contains it), not to disable verification.

  • --client-cert <path> presents a client certificate (a PEM file with the private key and the certificate) for indexes that require mutual TLS.

  • --trusted-host <host> marks a host as trusted "even though it does not have valid or any HTTPS". It also lets pip use plain HTTP to that host. It turns off exactly the protection that stops a network attacker from replacing packages; use it only for a host on your own machine or network that you control, and prefer fixing the certificate.

pip and Poetry

pip Poetry

Credentials in the URL, PIP_INDEX_URL

poetry config http-basic.<source> <user> <password>, or POETRY_HTTP_BASIC_<SOURCE>_USERNAME/_PASSWORD

.netrc

Not documented by Poetry; use the settings above

--keyring-provider

Poetry uses keyring itself (keyring.enabled), and stores http-basic passwords in it when available

--cert, system store

certificates.<source>.cert, or REQUESTS_CA_BUNDLE

--client-cert

certificates.<source>.client-cert

--proxy, *_proxy

The standard *_proxy variables