Errors in web applications rarely stop at one place. Users may only see a message like "Failed to load data," but behind the scenes, there are requests from the browser, PHP processes, MySQL queries, calls to external APIs, and possibly a job running in the background. Without a way to connect all these processes, application logs can easily turn into a pile of unreadable messages.
This is where request ID and correlation ID come in handy. Both are unique markers attached to a specific process or chain of processes. With these markers, developers can follow the journey of a single request from start to finish, even when that request passes through several different components.
What is the difference between request ID and correlation ID?
Request ID typically identifies a single HTTP request. For example, when a browser sends a request to open a profile page, the server might provide an ID like req-8f31. All logs directly related to that request can include the same ID.
Correlation ID has a broader scope. This ID connects multiple requests that still originate from a single user activity or business flow. For instance, a checkout process may involve requests to the main server, payment service, shipping service, and email system. Each service may have its own request ID, but they all carry the same correlation ID.
In simple applications, a single ID may be sufficient. However, as applications start using many APIs or separate services, this distinction becomes important. Request ID answers the question "which request is this?", while correlation ID helps answer "what activity do all these processes belong to?".
Why are regular logs often not enough?
Imagine five users checking out almost simultaneously. The server logs might record messages like "Payment request failed," "Order created," or "Timeout from shipping API." Without unique markers, developers have to guess which message belongs to which user.
The problem becomes more complicated when several processes run in parallel. The timestamp of log entries does not always reflect the actual business sequence. A message from the payment API might appear after logs from another process, even though that process started earlier.
Request ID and correlation ID do not eliminate errors. Their function is to make those errors visible in context. Developers can search all logs with the same ID, then see the sequence of processes, important parameters, response statuses, and the point at which the flow began to fail.
Starting from the application entry point
The first step is to create or read the ID from the HTTP header when a request comes in. Common headers used are X-Request-ID or Trace-ID. If the client has already sent an ID, the server can use it after validation. If not, the server creates a new ID.
A simple example using PHP:
<?php
$requestId = $_SERVER['HTTP_X_REQUEST_ID'] ?? bin2hex(random_bytes(16));
// Store in application context or request object
$requestContext = [
'request_id' => $requestId
];
header('X-Request-ID: ' . $requestId);
function logMessage(string $message, array $context = []): void
{
global $requestContext;
$entry = array_merge(
$requestContext,
$context,
['message' => $message, 'time' => date(DATE_ATOM)]
);
error_log(json_encode($entry));
}
This example is still simple, but it demonstrates an important principle: the ID is created once at the beginning, returned to the client, and reused in every log within a single request.
In production applications, it is advisable not to accept ID values raw without checks. Limit their length, use safe characters, and avoid storing sensitive data within the ID. IDs should only serve as markers, not as places to embed user information or secret tokens.
Pass IDs when calling other APIs
A common mistake occurs when the request ID is only used on the main server and then lost when the application calls other services. As a result, the PHP logs have one ID, while the payment service logs lack a clear connection.
When making outgoing requests, pass the correlation ID and create a new request ID if necessary. For example:
$headers = [
'X-Correlation-ID: ' . $correlationId,
'X-Request-ID: ' . bin2hex(random_bytes(16)),
'Content-Type: application/json'
];
With this pattern, each service can log its own request identity, but they can still be grouped by correlation ID. If the payment service experiences a timeout, developers can search for that correlation ID in the main application logs, payment service logs, and notification system.
Don't forget the browser side
For applications that heavily use JavaScript, IDs should also be visible from the browser side. When a fetch or XMLHttpRequest fails, the frontend can log the request ID from the response header and display it to the user as a reference code.
const response = await fetch('/api/orders', {
headers: {
'X-Correlation-ID': correlationId
}
});
const requestId = response.headers.get('X-Request-ID');
if (!response.ok) {
showError(`Request failed. Reference code: ${requestId}`);
}
This reference code is more useful than a message saying "an error occurred." Users can send it to the support team, while developers can directly search for the corresponding trace without asking users to explain all the steps they took.
Searchable log format
Avoid logs that are just free-form sentences like "database error." Use a structured format, such as JSON, to make it easier for monitoring systems or log analysis tools to search.
{
"level": "error",
"message": "Payment provider timeout",
"request_id": "req-8f31",
"correlation_id": "checkout-42c1",
"route": "POST /api/orders",
"user_id": 1842,
"duration_ms": 3200,
"provider_status": 504
}
Information such as route, duration, response status, and operation name is usually sufficient to aid diagnosis. However, do not log passwords, API keys, session tokens, credit card numbers, or unnecessary personal data. Overly detailed logs can pose new security risks.
What does this mean for us?
Request ID and correlation ID are not just a necessity for large companies. Small websites that have payments, WhatsApp integrations, email services, or several API endpoints can also benefit from them. The more parts involved in a single process, the more important it is to be able to follow its flow.
It is important to remember that IDs do not replace monitoring, testing, or good error handling. They only provide a common thread. If logs do not have clear timestamps, error levels, and operational information, IDs alone are still not enough.
What you can do now
- Create request IDs in middleware or the main entry point of the application.
- Return request IDs through response headers so they can be read by the browser or support teams.
- Use correlation IDs for business processes that span multiple services.
- Pass IDs when PHP calls APIs, queue workers, or internal services.
- Use structured logs with consistent fields.
- Ensure secrets and sensitive data do not enter the logs.
- Add reference codes that are safe to display to users when errors occur.
Good debugging is not just about finding the wrong line of code. We also need to know the journey of a request: where it came in, who processed it, where it stopped, and why. With request ID and correlation ID, that search shifts from guessing to tracing with a clear path.
– Rio Yotto @rioyotto
