# Correlation IDs are propagated through requests

- **Pattern:** `ab-002333` (`saas-logging.observability.correlation-ids-in-requests`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002333
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002333)

## Why it matters

NIST AU-3 requires that audit records contain sufficient information to establish the source and outcome of logged events — and when a single user request spans multiple log entries across middleware, API handlers, and database calls, a correlation ID is the thread that ties them together. Without it, debugging a reported user error requires manual timestamp correlation across log entries that may have subsecond overlaps with other concurrent requests. The practical impact: an investigation that takes 5 minutes with correlation IDs takes 30–60 minutes without them. OWASP A09 identifies insufficient log context as a monitoring failure; a request ID is the minimum contextual field that turns a pile of log lines into a traceable sequence.

## Severity rationale

Low because missing correlation IDs extend debugging time significantly but do not expose data or enable attacks directly.

## Remediation

Generate a UUID per incoming request in middleware and propagate it to all log entries for that request.

In `middleware.ts`:
```ts
import { NextResponse } from 'next/server'
import { randomUUID } from 'crypto'

export function middleware(req: NextRequest) {
  const requestId = req.headers.get('x-request-id') ?? randomUUID()
  const res = NextResponse.next()
  res.headers.set('x-request-id', requestId)
  return res
}
```

Then in API route handlers, read the header and include it in every log entry:
```ts
const requestId = request.headers.get('x-request-id')
logger.info({ requestId, userId }, 'Processing payment')
logger.error({ requestId, err, userId }, 'Payment failed')
```

If you're using Sentry with OpenTelemetry, trace IDs are automatically injected — check whether your log entries already include a `sentry.trace` or `traceId` field before implementing a manual approach.

## Detection

- **ID:** `correlation-ids-in-requests`
- **Severity:** `low`
- **What to look for:** Enumerate all relevant files and Search for correlation or request ID propagation in middleware and logging. Look for: `x-request-id` or `x-correlation-id` header reading and forwarding in middleware, generation of a UUID per request in middleware and passing it to the logger context, logger configuration that includes a request ID field in every log entry (via async context, Pino's `child()` method, or logger middleware), or any implementation of a trace ID / request ID pattern in the codebase.
- **Pass criteria:** At least 1 conforming pattern must exist. A request-scoped ID (request ID, correlation ID, or trace ID) is generated per incoming request and included in all log entries for that request's lifecycle. This allows filtering all logs for a single request by its ID.
- **Fail criteria:** No correlation ID mechanism found — log entries from the same request cannot be grouped together by a shared ID.
- **Skip (N/A) when:** No server-side logging. Also skip if the APM tool (Sentry, Datadog, etc.) is configured and handles trace ID propagation automatically — in that case this is handled at the platform level.
- **Detail on fail:** `"No correlation ID implementation found — log entries from the same request cannot be correlated; makes debugging multi-step operations difficult"`
- **Remediation:** When a user reports an error, you need to find all logs for their specific request — not just by timestamp range. A correlation ID makes this a one-query operation.

  In Next.js middleware, generate and attach a request ID:
  ```ts
  import { NextResponse } from 'next/server'
  import { randomUUID } from 'crypto'

  export function middleware(req: NextRequest) {
    const requestId = req.headers.get('x-request-id') ?? randomUUID()
    const res = NextResponse.next()
    res.headers.set('x-request-id', requestId)
    return res
  }
  ```

  Then in your API handlers, read this header and include it in log entries:
  ```ts
  const requestId = req.headers.get('x-request-id')
  logger.info({ requestId, userId }, 'Processing payment')
  ```

  If you're using Sentry, `Sentry.captureException` automatically includes a trace ID in structured logs when the Sentry SDK and OpenTelemetry are both configured.

## External references

- nist AU-3
- owasp A09
- iso-25010 maintainability

Taxons: observability

HTML version: https://auditbuffet.com/patterns/ab-002333
