Reading Network Traffic in a DevTools Panel

Show and analyse the inspected page's network requests in a custom DevTools panel: devtools.network.onRequestFinished, HAR entries, getContent for bodies, getHAR for history, filtering, and memory limits.

Published October 2, 2026 Updated October 2, 2026 7 min read
Table of Contents

A team builds a DevTools extension that shows every GraphQL operation the page makes, decoded and grouped by operation name — something the built-in Network panel can’t do. In MV3 the obvious route, webRequest in the service worker, can’t read response bodies and requires host permissions for every site. The DevTools route can: a DevTools page gets chrome.devtools.network, which reports every request the inspected tab makes in HAR format, including response bodies on demand, with no host permissions at all — but only while DevTools is open on that tab. This guide builds a panel on that API. It belongs to side panel and DevTools interfaces.

What chrome.devtools.network provides

A DevTools extension declares devtools_page in the manifest; that page loads whenever DevTools opens for any tab and can create panels with chrome.devtools.panels.create. Both the DevTools page and its panels can use chrome.devtools.network: onRequestFinished fires with a HAR entry object for each completed request (URL, method, headers, status, timings, sizes), and entry.getContent(callback) retrieves the response body and its encoding. getHAR() returns all requests recorded so far in this DevTools session. onNavigated fires when the inspected page navigates. The API only sees the inspected tab, only while DevTools is open, and only what the Network panel records.

From the inspected page to the custom panelThe inspected page makes requests; DevTools records them; the panel's onRequestFinished listener receives HAR entries, filters them, fetches bodies with getContent for matching requests, decodes them and renders a table; onNavigated clears the view.Inspected pagefetch / XHRDevTools network logHARonRequestFinishedentryfilter + getContentMatch GraphQLPOST /graphqlentry.getContent()response bodyPanel tableoperation, timing, size
DevTools records; the panel filters, fetches bodies, and renders.

Step-by-step: a network analysis panel

1. Declare the DevTools page and create a panel

1"devtools_page": "devtools.html"
1// devtools.js (loaded by devtools.html)
2chrome.devtools.panels.create("GraphQL", "icons/16.png", "panel.html");

Execution context: the manifest and the DevTools page. The DevTools page is invisible; it exists to create panels and sidebars. No permissions are needed for devtools.network — access is implied by the user opening DevTools on the page. See building a custom DevTools panel.

2. Listen for finished requests in the panel

 1// panel.js
 2const rows = [];
 3chrome.devtools.network.onRequestFinished.addListener((entry) => {
 4  const { request, response } = entry;
 5  if (request.method !== "POST" || !/\/graphql\b/.test(request.url)) return;
 6  const body = request.postData?.text ?? "";
 7  let op = "(anonymous)";
 8  try { op = JSON.parse(body).operationName ?? op; } catch {}
 9  const row = { op, url: request.url, status: response.status, ms: Math.round(entry.time), size: response.bodySize, entry };
10  rows.push(row);
11  appendRow(row);
12});

Execution context: the DevTools panel page. Each HAR entry includes the request’s postData, so the GraphQL operation name is available without fetching the response. entry.time is total duration in milliseconds; response.bodySize is the transferred size (−1 if unknown). Listening in the panel means the listener only runs once the panel has been opened; to capture from DevTools open, listen in the DevTools page and buffer (step 5).

Ways to observe network traffic from an extensiondevtools.network, webRequest and declarativeNetRequest feedback compared on response bodies, permissions, when they work and what they suit.APIResponse bodiesPermissionsWorks whendevtools.networkYes (getContent)NoneDevTools open on tabwebRequest (MV3)NowebRequest + hostsAlwaysDNR onRuleMatchedDebugNoUnpacked onlyDevelopmentContent script fetch wrapPage's own callsHostsMain world only
Only devtools.network gives response bodies — at the cost of needing DevTools open.

3. Fetch response bodies on demand

 1function loadBody(row) {
 2  return new Promise((resolve) => {
 3    row.entry.getContent((content, encoding) => {
 4      const text = encoding === "base64" ? atob(content ?? "") : (content ?? "");
 5      resolve(text);
 6    });
 7  });
 8}
 9
10table.addEventListener("click", async (e) => {
11  const row = rows[e.target.closest("tr")?.dataset.index];
12  if (!row) return;
13  const text = await loadBody(row);
14  detail.textContent = prettyJson(text);
15});

Execution context: the DevTools panel. getContent is callback-based and returns the body as text, or base64 for binary responses. Fetch bodies only when the user selects a row — eagerly fetching every body on a busy page wastes memory and slows DevTools. Display with textContent (or a safe JSON viewer): response bodies are untrusted page data.

4. Clear on navigation and reload history

1chrome.devtools.network.onNavigated.addListener(() => {
2  if (!preserveLog.checked) { rows.length = 0; table.tBodies[0].replaceChildren(); }
3});
4
5// When the panel opens, backfill from what DevTools already recorded
6chrome.devtools.network.getHAR((har) => {
7  for (const entry of har.entries) handleEntry(entry);
8});

Execution context: the DevTools panel. Mirroring the Network panel’s “Preserve log” checkbox gives users a familiar control. getHAR returns entries recorded before the panel was opened, so opening the panel late does not lose earlier traffic. HAR entries from getHAR also support getContent in current Chrome versions.

Selecting a request in the panelA GraphQL request finishes; the panel appends a row with the operation name; the user clicks the row; the panel calls getContent; DevTools returns the body; the panel renders pretty-printed JSON.DevToolsCustom panelUseronRequestFinished(entry)row: GetCart · 200 · 84 msclick rowentry.getContent()body (text)pretty JSON
Metadata on arrival, body on demand.

5. Capture before the panel is opened

1// devtools.js — runs as soon as DevTools opens
2const buffer = [];
3chrome.devtools.network.onRequestFinished.addListener((e) => { buffer.push(e); if (buffer.length > 500) buffer.shift(); });
4chrome.devtools.panels.create("GraphQL", "icons/16.png", "panel.html", (panel) => {
5  panel.onShown.addListener((win) => win.receiveBuffer?.(buffer.splice(0)));
6});

Execution context: the DevTools page. The panel’s window only exists once the user selects the panel. Buffering in the DevTools page (with a cap) and handing the buffer over in onShown captures traffic from the moment DevTools opens. The panel window object is passed to onShown, and the panel can expose a function on window to receive data.

6. Bound memory

Keep at most a few thousand rows, drop the entry reference for old rows, and never store response bodies unless the user asks. DevTools itself holds the network log; your panel should hold only what it displays.

7. Send data to the service worker only when needed

If the panel should save a capture or share it with other extension UI, send selected entries to the service worker with chrome.runtime.sendMessage or a port, identifying the tab with chrome.devtools.inspectedWindow.tabId. See connecting a DevTools panel to the service worker.

8. Filter and search like the Network panel

1const filterInput = document.querySelector("#filter");
2filterInput.addEventListener("input", () => {
3  const q = filterInput.value.trim().toLowerCase();
4  for (const tr of table.tBodies[0].rows) {
5    const row = rows[tr.dataset.index];
6    tr.hidden = Boolean(q) && !(`${row.op} ${row.url} ${row.status}`.toLowerCase().includes(q));
7  }
8});

Execution context: the DevTools panel. Developers expect a filter box that narrows the table as they type, matching operation names, URLs and status codes. Hiding rows is fine here because the table is bounded; for very long captures, filter the rows array and re-render instead. Add quick toggles for common cases — “errors only” (status ≥ 400), “slow” (over 500 ms) — which are what people look for most often.

Common mistakes

  • Expecting webRequest to read bodies. It cannot in MV3.
  • Fetching every body eagerly. Slows DevTools; fetch on selection.
  • Listening only in the panel. Misses traffic before the panel is opened.
  • Rendering bodies with innerHTML. Page data is untrusted.
  • Unbounded row arrays. Memory grows on long sessions.

Cross-browser variation

  • Chrome / Edge: devtools.network with onRequestFinished, getHAR, getContent, onNavigated.
  • Firefox: supports devtools.network.onRequestFinished, getHAR and onNavigated; getContent returns a promise of [content, mimeType].
  • Safari: Web Inspector extensions support devtools.network in recent versions; check API coverage for your target version.

Verification

  1. Open DevTools on a page that uses GraphQL, open the panel, and confirm operations appear with names.
  2. Open DevTools, trigger requests, then open the panel — confirm earlier requests appear.
  3. Click a row and confirm the body renders as text.
  4. Navigate with Preserve log off and confirm the table clears.

FAQ

Can the panel see requests when DevTools is closed?

No. The API exists only while DevTools is open on the tab.

Can I modify requests from the panel?

No. devtools.network is read-only; use DNR rules to modify requests.

Does it see service worker or worker requests?

It sees what the Network panel shows for the inspected target, which includes requests from the page’s dedicated workers in current Chrome.

Can I export the captured requests?

Yes. Build a HAR object from the selected entries — or use getHAR for everything — and download it from the panel with an object URL. Strip cookies and authorisation headers before export, since HAR files often end up in bug reports.

Does reading traffic slow the inspected page?

Listening to onRequestFinished has negligible cost because DevTools already records the traffic. Fetching many bodies or doing heavy parsing in the panel can make DevTools itself slower, so keep work on demand.

Other UI/UX Patterns & Interactive Components Resources