HTTP Request IDs

Last Updated: 22 April 2015

Table of Contents

HTTP request IDs let you correlate router logs for a given web request against the web dyno logs for that same request.

How it works

The Heroku router generates a unique request ID for every incoming HTTP request that it receives. This unique ID is then passed to your application as an HTTP header called X-Request-ID.

Alternately, you can specify the X-Request-ID header when making a request. The value must be between 20 and 200 characters, and consist of ASCII letters, digits, or the characters +, /, =, and -. Invalid IDs will be ignored and replaced with generated ones.

Your app code can then read this ID and log it or use it for other purposes.

Router logs

These request IDs will also also be visible in the Heroku router logs as request_id. Here’s an example:

$ heroku logs --ps router
2013-03-10T19:53:10+00:00 heroku[router]: at=info method=GET path=/ request_id=f9ed4675f1c53513c61a3b3b4e25b4c0 fwd="" dyno=web.4 connect=0ms service=61ms status=200 bytes=1382

Example: correlate H13 error against app backtrace

Scenario: your app is reporting occasional H13 errors. The router reports the dyno closed the connection; how do we find the web dyno logs associated with one of these requests? Request IDs can help here, especially on a busy app with lots of data in the logs.

Find the router log line for the H13:

2010-10-06T21:51:37-07:00 heroku[router]: at=error code=H13 desc="Connection closed without response" method=GET path=/ request_id=30f14c6c1fc85cba12bfd093aa8f90e3 fwd="" dyno=web.15 connect=3030ms service=9767ms status=503 bytes=0

Note the request ID, which is 30f14c6c1fc85cba12bfd093aa8f90e3 in this example. Now you can search your logs (using Papertrail or other log search method) for that ID and find the request, logged by the middleware shown in the previous section:

2010-10-06T21:51:37-07:00 heroku[web.15]: request_id=30f14c6c1fc85cba12bfd093aa8f90e3
2010-10-06T21:51:37-07:00 heroku[web.15]: /usr/local/heroku/vendor/gems/excon-0.14.0/lib/excon/ssl_socket.rb:74: [BUG] Segmentation fault

Here we see that this request is causing the dyno to crash due to a bug in the SSL library and/or the Ruby interpreter. Upgrading to a newer version of Ruby might help, or if not, you now have the backtrace that you can use to file a bug with.

Usage with Rails

By default, Rails will pick up the X-Request-ID and set it as the requests UUID. To tie this request ID in with your application logs, add this line to your config/environments/production.rb

config.log_tags = [ :uuid ]

The values in your logs will then be tagged with the request ID:

2014-02-07T00:58:00.978819+00:00 app[web.1]: [6a7f7b2f-889b-4ae8-b849-db1f635c971c] Started GET "/" for at 2014-02-07 00:58:00 +0000

Here 6a7f7b2f-889b-4ae8-b849-db1f635c971c is the request ID. You can get more information on tagged logging with UUID in Rails here. Note that if there is not an available X-Request-ID, then Rails will generate a UUID for you, so if you enable this in development you will see a “request ID” even though one may not be available.

Usage with Ruby Rack middleware

Here is a sample Rack module for logging the request ID.

module Rack::LogRequestID
  def initialize(app); @app = app; end

  def call(env)
    puts "request_id=#{env['HTTP_X_REQUEST_ID']}"

For a more comprehensive Rack middleware, we recommend the Heroku Request ID gem, which can also provide timing information if you have Rack::Runtime enabled:

use Rack::Runtime

Usage with Node.js

If you’re using the logfmt Node module >=0.21.0, the X-Request-Id header will automatically be output to your logs on every web request as request_id.

If you prefer not to use logfmt in your Node app, be aware that Node’s core HTTP module downcases header keys, so the header will be named req.headers['x-request-id'].

Usage with Django

You can easily use all of the power of request IDs in Django with the excellent django-log-request-id library. First, you’ll need to install it by adding it to your requirements.txt file:


Next, you need to install the newly available Django middleware into your application’s Make sure that it’s the first specified middleware, or it may not function properly:


Finally, you need to modify to configure Django to properly log outgoing requests to stdout.

# Support for X-Request-ID


    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'request_id': {
            '()': 'log_request_id.filters.RequestIDFilter'
    'formatters': {
        'standard': {
            'format': '%(levelname)-8s [%(asctime)s] [%(request_id)s] %(name)s: %(message)s'
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler',
            'filters': ['request_id'],
            'formatter': 'standard',
    'loggers': {
        'log_request_id.middleware': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,

Usage with Java

Depending on the framework your application is using, the method of accessing and logging the request ID in Java can vary. This is a simplified example using a Java Servlet filter to access the header and print it to System.out.

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;

public class RequestIdLoggingFilter implements Filter {
    public void init(FilterConfig filterConfig) throws ServletException {}

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        if (servletRequest instanceof HttpServletRequest) {
            final String requestId = ((HttpServletRequest) servletRequest).getHeader("X-Request-ID");
            if (requestId != null) {
                System.out.println("request_id=" + requestId);
        filterChain.doFilter(servletRequest, servletResponse);

    public void destroy() {}


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi=""

Usage with PHP

Assuming you’re using the excellent Monolog package, you can simply register a custom processor that adds the contents of the X-Request-Id HTTP header to the extra fields array of the record:

use Monolog\Logger;
use Monolog\Handler\StreamHandler;


$logger = new Logger('my_logger');
$logger->pushHandler(new StreamHandler('php://stderr', Logger::DEBUG));

$logger->pushProcessor(function ($record) {
    $record['extra']['request_id'] = isset($_SERVER['HTTP_X_REQUEST_ID']) ? $_SERVER['HTTP_X_REQUEST_ID'] : null;
    return $record;

$logger->addInfo('Hello World');

The logged message will then contain the request ID in your heroku logs stream:

2014-11-19T15:10:32.027154+00:00 app[web.1]: [2014-11-19 15:10:31] my_logger.INFO: Hello World [] {"request_id":"a0a86a10-2a3a-4852-ae80-893e5d4e8348"}

For more information, check out the documentation section on Processors, or framework-specific documentation on integrating Monolog Processors using Symfony.

Usage with Scala

Depending on the framework your application is using, the method of accessing and logging the request ID in Scala can vary. This is a simplified example using a Play Framework filter to access the header and log it.

import play.api.mvc._
import play.api.Logger
import scala.concurrent.Future
import play.api.libs.concurrent.Execution.Implicits.defaultContext

object RequestIdLoggingFilter extends Filter {
  def apply(nextFilter: (RequestHeader) => Future[Result])
  (requestHeader: RequestHeader): Future[Result] = {"request_id=${requestHeader.headers.get("X-Request-ID").getOrElse("None")}")

You can use the filter by declaring a Global object.

object Global extends WithFilters(RequestIdLoggingFilter) {
  // ...

The logged message will then contain the request ID in your heroku logs stream:

2014-11-19T16:08:59.427428+00:00 app[web.1]: [info] application - request_id=2bcf9cec-7717-40e3-9bcf-b438b93b7770

Usage with Clojure

Depending on the framework your application is using, the method of accessing and logging the request ID in Clojure can vary. This an example using the Compojure routing library to access the header and log it.

(defroutes request-id-logger
  (ANY "*" {:keys [headers params body] :as request}
    (println (format "request_id=%s" (get headers "x-request-id")))))

As long as this route is declared before your other routes, it will be executed before the router moves on to match any other routes.

The logged message will then contain the request ID in your heroku logs stream:

2014-12-20T23:23:56.782823+00:00 app[web.1]: request_id=0e748c63-70ef-4d45-ab97-96510ddcdf5a