Add a sha256 hash to every pinned requirement in requirements.txt, or pass the require-hashes option to pip install. pip then refuses any file whose hash does not match, and it errors if any requirement or dependency is unpinned or unhashed. It protects against tampering after you recorded the hash.
Why pip needs help
The pip documentation is blunt: by default, pip does not perform any checks to protect against remote tampering, and installing involves running arbitrary code from distributions. Hash-checking mode is one of two documented ways to change that. The other is to disallow source distributions with the only-binary option.
What a hashed requirement looks like
FooProject==1.2 \
--hash=sha256:2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 \
--hash=sha256:486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7
Two hashes appear because a package often ships several files, for example wheels for different platforms. You can list multiple hashes per package so the install works across machines.
The rules that trip people up
From the pip documentation:
- Hash-checking is all or nothing. Adding a hash to any requirement turns the mode on globally.
- Hashes are required for all requirements and all dependencies. A transitive dependency that is not spelled out and hashed causes an error.
- Requirements must be pinned, with the double equals form, a URL or a filesystem path.
- Weaker algorithms such as md5, sha1 and sha224 are excluded. sha256 is the recommended choice.
- Newer pip releases added a flag that turns off the automatic enforcement for mixed cases such as local directories. Check your version before relying on it.
How to bootstrap the file
Writing hashes by hand is tedious. The documentation describes using the require-hashes option as a convenient way to bootstrap your list, because pip shows the hashes of the packages it fetched. It fetches only the preferred archive for each package, so you may need extra hashes for alternatives. The pip hash command computes them for a file you already downloaded.
Do the bootstrap on a trusted network, from a known good index, and review the result in a pull request.
What this does and does not prove
Hashes recorded locally detect change. They do not tell you the first download was clean. If you generate the file on the day a malicious release is live, you have faithfully pinned the malicious release. The pip documentation also notes that the hash PyPI publishes next to each file only guards against corruption, because it comes from the same remote source, so it does not count for hash-checking mode.
Combine hashes with a delay before adopting new releases and a scan of what you pinned.
A habit that keeps it maintainable
Hash files rot if nobody owns them. Regenerate the file whenever you deliberately upgrade a package, and review the diff the same way you would review code. Keep the hashed file separate from the loose list of top level packages you actually chose, so people can see intent without wading through hashes. When an install fails with a mismatch, resist the urge to delete the hash line. A mismatch is the feature working: either the file changed or the requirement drifted, and both are worth ten minutes of attention.
If you want a quick look at what your projects actually resolve, Safeguard offers SBOM and dependency scanning, and there is a free scan at gold.safeguard.sh/scan.
Quick check
Do I need hashes for transitive dependencies?
Yes. In hash-checking mode every dependency must be spelled out and hashed, or pip reports an error.
Which hash algorithm should I use?
sha256 is the documented recommendation. md5, sha1 and sha224 are excluded.
Sources
Last reviewed: 19 October 2026.