sha1

Calculates the SHA-1 digest of a string and returns it as 40 hexadecimal characters.

Signature
sha1(string $string, bool $binary = false): string
  • Added in PHP 4.3.0

Parameters

ParameterTypeDescription
$stringstringThe data to hash. Binary safe.
$binaryoptionalbool= falseWhen true, returns the digest as 20 raw bytes instead of 40 hexits.

Return value

stringThe 40-character lowercase hexadecimal digest, or the raw 20-byte binary digest when binary is true.

Errors

Nothing. Every input string produces a digest.

sha1() implements the RFC 3174 Secure Hash Algorithm 1, producing a 160-bit digest. It is the digest format Git uses for object ids and the one many older APIs expect in their signature schemes.

When to reach for it

Reach for it when an external protocol requires SHA-1 specifically: legacy webhook signatures, Git-compatible object naming, WebSocket handshakes, or checksums published by a vendor in SHA-1 form. For your own new code, hash('sha256', ...) is the better default.

What trips people up

SHA-1 has practical collision attacks (SHAttered in 2017, chosen-prefix in 2020). That means it can no longer prove that a file came from who you think it came from. It is still perfectly serviceable as a non-adversarial fingerprint — accidental collisions remain vanishingly unlikely.

The other common surprise is length. Developers size a database column at 32 characters after working with md5(), then silently truncate SHA-1's 40 characters. Use CHAR(40), or BINARY(20) with $binary set to true.

When you compare a digest against a value supplied by a user, use hash_equals() rather than === so the comparison does not leak timing information.

Example

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

When to use it

  • Verify a download against a vendor-published SHA-1 checksum.
  • Compute a Git-style blob id for content addressing.
  • Build the Sec-WebSocket-Accept value during a WebSocket handshake.

Frequently asked questions

Is sha1 still secure in 2026?

Not against a deliberate attacker — chosen-prefix collisions are practical and affordable. It remains fine as a non-security fingerprint, but use sha256 for anything an adversary could target.

What is the difference between sha1 and hash('sha1', ...)?

Nothing in the output; hash() is the generic form that accepts any algorithm name from hash_algos(). Use hash() when the algorithm comes from configuration.

Why does sha1 return 40 characters instead of 32?

SHA-1 produces a 160-bit digest, which is 20 bytes, and each byte renders as two hexits. MD5 is 128 bits, hence 32 characters.

More examples

Git-style blob id

Git prefixes the content with a type header and a NUL byte before hashing. The printed digest is exactly the object id `git hash-object` would report for that file, which shows sha1() is binary safe across the embedded NUL.

Example · php
<?php
$content = "hello world\n";
$blob    = "blob " . strlen($content) . "\0" . $content;

echo sha1($blob), PHP_EOL;
echo strlen($content), " bytes of content", PHP_EOL;
var_dump(sha1($blob) === sha1("blob 12\0hello world\n"));

Verifying a legacy webhook signature

The first comparison succeeds because both sides used the shared secret. The second fails: a plain sha1() of the payload is not an HMAC, which is the most common reason a webhook signature check never matches.

Example · php
<?php
$payload = '{"event":"ping"}';
$secret  = 'whsec_demo';

$expected = 'sha1=' . hash_hmac('sha1', $payload, $secret);
$header   = 'sha1=' . hash_hmac('sha1', $payload, $secret);

var_dump(hash_equals($expected, $header));
var_dump(hash_equals($expected, 'sha1=' . sha1($payload)));
echo $expected, PHP_EOL;

Discussion

  • Be the first to comment on this lesson.