Last updated: February 5, 2025

<span aria-hidden="true" id="implementing-server-side-malware-scanning-with-clamav-in-nodejs"></span>

# Implementing server-side malware scanning with ClamAV in Node.js

![Kevin van Zonneveld](/assets/images/teammates/avatar-kvz-4.jpg?dpl=dpl_B2d4XACVUtA1h8m6kRxZhWsda77Q)

#### Kevin van Zonneveld

Co-founder · Amsterdam, The Netherlands · Show bio

[](https://x.com/kvz)[](https://github.com/kvz)

In this DevTip, we will explore how to implement server-side malware scanning using ClamAV in a Node.js application. By integrating ClamAV, you can scan uploaded files for malware before processing or storing them, significantly enhancing your web application's security.

<span aria-hidden="true" id="what-is-clamav"></span>

## What is ClamAV?

ClamAV is an open-source antivirus engine designed to detect trojans, viruses, malware, and other malicious threats. As of January 2025, ClamAV's latest stable version is 1.4.2, with version 1.4.x being the current LTS release. It is widely used for mail gateway scanning and can be easily integrated into various applications for file scanning.

<span aria-hidden="true" id="why-implement-server-side-malware-scanning"></span>

## Why implement server-side malware scanning?

Implementing server-side malware scanning is crucial for:

1. Protecting your server from malicious files
2. Preventing the spread of malware to other users
3. Maintaining the integrity of your application
4. Complying with security standards and regulations

By scanning files on the server, you add an essential layer of security to your web application.

<span aria-hidden="true" id="setting-up-clamav-on-your-server"></span>

## Setting up ClamAV on your server

Before integrating ClamAV with Node.js, install it on your server. Use the following commands on Ubuntu:

```bash
sudo apt-get update
sudo apt-get install clamav clamav-daemon
sudo systemctl stop clamav-daemon
sudo freshclam
sudo systemctl start clamav-daemon
sudo systemctl enable clamav-daemon

```

###### Note

After installation, wait a few minutes for the initial virus database download to complete.

On modern Ubuntu systems, the ClamAV socket is typically located at:

* `/var/run/clamav/clamd.ctl` (Ubuntu 22.04+)
* `/var/run/clamd.scan/clamd.sock` (older versions)

Verify your specific socket path with:

```bash
sudo find /var/run -name "clamd.*"

```

<span aria-hidden="true" id="integrating-clamav-with-nodejs"></span>

## Integrating ClamAV with Node.js

To use ClamAV in your Node.js application, install the latest version of the clamscan package (currently 2.4.0):

```bash
npm install clamscan@2.4.0

```

Now, let’s create a robust wrapper for ClamAV scanning:

```javascript
const ClamScan = require('clamscan')

class ClamAVScanner {
  constructor() {
    this.clamscan = null
    this.isInitialized = false
  }

  async initialize() {
    try {
      this.clamscan = await new ClamScan().init({
        removeInfected: true,
        quarantineInfected: false,
        scanLog: null,
        debugMode: false,
        fileList: null,
        scanRecursively: true,
        clamscan: {
          path: '/usr/bin/clamscan',
          db: null,
          scanArchives: true,
          maxFileSize: 26214400, // 25MB
        },
        preference: 'clamdscan',
        clamdscan: {
          socket: '/var/run/clamav/clamd.ctl',
          timeout: 60000,
          localFallback: true,
          path: '/usr/bin/clamdscan',
          configFile: null,
          multiscan: true,
          reloadDb: false,
        },
      })
      this.isInitialized = true
    } catch (err) {
      if (err.message.includes('virus database is empty')) {
        console.error('ClamAV database is not initialized. Please run freshclam')
      } else if (err.code === 'ENOENT') {
        console.error('ClamAV socket not found. Check if clamd is running')
      } else {
        console.error('ClamAV initialization error:', err)
      }
      throw err
    }
  }

  async scanFile(filePath) {
    if (!this.isInitialized) {
      throw new Error('ClamAV scanner not initialized')
    }

    try {
      const { isInfected, viruses } = await this.clamscan.scanFile(filePath)
      return { isInfected, viruses }
    } catch (error) {
      if (error.code === 'ENOENT') {
        throw new Error('File not found or ClamAV socket connection failed')
      }
      console.error('Error scanning file:', error)
      throw error
    }
  }

  async scanBuffer(buffer) {
    if (!this.isInitialized) {
      throw new Error('ClamAV scanner not initialized')
    }

    try {
      const { isInfected, viruses } = await this.clamscan.scanBuffer(buffer)
      return { isInfected, viruses }
    } catch (error) {
      console.error('Error scanning buffer:', error)
      throw error
    }
  }
}

module.exports = ClamAVScanner

```

<span aria-hidden="true" id="scanning-uploaded-files-with-clamav"></span>

## Scanning uploaded files with ClamAV

Integrate the scanner into an Express.js application with proper error handling:

```javascript
const express = require('express')
const multer = require('multer')
const fs = require('fs')
const path = require('path')
const ClamAVScanner = require('./ClamAVScanner')

const app = express()
const upload = multer({
  dest: 'uploads/',
  limits: {
    fileSize: 25 * 1024 * 1024, // 25MB limit
  },
})

const scanner = new ClamAVScanner()
let scannerInitialized = false

// Initialize the ClamAV scanner with retry logic
const initializeScanner = async (retries = 3, delay = 5000) => {
  for (let i = 0; i < retries; i++) {
    try {
      await scanner.initialize()
      scannerInitialized = true
      console.log('ClamAV scanner initialized successfully')
      return
    } catch (err) {
      console.error(`Failed to initialize ClamAV (attempt ${i + 1}/${retries}):`, err)
      if (i < retries - 1) await new Promise((resolve) => setTimeout(resolve, delay))
    }
  }
  process.exit(1)
}

initializeScanner()

app.post('/upload', upload.single('file'), async (req, res) => {
  if (!scannerInitialized) {
    return res.status(503).json({
      error: 'Scanner not initialized',
      message: 'The virus scanner is not ready. Please try again later.',
    })
  }

  if (!req.file) {
    return res.status(400).json({
      error: 'No file uploaded',
      message: 'Please provide a file to scan.',
    })
  }

  try {
    const scanResult = await scanner.scanFile(req.file.path)

    if (scanResult.isInfected) {
      // Delete the infected file
      fs.unlinkSync(req.file.path)

      return res.status(403).json({
        error: 'Malware detected',
        message: 'The uploaded file contains malware.',
        viruses: scanResult.viruses,
      })
    }

    // File is clean, proceed with your application logic
    res.status(200).json({
      message: 'File uploaded and scanned successfully',
      file: {
        name: req.file.originalname,
        size: req.file.size,
        mimetype: req.file.mimetype,
      },
    })
  } catch (error) {
    // Clean up the uploaded file
    if (req.file && fs.existsSync(req.file.path)) {
      fs.unlinkSync(req.file.path)
    }

    console.error('Error during file scan:', error)
    res.status(500).json({
      error: 'Scan failed',
      message: 'An error occurred while scanning the file.',
    })
  }
})

app.listen(3000, () => {
  console.log('Server running on port 3000')
})

```

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

## Best practices and performance tips

1. **Update ClamAV Regularly**: Keep your virus definitions current by using `freshclam`. Add the following entry to your crontab:

```bash
0 */6 * * * /usr/bin/freshclam --quiet  
```

2. **Implement File Size Limits**: ClamAV defaults to scanning files up to 25MB. Adjust this limit as needed:

```javascript
const upload = multer({  
  limits: { fileSize: 25 * 1024 * 1024 }, // 25MB  
})  
```

3. **Use Stream Scanning**: For large files, implement scanning on streams:

```javascript
const scanStream = async (readStream) => {  
  return new Promise((resolve, reject) => {  
    readStream  
      .pipe(scanner.createStream())  
      .on('scan-complete', (result) => resolve(result))  
      .on('error', (error) => reject(error))  
  })  
}  
```

4. **Implement Batch Processing**: When handling multiple files, use batch scanning for efficiency:

```javascript
const scanFiles = async (files) => {  
  return await scanner.clamscan.scanFiles(files)  
}  
```

5. **Consider TCP Socket Scanning**: For high-volume environments, using the TCP socket method can offer improved performance over file-based scanning.
6. **Monitor System Resources**: ClamAV can be resource-intensive. Monitor memory usage and respond accordingly:

```javascript
const os = require('os')  
const freeMem = os.freemem() / (1024 * 1024) // Free memory in MB  
if (freeMem < 100) {  
  console.warn('Low memory warning')  
}  
```

<span aria-hidden="true" id="troubleshooting-common-issues"></span>

## Troubleshooting common issues

1. **Socket Connection Issues**:

```javascript
if (error.code === 'ENOENT') {  
  console.error('Socket not found. Check ClamAV daemon status:')  
  console.error('sudo systemctl status clamav-daemon')  
}  
```

2. **Database Update Issues**:

```javascript
if (error.message.includes('virus database is empty')) {  
  console.error('ClamAV database is empty. Run: sudo freshclam')  
}  
```

3. **Permission Issues**:

```javascript
if (error.code === 'EACCES') {  
  console.error('Permission denied. Check file and socket permissions')  
}  
```

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

## Error handling best practices

When integrating ClamAV into your application, consider these additional error handling strategies:

1. Handle database update errors:

```javascript
if (error && error.message.includes('virus database is empty')) {  
  console.error('ClamAV database is not initialized. Please run freshclam')  
}  
```

2. Handle socket connection errors:

```javascript
if (error && error.code === 'ENOENT') {  
  console.error('ClamAV socket not found. Check if clamd is running')  
}  
```

Implementing these checks ensures that your scanner is properly initialized and that any configuration issues are promptly addressed.

<span aria-hidden="true" id="conclusion"></span>

## Conclusion

Implementing server-side malware scanning with ClamAV in Node.js significantly enhances the security of your web applications. By following these best practices, performance optimizations, and robust error handling measures, you can build a resilient file scanning system that protects your server and users from potential threats.

If you're looking for a more comprehensive solution for handling file uploads securely, consider using [Transloadit](/index.md). Transloadit offers robust file processing capabilities, including virus scanning, that can be easily integrated into your applications.

\#node-js#clamav#security#file-upload#malware-detection

### 👩‍💻 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
