Header Image How to Scan File Uploads for Malware on Fly.io

How to Scan File Uploads for Malware on Fly.io

If your Fly.io app accepts file uploads, those files aren't scanned for malware. Fly handles global deployment and edge networking — but a user can upload a malicious file and it'll be stored and served like any other attachment.

AttachmentScanner adds malware scanning with a single API call. Files are checked against multiple antivirus engines and you get back a clear result: clean, malicious, or suspicious.

This guide covers adding scanning to a Fly app. For the full picture on scanning strategies, see the complete guide to scanning user uploads.

Set Your Secrets

Sign up for an account to get your API token and scanner URL. Add them as secrets in your Fly app:

fly secrets set ATTACHMENT_SCANNER_URL=https://scans.attachmentscanner.com
fly secrets set ATTACHMENT_SCANNER_API_TOKEN=your_token_here

Fly injects secrets as environment variables at runtime. That's all the configuration you need.

Your First Scan

Test the connection with the EICAR test file — a standardised test file that every antivirus engine detects:

curl -H "Authorization: Bearer $ATTACHMENT_SCANNER_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.attachmentscanner.com/eicar.com"}' \
  -XPOST $ATTACHMENT_SCANNER_URL/v1.0/scans
{
  "status": "found",
  "filename": "eicar.com",
  "matches": ["Eicar-Test-Signature"]
}

That's it — scanning is working. Now let's integrate it into your app.

Scanning from Your App

The examples below use TypeScript, but AttachmentScanner works with any language. See the documentation for examples in Ruby, Python, Go, and more.

Scan by URL

If your files are in cloud storage, pass a URL and AttachmentScanner fetches the file directly:

const response = await fetch(
  `${process.env.ATTACHMENT_SCANNER_URL}/v1.0/scans`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.ATTACHMENT_SCANNER_API_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ url: fileUrl }),
  }
);

const result = await response.json();

if (result.status === "found") {
  // Reject the upload
}

Scan a File Upload Directly

To scan a file from a multipart upload, send it as multipart/form-data:

const formData = new FormData();
formData.append("file", fileBuffer, filename);

const response = await fetch(
  `${process.env.ATTACHMENT_SCANNER_URL}/v1.0/scans`,
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.ATTACHMENT_SCANNER_API_TOKEN}`,
    },
    body: formData,
  }
);

The examples above are synchronous — your server blocks while the scan runs. For production, use async scanning with callbacks. Your upload handler returns immediately, and AttachmentScanner POSTs the result to your callback URL when the scan finishes.

Your App stage upload async + callback AttachmentScanner multi-engine scan callback ok → move to uploads found → quarantine

// Upload handler — returns immediately
await fetch(`${process.env.ATTACHMENT_SCANNER_URL}/v1.0/scans`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ATTACHMENT_SCANNER_API_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    url: presignedUrlFor(stagedPath),
    async: true,
    callback: "https://your-app.fly.dev/webhooks/scan-complete",
  }),
});

return res.status(202).json({ status: "processing" });

The uploads guide covers the full async pattern including callback handling, staging areas, and the warning status.

Multi-Region Scanning

One of Fly's strengths is running your app close to users. If your app runs in multiple regions, your scanning API calls still go to a single AttachmentScanner endpoint — but because we scan via URL (not file upload), latency stays low. The scan engines fetch the file directly from your storage, not through your app.

Works Everywhere

AttachmentScanner is cloud-agnostic — the same API works whether you're on Fly.io, Heroku, Railway, Vercel, or your own infrastructure. If you move between platforms, the integration stays the same.

Getting Started

  1. Sign up and grab your API token
  2. Set ATTACHMENT_SCANNER_URL and ATTACHMENT_SCANNER_API_TOKEN with fly secrets set
  3. Test with EICAR to confirm scanning works
  4. Set up async scanning with callbacks for production

If you need help with your integration, get in touch — we're always happy to help.

2026-04-09
Profile Image: AttachmentScanner Team AttachmentScanner Team

Other Articles