Capturing Request Bodies for Forensics in Fastify Without Leaking Secrets

A Fastify preHandler hook captures bodies for forensics. Here's the redaction-aware version that won't put passwords in your logs.

May 9, 2026·4 min read

Capturing Request Bodies for Forensics in Fastify Without Leaking Secrets

Fastify's preHandler hook is the place. Bodies are parsed, route is matched, and you can attach the redacted version to your trace span before the handler runs.

The hook

import { trace } from '@opentelemetry/api';

const SENSITIVE = new Set([
  'password', 'pass', 'pwd', 'token', 'access_token', 'refresh_token',
  'api_key', 'apikey', 'secret', 'authorization', 'cookie', 'session',
  'creditcard', 'card_number', 'cvv', 'cvc', 'ssn',
]);

function redact(obj, depth = 0) {
  if (depth > 10) return '[max-depth]';
  if (obj == null || typeof obj !== 'object') return obj;
  if (Array.isArray(obj)) return obj.map(item => redact(item, depth + 1));

  const result = {};
  for (const [key, value] of Object.entries(obj)) {
    if (SENSITIVE.has(key.toLowerCase())) result[key] = '[REDACTED]';
    else if (typeof value === 'object') result[key] = redact(value, depth + 1);
    else result[key] = value;
  }
  return result;
}

export async function forensicsHook(req, reply) {
  const span = trace.getActiveSpan();
  if (!span) return;

  if (req.body && typeof req.body === 'object') {
    const safe = JSON.stringify(redact(req.body));
    span.setAttribute('http.request.body', safe.slice(0, 8192));
  }

  span.setAttribute('http.request.headers', JSON.stringify(redactHeaders(req.headers)));
}

function redactHeaders(headers) {
  const result = {};
  for (const [key, value] of Object.entries(headers)) {
    const lower = key.toLowerCase();
    result[key] = (lower === 'authorization' || lower === 'cookie' || lower.startsWith('x-api-key'))
      ? '[REDACTED]'
      : String(value);
  }
  return result;
}

Wiring globally

import Fastify from 'fastify';
import { forensicsHook } from './forensics.js';

const app = Fastify({ trustProxy: 1 });
app.addHook('preHandler', forensicsHook);

Multipart bodies

Skip file content; capture only field metadata:

async function safeMultipart(req) {
  if (!req.isMultipart?.()) return null;
  const fields = {};
  for await (const part of req.parts()) {
    if (part.type === 'field') {
      fields[part.fieldname] = SENSITIVE.has(part.fieldname.toLowerCase())
        ? '[REDACTED]'
        : part.value;
    } else {
      fields[part.fieldname] = `[file: ${part.filename}, ${part.mimetype}]`;
    }
  }
  return fields;
}

Response capture

app.addHook('onSend', async (req, reply, payload) => {
  const span = trace.getActiveSpan();
  if (span && payload && reply.getHeader('content-type')?.includes('json')) {
    try {
      const parsed = JSON.parse(payload);
      span.setAttribute('http.response.body', JSON.stringify(redact(parsed)).slice(0, 8192));
    } catch {}
  }
  return payload;
});

The automatic option

node -r securenow/register server.js

Auto-captures bodies, headers, response payloads with redaction across Fastify routes. The hand-rolled version above gives you fine control; SecureNow gives you maintenance-free.

Related

Frequently Asked Questions

Why preHandler instead of onRequest?

preHandler runs after body parsing, so `req.body` is populated. onRequest runs before parsing — no body to capture.

Does this work with all body types (JSON, multipart, etc.)?

JSON yes, multipart needs special handling because the file streams shouldn't be captured (huge). Multipart bodies should capture only the field metadata, not file contents.

Is there an automatic option?

Yes — preload `securenow/register` and the SDK auto-captures bodies for Fastify with redaction across all routes.

Recommended reading

Create Custom Alert Rules From the Command Line

SecureNow 8.1 adds `securenow alerts rules create` — define your own detection rules from SQL, scope them to your apps, and ship them without leaving the terminal. Here's how, with a real magic-link brute-force example.

Jun 11
Secure a Next.js App with SecureNow Using This AI Onboarding Prompt

A copy-paste prompt that lets an AI coding agent install SecureNow, wire Next.js instrumentation, verify traces and logs, deploy to AWS, simulate attacks, and prove firewall blocking with human approval gates.

May 18
nextjs securenow ai onboarding prompt
Adding Backend Tracing to a Sentry Stack with OpenTelemetry

If your team uses Sentry for frontend errors and needs backend distributed tracing without doubling the Sentry bill, here's the OpenTelemetry path that doesn't make you choose.

May 9