In modern JavaScript, the correct way to compute a SHA256 hash is the built-in Web Crypto API (crypto.subtle.digest), which runs natively in browsers and Node.js without any third-party library. SHA256 in JavaScript is easy to call and easy to misuse, so the harder part is knowing what the hash is actually good for. This guide shows the canonical implementation and then draws the line between the jobs SHA256 does well and the jobs where reaching for it is a security mistake.
The canonical JavaScript SHA256
The Web Crypto API is available as crypto.subtle in browsers and via the node:crypto module's webcrypto export in Node. digest returns a promise that resolves to an ArrayBuffer, which you convert to a hex string:
async function sha256(message) {
const data = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
return [...new Uint8Array(hashBuffer)]
.map(b => b.toString(16).padStart(2, '0'))
.join('');
}
await sha256('hello world');
// "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
In Node.js you can also use the classic synchronous API, which is convenient for server-side scripts:
import { createHash } from 'node:crypto';
const digest = createHash('sha256').update('hello world').digest('hex');
Both produce the same 64-character hex string. Prefer Web Crypto for isomorphic code that runs in both environments; prefer createHash when you only target Node and want a synchronous call.
Why avoid third-party SHA256 libraries
For years the standard advice was to install a package like crypto-js or js-sha256. That advice is now dated. Every current runtime ships Web Crypto, so a userland hashing library adds a dependency, a supply-chain risk, and slower pure-JS math for no benefit. Removing a hashing package is one of the easiest dependency reductions you can make. If you want to understand why cutting unnecessary dependencies matters, our SCA product page explains how transitive packages accumulate risk.
The one caveat is very old environments without Web Crypto. If you must support them, a maintained polyfill is safer than a hand-rolled implementation, but for anything current, the platform API is the right answer.
What SHA256 is good for
SHA256 is a fast cryptographic hash. Its legitimate uses share a common shape: proving that data has not changed.
- Integrity checks. Verify a downloaded file matches a published checksum, or that a message was not tampered with in transit (usually as part of an HMAC).
- Content addressing. Deduplicate or reference data by its hash, the way Git identifies objects.
- Fingerprinting. Detect whether two blobs are identical without comparing them byte by byte.
- HMAC and signatures. SHA256 is the hash inside many message-authentication and digital-signature schemes.
In all of these, the property you rely on is that it is infeasible to find two inputs with the same hash, and that any change to the input changes the output completely.
What SHA256 is NOT for: password storage
This is the mistake that shows up in breach post-mortems again and again. SHA256 is designed to be fast, and fast is exactly wrong for passwords. An attacker who steals a table of SHA256 password hashes can try billions of guesses per second on commodity GPUs. Salting helps against precomputed rainbow tables but does nothing about raw guessing speed.
For passwords, use a slow, memory-hard key-derivation function built for the job:
import { scrypt, randomBytes } from 'node:crypto';
import { promisify } from 'node:util';
const scryptAsync = promisify(scrypt);
async function hashPassword(password) {
const salt = randomBytes(16).toString('hex');
const derived = await scryptAsync(password, salt, 64);
return `${salt}:${derived.toString('hex')}`;
}
scrypt, bcrypt, and argon2 are the right tools here. They are deliberately slow and tunable, which caps how fast an attacker can guess. If you see sha256(password) anywhere in a codebase, treat it as a finding to fix.
Client-side hashing does not replace HTTPS
A recurring anti-pattern is hashing a password in the browser with JavaScript SHA256 and sending the digest instead of the plaintext, on the theory that this protects the credential. It does not. Over an unencrypted channel, the hash simply becomes the password: anyone who intercepts it can replay it. Client-side hashing also cannot substitute for server-side password hashing, because the transmitted value is what the server would need to store safely.
The real protection is TLS for transport plus a proper KDF for storage. Client-side SHA256 has legitimate uses, such as computing a subresource-integrity value or fingerprinting an upload before sending it, but authentication is not one of them.
Verifying integrity in practice
A common and correct use of JavaScript SHA256 is confirming a file matches its published hash. On the server or in a build step:
import { createReadStream } from 'node:fs';
import { createHash } from 'node:crypto';
function fileSha256(path) {
return new Promise((resolve, reject) => {
const hash = createHash('sha256');
createReadStream(path)
.on('data', chunk => hash.update(chunk))
.on('end', () => resolve(hash.digest('hex')))
.on('error', reject);
});
}
Streaming the file avoids loading large artifacts into memory. Compare the result against the expected value with a constant-time comparison when the check is security-sensitive, so you do not leak information through timing.
FAQ
What is the simplest way to compute SHA256 in JavaScript?
Use the built-in Web Crypto API: await crypto.subtle.digest('SHA-256', data), where data is a Uint8Array. It works in browsers and Node.js with no external library. In Node-only code, createHash('sha256') from node:crypto is a synchronous alternative.
Can I use SHA256 to store passwords?
No. SHA256 is too fast, which lets attackers guess billions of hashes per second if the table leaks. Use a slow, memory-hard key-derivation function such as bcrypt, scrypt, or argon2 with a unique salt per password.
Is hashing a password in the browser with JavaScript SHA256 secure?
No. Without HTTPS the transmitted hash just becomes a replayable password, and client-side hashing does not replace proper server-side storage. Rely on TLS for transport and a KDF on the server for storage.
Do I still need a library like crypto-js for SHA256?
Not for any current runtime. Web Crypto is built into all modern browsers and Node.js, so a third-party hashing library adds dependency and supply-chain risk with no benefit. Reserve polyfills for genuinely legacy environments.