Last updated: February 23, 2025

<span aria-hidden="true" id="hash-files-in-the-browser-with-web-crypto"></span>

# Hash files in the browser with Web Crypto

![Tim Koschützki](/assets/images/teammates/avatar-tim-kos-1.jpg?dpl=dpl_C6YH6XrtnwbHLJcm1LDKywQn4CB3)

#### Tim Koschützki

Co-founder · Berlin, Germany · Show bio

[](https://x.com/tim%5Fkos)[](https://github.com/tim-kos)

File integrity verification is crucial for secure data handling in web applications. The Web Crypto API provides built-in cryptographic operations that let you compute secure hash values directly in the browser. This post explores how to implement client-side file hashing to detect data tampering or corruption during file transfers.

<span aria-hidden="true" id="browser-compatibility-and-requirements"></span>

## Browser compatibility and requirements

The Web Crypto API is widely supported in modern browsers:

* Chrome 41+
* Firefox 34+
* Safari 7+
* Edge 79+

###### Important

The Web Crypto API requires a secure context (HTTPS) to function. When developing locally, `localhost` is considered secure by default.

<span aria-hidden="true" id="supported-hash-algorithms"></span>

## Supported hash algorithms

The Web Crypto API supports several hash algorithms through the `crypto.subtle.digest()` method:

* SHA-256 (recommended for general use)
* SHA-384 (stronger security)
* SHA-512 (strongest security)
* SHA-1 (not recommended due to known vulnerabilities)

<span aria-hidden="true" id="implementing-file-hashing"></span>

## Implementing file hashing

Here's a complete implementation that handles file hashing with proper error handling:

```javascript
async function calculateHash(file, algorithm = 'SHA-256') {
  if (!(file instanceof File)) {
    throw new Error('Input must be a File object')
  }

  try {
    const arrayBuffer = await file.arrayBuffer()
    const hashBuffer = await crypto.subtle.digest(algorithm, arrayBuffer)
    const hashArray = Array.from(new Uint8Array(hashBuffer))
    return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
  } catch (error) {
    if (error instanceof DOMException) {
      throw new Error(`Unsupported hash algorithm: ${algorithm}`)
    }
    throw new Error('Failed to calculate file hash')
  }
}

function setupFileHashing() {
  const fileInput = document.getElementById('fileInput')
  const hashOutput = document.getElementById('hashOutput')
  const algorithmSelect = document.getElementById('algorithmSelect')

  fileInput.addEventListener('change', async (e) => {
    const file = e.target.files[0]
    if (!file) return

    const algorithm = algorithmSelect.value
    hashOutput.textContent = 'Computing hash...'

    try {
      const hash = await calculateHash(file, algorithm)
      hashOutput.textContent = `${algorithm}: ${hash}`
    } catch (error) {
      hashOutput.textContent = `Error: ${error.message}`
      console.error('Hashing error:', error)
    }
  })
}

```

The corresponding HTML structure:

```html
<div class="hash-container">
  <select id="algorithmSelect">
    <option value="SHA-256">SHA-256</option>
    <option value="SHA-384">SHA-384</option>
    <option value="SHA-512">SHA-512</option>
  </select>
  <input type="file" id="fileInput" />
  <div id="hashOutput"></div>
</div>

```

<span aria-hidden="true" id="handling-large-files"></span>

## Handling large files

For large files, processing the entire content at once might cause memory issues. Here's an implementation that processes files in chunks:

```javascript
async function calculateHashInChunks(file, algorithm = 'SHA-256') {
  const chunkSize = 2097152 // 2MB chunks
  const chunks = Math.ceil(file.size / chunkSize)
  let position = 0

  // Create a hash object
  const crypto = window.crypto.subtle
  let hashBuffer = await crypto.digest(algorithm, new ArrayBuffer(0))

  for (let i = 0; i < chunks; i++) {
    const chunk = file.slice(position, position + chunkSize)
    const arrayBuffer = await chunk.arrayBuffer()
    position += chunkSize

    // Update hash with current chunk
    hashBuffer = await crypto.digest(algorithm, arrayBuffer)

    // Update progress if needed
    const progress = Math.round(((i + 1) / chunks) * 100)
    console.log(`Processing: ${progress}%`)
  }

  const hashArray = Array.from(new Uint8Array(hashBuffer))
  return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')
}

```

<span aria-hidden="true" id="verifying-file-integrity"></span>

## Verifying file integrity

To verify file integrity, compare the computed hash with a known good value:

```javascript
function verifyFileIntegrity(computedHash, expectedHash) {
  // Use constant-time comparison to prevent timing attacks
  if (computedHash.length !== expectedHash.length) {
    return false
  }

  return computedHash.toLowerCase() === expectedHash.toLowerCase()
}

// Example usage
const expectedHash = '123abc...'
const isValid = verifyFileIntegrity(computedHash, expectedHash)

```

<span aria-hidden="true" id="best-practices"></span>

## Best practices

1. Always use secure hash algorithms (SHA-256 or stronger)
2. Implement proper error handling
3. Show progress indicators for large files
4. Use constant-time comparisons for hash verification
5. Consider implementing rate limiting for multiple files

Client-side file hashing adds an important security layer to web applications, helping ensure data integrity during file transfers. While this implementation uses the Web Crypto API, production systems often employ additional security measures and server-side verification.

Happy coding!

\#javascript#file-integrity#web-security#browser#hashing

### 👩‍💻 Join 20k+ developers

Sign up for our [monthly newsletter](/newsletters.md) to receive direct links to 3 exclusive tech — and 2 product updates. No less, no more.

Your email:

Get access

## File uploading and encoding. Made simple.

Transloadit streamlines file handling for developers, trusted by brands like Coursera and The New York Times. We’re known for a reliable API, top-notch support, and a strong commitment to open source, with projects like [Uppy⁠](https://uppy.io) and [Tus⁠](https://tus.io) setting standards in file processing.

[Sign up](/c/)[Book a Demo](https://survey.typeform.com/to/kRg47Xi5)

No credit card needed · 5 GB included in the free plan

Cancel anytime
