Safeguard
Supply Chain•

pip extra-index-url versus uv first-index: which is safer?

pip treats all indexes as equal and picks the best version, which pip's docs call unsafe for private packages. uv defaults to the first index that has it.

Safeguard Security Team
3 min read

For private Python packages, pip's extra-index-url is the riskier choice: pip searches all indexes with no priority and takes the best version, so a public package with the same name and a higher version can win. uv's default first-index strategy only considers the first index that contains the package name.

How pip behaves

The pip documentation says index-url sets the base index, default https://pypi.org/simple, and replaces the default. Extra-index-url adds indexes in addition to it. About choosing between candidates, pip says there is no priority in the locations searched, it selects the best match, meaning the newest version that satisfies the constraints, and where more than one source of that version exists, any source is acceptable.

The docs call using extra-index-url to search for packages not in the main repository unsafe. An attacker publishes a package with the same name to a public index and it may be chosen over your private one.

How uv behaves

uv has an index strategy setting, via the index-strategy flag or UV_INDEX_STRATEGY:

  • first-index, the default: candidates are limited to those in the first index that contains the package. A package on your internal index is never taken from PyPI.
  • unsafe-first-match: prefers the first index with a compatible version, even if newer versions exist elsewhere.
  • unsafe-best-match: picks the best version from all candidates, closest to pip, and the docs warn it exposes you to dependency confusion.

Pin a package to an index

uv also lets you tie a package to one index in pyproject.toml:

[tool.uv.sources]
torch = { index = "pytorch" }

Setting explicit = true on an index means packages are installed from it only when pinned to it this way. The named index must be defined in the project's pyproject.toml, not just on the command line or in user config.

Comparison

ApproachBehavior with a name on two indexesRisk
pip with extra-index-urlNewest version wins, any sourceHigher
uv first-indexFirst index containing the nameLower
uv explicit index with sourcesOnly pinned packages use itLowest
One proxy index that merges sourcesDecided by the proxy rulesDepends on the proxy

What to do in a pip shop

  1. Prefer a single index-url that points to your own proxy.
  2. Configure that proxy to block public names that collide with internal ones, or to serve internal names first.
  3. Add hash checking so a swapped package fails. See pip hash checking for AI added dependencies.
  4. Reserve an internal naming prefix.

Background on the attack: what a dependency confusion attack is.

Safeguard dependency and SBOM scanning can list the Python packages in a repository and flag known vulnerabilities. You can try a free scan at gold.safeguard.sh/scan.

Quick check

Does pip prefer the index I list first?

No. The docs say there is no priority among locations. It chooses the best matching version.

What is uv's default strategy?

first-index, which limits candidates to the first index that contains the package.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.