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.
Low because missing correlation IDs extend debugging time significantly but do not expose data or enable attacks directly.
Generate a UUID per incoming request in middleware and propagate it to all log entries for that request.
In middleware.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:
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.
ID: saas-logging.observability.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:
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:
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.