crypto.createHash()
Creates a Hash object that computes a digest of streamed data using the named algorithm.
crypto.createHash(algorithm[, options])- Added in Node.js v0.1.92
Parameters
| Parameter | Type | Description |
|---|---|---|
$algorithm | string | The digest algorithm name, such as 'sha256', 'sha512', 'sha1' or 'md5'. Availability depends on the OpenSSL build; crypto.getHashes() lists what is supported. |
$optionsoptional | Object | stream.Transform options, which control the object's behaviour when it is used as a stream. |
$options.outputLengthoptional | number | For XOF hash functions such as 'shake128' and 'shake256', the desired digest length in bytes. Required for XOF functions that have no default output length. |
Return value
HashA Hash object, which is also a stream.Transform, that accumulates data via update() and produces the digest via digest().
Errors
Throws ERR_CRYPTO_UNKNOWN_DIGEST if the algorithm is not supported by the OpenSSL build in use.
Changelog
- PHP v12.8.0The outputLength option was added for XOF hash functions such as 'shake128' and 'shake256'.
- PHP v0.11.4The options argument was added.
crypto.createHash() returns a stateful Hash object. You push bytes into it with update() — as many times as you like — and then collapse everything you pushed into a fixed-size digest with digest(). Hashing is deterministic: the same bytes always produce the same digest, on any machine, in any Node version.
When to reach for it
Reach for createHash() when the data arrives in pieces or is too large to hold in memory. Because the returned object is a stream.Transform, you can pipe a multi-gigabyte file straight through it and never buffer more than one chunk. For a single small value already sitting in a variable, crypto.hash() is the faster one-shot call.
What trips people up
The biggest mistake is using a plain hash for passwords. SHA-256 is designed to be fast, which is exactly wrong for password storage — a GPU tries billions of candidates a second. Use crypto.scryptSync() instead. The second mistake is using a bare hash to verify that a message came from a trusted sender; anyone can recompute a hash, so authenticity needs a key, which is what crypto.createHmac() provides.
'md5' and 'sha1' still work and are still fine as non-security checksums for cache keys or deduplication, but both are broken against deliberate collisions and must not gate a security decision.
Example
When to use it
- Computing a SHA-256 checksum of a downloaded file to verify it was not corrupted
- Deduplicating uploads by fingerprinting content instead of comparing bytes
- Deriving a stable cache key or ETag from a request body
- Hashing a large file by streaming it so the whole thing never enters memory
Frequently asked questions
What is the difference between crypto.createHash and crypto.hash?
crypto.hash() is a one-shot helper that hashes a single value and returns the digest immediately, and it is faster for small inputs that fit in one allocation. crypto.createHash() returns a reusable streaming object, which is what you need when the data arrives in chunks or is too large to hold in memory.
Can I use crypto.createHash to store passwords?
No. Plain hashes such as SHA-256 are engineered for speed, so an attacker with a leaked digest table can test billions of guesses per second on a GPU. Use crypto.scryptSync() with a per-user random salt, or argon2/bcrypt from npm.
Is MD5 still safe to use in Node.js?
Only for non-security purposes such as cache keys, ETags or deduplicating files you already trust. MD5 collisions can be produced on a laptop, so it must never decide whether a file, token or signature is authentic — use SHA-256 for that.
More examples
Checksum a file by streaming it
Piping into the Hash keeps memory flat regardless of file size, because only one chunk is resident at a time. The digest printed here is the same b94d27b9... value as hashing the string directly, confirming the streamed and in-memory paths agree.
import crypto from 'node:crypto';
import fs from 'node:fs';
import { pipeline } from 'node:stream/promises';
fs.writeFileSync('checksum-demo.txt', 'hello world');
// A Hash is a Transform stream, so the file is never fully buffered —
// this works the same for an 8 GB ISO as for these 11 bytes.
const hash = crypto.createHash('sha256');
await pipeline(fs.createReadStream('checksum-demo.txt'), hash);
console.log('sha256:', hash.digest('hex'));
fs.unlinkSync('checksum-demo.txt');Choosing the right tool for the job
All three produce 32 bytes, which is why they are so easily confused. A plain hash answers "are these bytes identical", scrypt answers "does this password match" while resisting brute force, and HMAC answers "was this written by someone with the key".
const crypto = require('node:crypto');
const password = 'hunter2';
// WRONG for passwords — fast by design, so it is fast to crack.
const bad = crypto.createHash('sha256').update(password).digest('hex');
// RIGHT for passwords — deliberately slow and salted.
const salt = crypto.randomBytes(16);
const good = crypto.scryptSync(password, salt, 32);
// RIGHT for "did this come from someone holding the key?"
const signature = crypto.createHmac('sha256', 'shared-secret')
.update('id=42&amount=100')
.digest('hex');
console.log('plain hash length :', bad.length);
console.log('scrypt key bytes :', good.length);
console.log('hmac length :', signature.length);
console.log('all three are 256-bit outputs, but only one is safe for each job');Shorter digests with a SHAKE XOF
outputLength only applies to XOF algorithms like shake128 and shake256; passing it to sha256 has no effect. Note that a shorter SHAKE digest is a prefix of a longer one, which is a property of the XOF construction and lets you pick a length after the fact.
const crypto = require('node:crypto');
// SHAKE is an extendable-output function: you choose the digest length.
const short = crypto.createHash('shake256', { outputLength: 16 })
.update('hello world')
.digest('hex');
const long = crypto.createHash('shake256', { outputLength: 64 })
.update('hello world')
.digest('hex');
console.log('16-byte digest:', short);
console.log('64-byte hex chars:', long.length);
console.log('short is a prefix of long:', long.startsWith(short));
// Truncating SHA-256 yourself is also acceptable for non-security IDs:
const truncated = crypto.createHash('sha256').update('hello world').digest('hex').slice(0, 16);
console.log('truncated sha256:', truncated);
Discussion