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.

Lhoussine
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

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
How to Block Bot Traffic in Express With No Extra Infra

Five approaches to bot blocking in Express, ranked by effort vs. effectiveness. From a 5-line allowlist to a full IP-reputation firewall — all without Cloudflare, AWS WAF, or any new infrastructure.

May 9
How to Block Bot Traffic in Fastify With No Extra Infra

Fastify hooks (onRequest) and the SecureNow preload both work cleanly. Here's the production setup for IP blocking and user-agent filtering.

May 9