buf.equals()

Compares two buffers byte for byte and returns true when their contents are identical.

Signature
buf.equals(otherBuffer)
  • Added in Node.js v0.11.13

Parameters

ParameterTypeDescription
$otherBufferBuffer | Uint8ArrayA Buffer or Uint8Array with which to compare buf.

Return value

booleanReturns true if both buf and otherBuffer have exactly the same bytes, false otherwise. Equivalent to buf.compare(otherBuffer) === 0.

Errors

Throws ERR_INVALID_ARG_TYPE if otherBuffer is not a Buffer or Uint8Array — strings and numbers are rejected rather than coerced.

Changelog

  • PHP v8.0.0The arguments can now be Uint8Arrays, not only Buffers.

Buffers are objects, so === asks whether two references point at the same object. It is false for two distinct buffers holding identical bytes, and that mistake is the reason this method exists. buf.equals() compares the contents instead: same length, same bytes, in the same order.

What counts as equal

Only the bytes matter. How the buffers were built is irrelevant — Buffer.from('ABC') and Buffer.from('414243', 'hex') are equal because both hold 41 42 43. Length is part of the comparison, so a buffer is never equal to a longer one that merely starts with the same bytes. Since Node.js 8 the argument may be any Uint8Array, which makes it usable against Web Crypto output and other typed-array APIs without a conversion step.

Never use it on secrets

The comparison returns as soon as it finds a differing byte, so how long it takes leaks how many leading bytes matched. For an HMAC signature, an API token, or a password hash, use crypto.timingSafeEqual(), which always inspects every byte. It requires both inputs to be the same length and throws otherwise, so compare lengths separately first.

For an ordering rather than a yes/no answer — sorting keys, building a binary tree — use buf.compare(), which returns -1, 0, or 1.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Assert that a file read back from disk matches the bytes that were written.
  • Detect whether a cached binary blob changed before re-uploading it.
  • Confirm a decoded protocol frame carries the expected fixed header bytes.
  • Compare a computed checksum against a stored one in non-security-sensitive code.

Frequently asked questions

Why does === return false for two buffers with the same content?

Because === compares object references, and two separately created buffers are two different objects no matter what bytes they hold. Buffers have no value semantics in JavaScript, so there is no operator that compares their contents. Use buf.equals(other) for a boolean, or buf.compare(other) === 0 for the identical result expressed as an ordering.

Is buf.equals() safe for comparing passwords or API tokens?

No — buf.equals() stops at the first differing byte, so its execution time reveals how many leading bytes were correct, which is enough to recover a secret through repeated timing measurements. Use crypto.timingSafeEqual(), which reads both inputs in full. It throws when the lengths differ, so compare lengths yourself and return false before calling it.

Can buf.equals() compare a Buffer with a Uint8Array or a string?

A Uint8Array works and has since Node.js 8, because a Buffer is itself a Uint8Array subclass. A string does not: passing one throws ERR_INVALID_ARG_TYPE rather than coercing it. Convert first with Buffer.from(str, encoding), and make sure the encoding matches the one the buffer was built with, or the byte comparison will fail on identical-looking text.

More examples

Verifying a file round-trips unchanged

readFileSync returns a fresh Buffer, so `===` against the original would be false even though nothing changed on disk; equals() gives the answer you actually wanted. The digest comparison shows the same method working on crypto output, and the tampered copy confirms that a single differing byte anywhere flips the result.

Example · javascript
const { Buffer } = require('node:buffer');
const fs = require('node:fs');
const { createHash } = require('node:crypto');

const payload = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
fs.writeFileSync('round-trip.bin', payload);

const readBack = fs.readFileSync('round-trip.bin');
console.log(readBack.equals(payload));
// true

// Digests are Buffers too, so equals() compares them directly.
const digestOf = (b) => createHash('sha256').update(b).digest();
console.log(digestOf(readBack).equals(digestOf(payload)));
// true

// One flipped byte is enough to fail.
const tampered = Buffer.from(payload);
tampered[7] = 0x00;
console.log(tampered.equals(payload));
// false

fs.unlinkSync('round-trip.bin');

Comparing secrets safely

Swapping equals() for timingSafeEqual() here is the whole point: equals() returns early at the first wrong byte, so an attacker who can measure response times can recover a signature one byte at a time. timingSafeEqual() always reads every byte, but it refuses mismatched lengths, which is why the explicit length check comes first and returns false rather than throwing.

Example · javascript
const { Buffer } = require('node:buffer');
const { timingSafeEqual, createHmac } = require('node:crypto');

function verifySignature(body, headerValue, secret) {
  const expected = createHmac('sha256', secret).update(body).digest();
  const received = Buffer.from(headerValue, 'hex');

  // timingSafeEqual throws if the lengths differ, so check that first.
  if (received.length !== expected.length) return false;
  return timingSafeEqual(received, expected);
}

const body = Buffer.from('{"event":"ping"}');
const secret = 'shhh';
const good = createHmac('sha256', secret).update(body).digest('hex');

console.log(verifySignature(body, good, secret));
// true
console.log(verifySignature(body, '00'.repeat(32), secret));
// false

equals() versus compare() when sorting

Buffer.compare is a static function with the exact shape Array.prototype.sort wants, giving byte-lexicographic ordering where a prefix sorts before the longer value. equals() cannot be used for sorting because it collapses the three possible outcomes into a boolean; use it only when the question is genuinely yes-or-no.

Example · javascript
const { Buffer } = require('node:buffer');

const keys = [
  Buffer.from('7f', 'hex'),
  Buffer.from('01', 'hex'),
  Buffer.from('0100', 'hex'),
];

// compare() returns an ordering, so it can drive a sort.
keys.sort(Buffer.compare);
console.log(keys.map((k) => k.toString('hex')));
// [ '01', '0100', '7f' ]

// equals() only answers yes or no.
console.log(keys[0].equals(Buffer.from('01', 'hex')));
// true

// A shorter buffer sorts before a longer one that starts with it.
console.log(Buffer.from('01', 'hex').compare(Buffer.from('0100', 'hex')));
// -1
console.log(Buffer.from('01', 'hex').equals(Buffer.from('0100', 'hex')));
// false

Discussion

  • Be the first to comment on this lesson.