0% прочитано

Correlation ID: How I Trace One Request Through a System

One request may pass through middleware, a controller, application logic, and persistence. I show how one correlation ID connects an API response with internal diagnostics.

2 жовтня 2026 р. 7 хв читанняLaravel

When an API request fails, a message such as "something went wrong" provides almost no diagnostic value.

At the same time, the system may be processing hundreds of other requests. Logs may contain many similar failures. The important question becomes: which internal record belongs to the exact response received by this client?

I use a Correlation ID for this purpose: one identifier that travels with a request through the system.

The idea is simple: accept a valid identifier from the client or generate one, store it in the request context, and return it in the response.

A Correlation ID does not explain why a failure happened. It helps identify the exact request whose failure needs to be investigated.

One Request Gets One Identifier

One identifier follows the whole request
HTTP request    |    vaccept or generate ID    |    vmiddleware    |    vcontroller    |    vapplication logic    |    vdiagnostics / logs

I want the correlation identifier to exist as close to the API entry point as possible.

The same value can then be used regardless of where request processing finishes: a successful response, validation failure, or internal error.

In Laravel, middleware is a natural place for this responsibility.

I Can Continue a Valid Client-Supplied ID

In a distributed system, a request may arrive from another service rather than directly from a user.

If an upstream system already created a correlation identifier, keeping that chain can be useful.

However, I do not trust an arbitrary header without validation. The value should match an accepted format and reasonable length limit.

If the value is missing or invalid, the system generates its own identifier.

PHP - Accept a valid ID or generate a new one
private function resolveCorrelationId(?string $candidate): string{    if (        $candidate !== null        && $this->isAcceptableCorrelationId($candidate)    ) {        return $candidate;    }     return (string) Str::uuid();}
A client-supplied Correlation ID is still external input. I validate it like any other value crossing the system boundary.

Middleware Adds the ID to the Request Context

PHP - Simplified Correlation ID middleware
final class CorrelationIdMiddleware{    private const HEADER = 'X-Correlation-ID';     public function handle(        Request $request,        Closure $next,    ): Response {        $correlationId = $this->resolveCorrelationId(            $request->header(self::HEADER),        );         $request->attributes->set(            'correlation_id',            $correlationId,        );

The middleware performs three small operations.

  1. Read X-Correlation-ID or create a new value.
  2. Store it in the current request attributes.
  3. Return the same ID in the response header.

Other parts of the application no longer need to decide where that identifier came from.

I Return the Same ID to the Client

Generic response example
HTTP/1.1 409 ConflictX-Correlation-ID: 8d29b96f-... {    "code": "request_conflict",    "message": "The request cannot be completed.",    "correlationId": "8d29b96f-..."}

This identifier becomes especially useful when the client receives an error.

Instead of reporting "I received a 500 around 14:20", the client can provide the exact request identifier.

The same value can then be used internally to find the technical information associated with that request.

I also prefer returning it in an HTTP header because it remains available independently from the exact response body structure.

I Add the Same ID to Diagnostic Context

PHP - Correlation ID in internal diagnostics
Log::error('Request processing failed', [    'correlation_id' => $request->attributes->get(        'correlation_id',    ),    'exception' => $exception::class,]);

The identifier itself does not replace proper diagnostics.

It simply provides a shared key that can connect records created while processing one request.

Depending on the system, these may include application logs, an error tracker, external-call diagnostics, or other technical events.

The important part is that the relevant records share the same identifier.

Correlation IDs Are Especially Useful with Safe API Errors

PHP - Safe public error response
return response()->json(    [        'code' => 'request_conflict',        'message' => 'The request cannot be completed.',        'correlationId' => $request->attributes->get(            'correlation_id',        ),    ],    409,);

A public error should not need to expose a stack trace, internal SQL, or arbitrary exception text merely to make diagnostics easier.

The client can receive a stable and safe problem description while technical details remain internal.

The correlation identifier connects those two sides.

This is why correlation works well together with a deliberate API error contract.

A Correlation ID Is Not Identity or a Secret

A request correlation identifier has one narrow purpose: tracing the processing of one request.

I do not use it as:

  • authentication;
  • operation authorization;
  • a secret;
  • a user or business-entity identity;
  • a replacement for an idempotency key.

The fact that a value is unique does not make it appropriate for every problem that needs an identifier.

Not Every Class Should Know About the HTTP Header

There is another boundary I try to preserve: internal code does not need to know that the value originally arrived through an X-Correlation-ID HTTP header.

The header belongs to the transport layer.

Internally, it is enough to have an already resolved identifier in the current request context or pass it only where it is actually needed.

This prevents a transport detail from spreading through every service and class.

The Same ID Becomes Even More Useful Across Services

The same ID follows the request across systems
Client   |   | ID: abc-123   vService A   |   | ID: abc-123   vService B   |   | ID: abc-123   vService C

A correlation identifier is already useful inside one Laravel application.

Its value increases further when one incoming request produces calls to other services.

If the next service supports the same convention, the identifier can be propagated downstream.

During an investigation, this turns separate records across different systems into one logical request chain.

What I Check When Adding Correlation IDs

For this mechanism, I check several things:

  • The ID is created at the request boundary, not only after an error happens.
  • Client-supplied values are validated before use.
  • If no acceptable value exists, the system creates its own.
  • The same ID remains available throughout request processing.
  • It is returned to the client.
  • It appears in the required diagnostic context.
  • It is not treated as a secret, authorization proof, or business identity.

A Small Field with Significant Diagnostic Value

A Correlation ID does not solve failures and does not replace logging or observability.

Its responsibility is much smaller: give all records related to one request a shared reference.

The client receives the identifier in the response. The system uses the same value for internal diagnostics. If the request crosses service boundaries, the same relationship can continue downstream.

For me, this is one of those small technical mechanisms that is almost invisible during normal operation but becomes extremely useful when one exact request must be found among thousands of others.

A Correlation ID does not tell me what happened. It tells me exactly which request I need to investigate.