Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to hide sensitive logs in Node.js

Accidentally logging passwords, tokens, or personal data is a serious security risk that can expose sensitive information in log aggregators, monitoring tools, and stdout captures. As the creator of CoreUI with 25 years of backend development experience, I’ve seen production incidents caused by tokens appearing in plain-text logs. The safest approach is to use a structured logger like Pino or Winston with built-in redaction support that strips sensitive fields before they’re ever written. This ensures credentials never appear in logs regardless of which developer added the log statement.

Use Pino’s redact option to automatically remove sensitive fields from all log output.

import pino from 'pino'

const logger = pino({
  redact: {
    paths: [
      'password',
      'token',
      'accessToken',
      'refreshToken',
      'authorization',
      'req.headers.authorization',
      'req.headers.cookie',
      'body.password',
      'body.creditCard',
      '*.secret'
    ],
    censor: '[REDACTED]'
  }
})

// ❌ Would log the actual password without redaction
logger.info({ body: { username: 'alice', password: 's3cr3t' } }, 'Login attempt')

// ✅ Output: {"body":{"username":"alice","password":"[REDACTED]"},"msg":"Login attempt"}

Pino’s redact option uses fast-redact under the hood to replace matching path values with the censor string. The redaction happens during serialization, before the log is written anywhere. Use wildcard paths like '*.secret' to redact fields named secret at any depth.

Redacting Nested and Array Paths

Target deeply nested sensitive values with bracket notation.

const logger = pino({
  redact: {
    paths: [
      'user.password',
      'user.ssn',
      'payment.cardNumber',
      'payment.cvv',
      'users[*].password',
      'items[*].apiKey'
    ],
    censor: '[REMOVED]'
  }
})

logger.info({
  users: [
    { id: 1, name: 'Alice', password: 'secret' },
    { id: 2, name: 'Bob', password: 'pass123' }
  ]
}, 'Fetched users')
// passwords are replaced with [REMOVED] in all array elements

The [*] wildcard matches every element in an array. This is essential when logging collections of user objects where each item may contain sensitive fields.

Environment-Based Log Level Control

Disable verbose logging in production to reduce the attack surface.

import pino from 'pino'

const logger = pino({
  level: process.env.LOG_LEVEL ?? (process.env.NODE_ENV === 'production' ? 'warn' : 'debug'),
  redact: ['password', 'token', 'authorization']
})

// Only logged in development (debug level)
logger.debug({ query: req.query }, 'Incoming request')

// Always logged
logger.warn({ userId }, 'Failed login attempt')
logger.error({ err }, 'Unhandled error')

Setting level: 'warn' in production means debug and info logs are never written, reducing both log volume and the chance of sensitive data appearing in verbose messages.

Custom Serializers for Request Objects

Sanitize Express/Fastify request objects before they’re logged.

const logger = pino({
  serializers: {
    req(req) {
      return {
        method: req.method,
        url: req.url,
        // Omit headers entirely, or whitelist safe ones
        headers: {
          'user-agent': req.headers['user-agent'],
          'content-type': req.headers['content-type']
        }
        // Never log req.body here - it may contain passwords
      }
    },
    res(res) {
      return {
        statusCode: res.statusCode
      }
    }
  }
})

Custom serializers let you control exactly what shape gets logged for complex objects. Whitelisting safe headers rather than blacklisting dangerous ones is safer — new headers added in future won’t accidentally leak.

Best Practice Note

This is the same logging strategy we use in CoreUI backend services — all sensitive fields are redacted at the logger configuration level, not at individual call sites. Never rely on developers remembering to omit sensitive data from log calls. Centralizing redaction rules in the logger configuration means the rules apply automatically to every log statement in the codebase. Review your redaction list whenever you add new data models with personal or financial information.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
Javascript Random - How to Generate a Random Number in JavaScript?
Javascript Random - How to Generate a Random Number in JavaScript?

How to Remove Elements from a JavaScript Array
How to Remove Elements from a JavaScript Array

How to Convert a Map to an Array in JavaScript
How to Convert a Map to an Array in JavaScript

How to Clone an Object in JavaScript
How to Clone an Object in JavaScript

Answers by CoreUI Core Team