A self-hostable WebUI for checking HaveIBeenPwned, but without your password ever leaving your browser.
- Privacy-First: Passwords are hashed in your browser using SHA1. Only the hash prefix is sent to the server
- No Password Transmission: Your complete password never leaves your device
- Self-Hosted: Full control over your data and deployment
- Simple UI: Clean, intuitive interface with password visibility toggle
- Real-time Checking: Instantly check if a password has been compromised
This application uses the HaveIBeenPwned Range API for secure password checking:
- Browser-Side Hashing: When you enter a password, it's hashed to SHA1 (40 hex characters) entirely in your browser
- Prefix Extraction: The first 5 characters of the hash (prefix) are sent to the server
- Range Query: The server queries the HaveIBeenPwned API with only the 5-character prefix
- Suffix Matching: The HaveIBeenPwned API returns all SHA1 hashes matching that prefix with their breach counts
- Client-Side Verification: Your browser compares the remaining 35 characters (suffix) locally to find exact matches
- Result Display: Only the breach count is shown - your actual password is never transmitted
- k-anonymity: By only sending the first 5 characters, you benefit from k-anonymity - the server cannot know which specific password you're checking among many possibilities
- One-Way Hashing: SHA1 is a one-way function; even if intercepted, hashes cannot be reversed to reveal passwords
- No Server Storage: Your server only makes API calls to HaveIBeenPwned and doesn't store any information about your queries
Pre-built images are available at ghcr.io/tommyschnabel/selfhosted_pwned and support both amd64 and arm64/v8 architectures.
docker run -d -p 8080:8080 --name pwned ghcr.io/tommyschnabel/selfhosted_pwnedThen visit http://localhost:8080 in your browser.
Create a docker-compose.yml file:
version: '3.8'
services:
pwned:
image: ghcr.io/tommyschnabel/selfhosted_pwned:latest
ports:
- "8080:8080"
restart: unless-stoppedThen run:
docker-compose up -d- Docker installed on your system
docker build -t selfhosted-pwned .docker run -d -p 8080:8080 --name pwned selfhosted-pwnedThen visit http://localhost:8080 in your browser.
- Frontend: JavaScript/Node.js with Webpack bundling
- Backend: Go HTTP server
- External API: HaveIBeenPwned Range API (v3)
Check if a SHA1 hash has been compromised.
Request:
{
"hash": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
}Response:
{
"prefix": "a94a8",
"found": true,
"count": 3
}Check if a password has been compromised (hashed server-side).
Request:
{
"password": "test"
}Response:
{
"prefix": "a94a8",
"found": true,
"count": 3
}See LICENSE file for details.

