# X-Response-Time

> Learn how the X-Response-Time header indicates server processing time in milliseconds. Useful for performance monitoring and debugging slow requests.

Source: https://howhttpworks.com/headers/x-response-time
Last reviewed: 2026-10-05

> **TL;DR:** `X-Response-Time` has no standard measurement boundary or format. Express's response-time middleware measures from middleware entry until response headers are written, in milliseconds. It does not measure complete body transfer or browser round-trip latency.

The field is an application convention. Check the producer before interpreting its units or comparing values across services. This page's concrete behavior refers to Express's documented [response-time middleware](https://expressjs.com/en/resources/middleware/response-time/).

## Example Response

Field format:

```text
X-Response-Time: <elapsed-milliseconds>ms
```

With default options, the middleware uses the name `X-Response-Time`, three decimal places, and the `ms` suffix. Options can change the name, precision, and suffix.

## What It Usually Measures

For this middleware, the clock starts when the request enters it and stops before headers are written. Work before that middleware is excluded. With a streamed response, later chunks can be generated long after this timer stops. DNS, TLS negotiation, and browser rendering are outside its measurement boundary.

## Correct Express Implementation

Install `express` and `response-time` in your example project with `pnpm add express response-time`. Save this as `app.mjs` and run `node app.mjs`:

```javascript
import express from 'express'
import responseTime from 'response-time'

const app = express()
app.use(responseTime())
app.get('/', (req, res) => res.json({ status: 'ok' }))
app.listen(3000, '127.0.0.1')
```

Inspect the measured value:

```bash
curl -sS -D - http://127.0.0.1:3000/ -o /dev/null
```

To change precision and the field name, replace the middleware registration with documented options:

```javascript
app.use(responseTime({ digits: 1, header: 'X-Response-Time', suffix: true }))
```

Changing precision affects formatting, not the clock's start and stop boundaries. With this registration, the field still stops at header output even if a later streaming chunk takes much longer to generate.

## When to Expose It

Treat the timing as public response data wherever you send it. If a cross-origin browser client must read it through Fetch, the server also needs the appropriate CORS policy and this illustrative exposure field:

```http
Access-Control-Expose-Headers: X-Response-Time
```

This controls script access, not visibility to curl or other HTTP clients. See the [Fetch Standard's CORS exposure rules](https://fetch.spec.whatwg.org/#http-access-control-expose-headers).

## Recommended Standard Alternative

[Server-Timing](https://howhttpworks.com/headers/server-timing) defines named metrics and Performance API integration. To publish both from the same measurement, replace `app.use(responseTime())` above with:

```javascript
app.use(responseTime((req, res, time) => {
  res.setHeader('X-Response-Time', `${time.toFixed(3)}ms`)
  res.setHeader('Server-Timing', `app;dur=${time.toFixed(3)}`)
}))
```

## Measuring and Exposing Response Time Correctly

Setting a header in Node's `finish` event is too late. Calling `setHeader()` after headers were sent throws [`ERR_HTTP_HEADERS_SENT`](https://nodejs.org/api/errors.html#err_http_headers_sent); it does not silently update the response.

The [middleware source](https://github.com/expressjs/response-time/blob/master/index.js) uses `on-headers` and `process.hrtime()` to calculate the duration before header output. It does not simply wait for `res.end()` or for the response to finish.
