NGINX Server-Timing

RUMvision can collect NGINX timing data when your website exposes it through the Server-Timing response header.

This helps you understand whether slow server response times are caused by NGINX proxying, upstream connection time, or waiting for your application server.

NGINX does not expose standard Server-Timing metrics by default. If your site is not exposing these headers yet, you can add them in your NGINX configuration.

Add Server-Timing via NGINX

Below are the recommended fields to expose. Once these fields are exposed and NGINX is selected in your tech stack settings and enabled the NGINX checkbox, we will automatically collect them.

ValueType/sourceMeaning
upstream_connect_timeMetric
$upstream_connect_time
Time spent establishing a connection with the upstream server, including TLS handshake when applicable.
upstream_header_timeMetric
$upstream_header_time
Time between connecting to the upstream server and receiving the first byte of the upstream response header.
upstream_cache_statusFilter
$upstream_cache_status
Shows NGINX cache behavior, such as HIT, MISS, BYPASS, EXPIRED, or STALE.

Where to implement

Implement this in the NGINX configuration that handles your HTML document requests.

/etc/nginx/nginx.conf
/etc/nginx/conf.d/*.conf

The example below uses NGINX njs to convert NGINX timing variables from seconds to milliseconds, because Server-Timing expects durations in milliseconds.

Rule to implement

Apply this to all HTML document requests. These are the main page requests RUMvision uses to understand document TTFB and backend-related delays.

This usually means the main document request of your pages, not images, scripts, stylesheets, or API calls.

If needed, you can also expose these timings on other request types, but HTML document requests are the recommended starting point.

Create this njs file:

// /etc/nginx/timings.js 
function toMs(value) {
  if (!value || value === '-') {
    return '0';
  }
  const firstValue = String(value).split(/[,:]/)[0];
  const seconds = Number(firstValue);
  if (!Number.isFinite(seconds)) {
    return '0';
  }
  return String(Math.round(seconds * 1000));
}

function upstreamConnect(r) {
  return toMs(r.variables.upstream_connect_time);
}

function upstreamHeader(r) {
  return toMs(r.variables.upstream_header_time);
}
export default {
  upstreamConnect,
  upstreamHeader
};

Then update your NGINX configuration:

load_module modules/ngx_http_js_module.so;

http {
  js_import timings from /etc/nginx/timings.js;

  js_set $rumvision_upstream_connect timings.upstreamConnect;
  js_set $rumvision_upstream_header timings.upstreamHeader;

  server {
    location / {
      proxy_pass http://backend;

      add_header Server-Timing 'nginxConnect;dur=$rumvision_upstream_connect, nginxOrigin;dur=$rumvision_upstream_header, nginxCache;desc="$upstream_cache_status"' always;
    }
  }
}

If you do not use NGINX caching, you can remove the nginxCache part from the header value.

Testing the outcome

Next step is testing the outcome. You could either wait for data to arrive in your RUMvision dashboard, or proactively test the outcome using the DevTools of your preferred browser.

Expected outcome

After deploying the configuration, your HTML document should return a Server-Timing header similar to this:

Server-Timing: nginxConnect;dur=12, nginxOrigin;dur=248, nginxCache;desc="MISS"

For a cached response, upstream values may be 0 and cache status may be HIT.

Server-Timing: nginxConnect;dur=0, nginxOrigin;dur=0, nginxCache;desc="HIT"

Check the response headers

To verify the setup:

  1. Open your website in Chrome or Edge.
  2. Open DevTools.
  3. Go to the Network tab.
  4. Reload the page.
  5. Click the main HTML document request.
  6. Check the Response Headers section.
  7. Look for server-timing.

Test in DevTools Console

As the browser exposes Server-Timing values through the PerformanceServerTiming interface, you can also test it in the browser console by running the following JavaScript:

const navEntries = window.performance.getEntriesByType('navigation');
console.table( navEntries[0].serverTiming );

Important

In general, be sure to:

  • Always test on staging environment before deploying these steps to a production environment.
  • Avoid exposing sensitive internal details. Server-Timing data is visible in the browser, so only expose metrics that are safe to share with visitors and third-party scripts.

NGINX notes

NGINX timing variables such as $upstream_connect_time and $upstream_header_time are measured in seconds with millisecond resolution. The example above converts them to milliseconds before exposing them as Server-Timing durations.

Do not use $upstream_response_time for this response header. That value is only known after the full upstream response body has been received, which can be too late for a normal response header.

If your origin already sends a Server-Timing header, make sure your NGINX configuration does not remove useful existing metrics.