How to Generate SHA-256 Hashes in the Browser Using the Web Crypto API

You can generate SHA-256 hashes directly in the browser without any external library. The Web Crypto API — built into every modern browser — provides hardware-accelerated cryptographic hashing through crypto.subtle.digest(). It is faster than any JavaScript library, more secure than server-side tools, and your data never leaves your device.
In this tutorial, we walk through exactly how to use the Web Crypto API to generate SHA-256 hashes in JavaScript — with complete code examples, performance comparisons, and practical use cases. Whether you are verifying file integrity, building a client-side security feature, or simply need a quick hash for debugging, this guide covers everything you need.
What Is the Web Crypto API?
The Web Crypto API is a W3C standard built into all modern browsers (Chrome, Firefox, Safari, Edge). It provides a low-level interface for performing cryptographic operations — including hashing, encryption, decryption, signing, and key generation — without requiring any third-party library.
The key interface is crypto.subtle, which exposes methods like digest(), encrypt(), sign(), and generateKey(). For hashing, digest() is all you need.
Important requirement: The Web Crypto API only works in secure contexts — meaning HTTPS pages or localhost. If you are developing locally, localhost qualifies as a secure context, so you are good to go.
Step-by-Step: Generate a SHA-256 Hash in JavaScript
Here is the complete, production-ready function to generate a SHA-256 hash from any string input:
JavaScript
async function sha256(message) {
// Step 1: Encode the string into a Uint8Array
const encoder = new TextEncoder();
const data = encoder.encode(message);
// Step 2: Hash the data using SHA-256
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
// Step 3: Convert the ArrayBuffer to a hex string
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
return hashHex;
}
// Usage
sha256('Hello, World!').then(hash => {
console.log(hash);
// Output: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
});
Let us break down each step:
Step 1: Encode the String
The Web Crypto API does not accept plain strings. You must convert your input into a Uint8Array (an array of bytes) using TextEncoder. This encoder uses UTF-8 by default, which handles international characters correctly.
JavaScript
const encoder = new TextEncoder();
const data = encoder.encode('Hello, World!');
// data is now a Uint8Array: [72, 101, 108, 108, 111, ...]
Step 2: Hash with crypto.subtle.digest()
The digest() method accepts two arguments: the algorithm name (as a string) and the data (as an ArrayBuffer or TypedArray). It returns a Promise that resolves to an ArrayBuffer containing the raw hash bytes.
Supported algorithms:
- SHA-1 — 160-bit hash (deprecated for security use, still used for checksums)
- SHA-256 — 256-bit hash (the industry standard for most applications)
- SHA-384 — 384-bit hash
- SHA-512 — 512-bit hash (maximum security for sensitive operations)
Note: The Web Crypto API intentionally does not support MD5. MD5 is cryptographically broken and vulnerable to collision attacks. The W3C specification excludes it by design to prevent developers from using it in security-sensitive contexts. If you absolutely need MD5 for legacy compatibility (like matching an old checksum), you will need a standalone JavaScript library — but for any new work, SHA-256 is the correct choice.
Step 3: Convert to a Hex String
The digest() method returns raw bytes in an ArrayBuffer. To display the hash in the standard hexadecimal format (a 64-character string for SHA-256), convert each byte to its two-digit hex representation:
JavaScript
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
A Flexible Multi-Algorithm Hash Generator
Here is an extended version that supports multiple algorithms, giving you a reusable utility function:
JavaScript
async function generateHash(message, algorithm = 'SHA-256') {
const encoder = new TextEncoder();
const data = encoder.encode(message);
const hashBuffer = await crypto.subtle.digest(algorithm, data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// Generate multiple hashes for comparison
async function generateAllHashes(message) {
const algorithms = ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512'];
const results = {};
for (const algo of algorithms) {
results[algo] = await generateHash(message, algo);
}
return results;
}
// Usage
generateAllHashes('Hello, World!').then(console.table);
This will output a clean table with SHA-1, SHA-256, SHA-384, and SHA-512 hashes side-by-side — useful for documentation, verification workflows, or building your own hash generator tool.

Hashing Files in the Browser
The Web Crypto API can hash more than just strings. You can hash entire files directly in the browser — useful for verifying download integrity or building a file checksum tool:
JavaScript
async function hashFile(file) {
const arrayBuffer = await file.arrayBuffer();
const hashBuffer = await crypto.subtle.digest('SHA-256', arrayBuffer);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}
// Usage with a file input
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (file) {
const hash = await hashFile(file);
console.log(`SHA-256: ${hash}`);
}
});
The entire file is read into memory using file.arrayBuffer() and hashed locally. The file never leaves your browser. This is the same approach that DevutiliX uses to provide file hashing without any server communication.
Performance: Web Crypto API vs. JavaScript Libraries
The performance difference between the native Web Crypto API and pure-JavaScript hashing libraries is significant:
| Implementation | Speed | Best For |
| Web Crypto API (native) | Fastest — hardware-accelerated | Production use, large files, performance-critical tasks |
| WASM libraries (e.g., libsodium.js) | Near-native | Algorithms not in Web Crypto (e.g., Argon2, Blake3) |
| Pure JS libraries (e.g., crypto-js) | Slowest — interpreted | Legacy environments without Web Crypto support |
The Web Crypto API runs as compiled, native code inside the browser engine. On most modern hardware, it can leverage AES-NI and other CPU instruction sets for hardware-accelerated hashing. A pure JavaScript library like crypto-js runs as interpreted bytecode — it simply cannot compete on raw throughput.
For SHA-256 and SHA-512, there is no reason to use an external library in 2026. The Web Crypto API has universal browser support and is the correct tool for the job. Only reach for libraries like @noble/hashes or WASM-based alternatives when you need algorithms that Web Crypto does not support (like Blake3, Argon2, or Scrypt).
Practical Use Cases for Browser-Native Hashing
- File integrity verification: Hash a downloaded file and compare it against the published checksum — without uploading the file to any verification service.
- Client-side password hashing: While bcrypt/argon2 is preferred for password storage, SHA-256 can be used as a pre-hash before sending to a server, adding a layer of client-side privacy.
- Content fingerprinting: Generate unique identifiers for text blocks, images, or documents to detect duplicates or changes.
- Data deduplication: Hash file contents to identify duplicates before uploading to cloud storage.
- Building privacy-first developer tools: This is exactly how we built the hash generator in DevutiliX — using the Web Crypto API to ensure zero server-side data processing.
Browser Support in 2026
The Web Crypto API has universal support across all modern browsers:
- Chrome 37+ (including Edge, Opera, Brave, and all Chromium-based browsers)
- Firefox 34+
- Safari 7+ (including iOS Safari)
- Node.js 15+ (via globalThis.crypto.subtle)
In 2026, there is effectively no reason to polyfill or avoid the Web Crypto API. If your target audience uses a browser released in the last decade, crypto.subtle.digest() is available.
Key Takeaways
- Use crypto.subtle.digest(‘SHA-256’, data) for all browser-side hashing. It is native, hardware-accelerated, and asynchronous.
- Always encode strings with TextEncoder before hashing — the API works with ArrayBuffer, not strings.
- MD5 is intentionally excluded from Web Crypto. Use SHA-256 for all new work.
- File hashing works seamlessly via file.arrayBuffer() — no server upload required.
- Only use external libraries for algorithms not supported by Web Crypto (Argon2, Blake3, Scrypt).
If you want to skip the implementation and just hash something right now, DevutiliX provides an instant, client-side hash generator that uses this exact Web Crypto API approach — SHA-1, SHA-256, SHA-384, and SHA-512, all in your browser, with zero data transmission.
Want to know about Best Free Online Developer Tools — No Signup, 100% Client-Side
Explore some more privacy-first tools
Enjoyed this technical dispatch?
Share it with fellow engineers or connect with us for questions.
Nirdhum
Veridicus Lab is an independent software lab creating privacy-first digital tools, developer utilities, and offline-first mobile apps.
