Fastify Upload Security: Scan Files Before They Reach Storage
Use @fastify/multipart and @pompelmi/fastify-plugin to block risky uploads in Fastify before your handler writes to disk or object storage.
Fastify Upload Security: Scan Files Before They Reach Storage
Fastify gives you a clean place to secure uploads: the request lifecycle before your handler touches storage.
That matters because the real risk in file uploads is usually not multipart parsing. It is what happens after parsing, when the route trusts a file too early and writes it somewhere durable.
Use preHandler as the upload gate
For Fastify, the practical default is:
- parse multipart with
@fastify/multipart - scan the file before your main handler runs
- stop the request early on risky uploads
- only store files after a clean verdict
import Fastify from 'fastify';import multipart from '@fastify/multipart';import { createUploadGuard } from '@pompelmi/fastify-plugin';import { CommonHeuristicsScanner, composeScanners, createZipBombGuard,} from 'pompelmi';
const app = Fastify({ logger: true });await app.register(multipart);
const scanner = composeScanners( [ [ 'zipGuard', createZipBombGuard({ maxEntries: 512, maxTotalUncompressedBytes: 100 * 1024 * 1024, maxCompressionRatio: 12, }), ], ['heuristics', CommonHeuristicsScanner], ], { stopOn: 'suspicious', tagSourceName: true });
app.post( '/upload', { preHandler: createUploadGuard({ scanner, includeExtensions: ['pdf', 'png', 'jpg', 'jpeg', 'zip'], allowedMimeTypes: [ 'application/pdf', 'image/png', 'image/jpeg', 'application/zip', ], maxFileSizeBytes: 10 * 1024 * 1024, failClosed: true, }), }, async (request) => { return { ok: true, verdict: (request as any).pompelmi?.verdict ?? 'clean' }; });This is where @pompelmi/fastify-plugin fits well: it plugs into Fastify’s lifecycle and prevents the route from reaching business logic when the upload should never be trusted.
What to harden besides the scanner
Fastify upload security is not only one library call. A strong route usually combines:
- multipart size limits
- a route-specific extension allowlist
- MIME allowlists for obvious mismatches
- content inspection for disguised or risky files
- archive controls for ZIP-heavy routes
That is especially important when a single product accepts images, PDFs, CSV exports, and archives. Those should not all share one generic upload policy.
Why early blocking matters
Early blocking reduces the blast radius:
- no write to disk before the verdict
- no live object in S3 before review
- no downstream image, PDF, or indexing jobs on risky input
It also keeps security behavior easier to reason about. The upload either reaches the handler because it passed policy, or it does not.
Common Fastify mistakes
- Reading multipart and storing immediately, then scanning later
- Letting archives through with only a
.zipallowlist - Treating request MIME as authoritative
- Using one upload route for every file type in the product
- Logging parser failures but not blocked security verdicts
Conclusion
Fastify upload security is strongest when the trust decision happens in preHandler, before your main route writes anything durable. That gives you a small, explicit upload boundary instead of a best-effort cleanup job after storage.
The fastest way to implement that pattern is the canonical Fastify guide with route-specific policies for the file types your application actually accepts.