Chrome Extension Web Request Patterns — Developer Guide

7 min read

WebRequest API Patterns

Overview

Request Lifecycle Events

// Events fire in this order for each request:
// 1. onBeforeRequest       — request about to be made (can cancel/redirect)
// 2. onBeforeSendHeaders   — headers about to be sent (can modify headers)
// 3. onSendHeaders         — headers sent (informational)
// 4. onHeadersReceived     — response headers received (can modify)
// 5. onAuthRequired        — HTTP auth needed (can provide credentials)
// 6. onResponseStarted     — first byte received (informational)
// 7. onCompleted           — request complete (informational)
// or onErrorOccurred       — request failed

Observing Requests (MV3 Compatible)

// Monitor all requests (no blocking)
chrome.webRequest.onCompleted.addListener(
  (details) => {
    console.log(`${details.method} ${details.url} -> ${details.statusCode}`);
    console.log('Type:', details.type); // "main_frame", "sub_frame", "script", "image", etc.
    console.log('Tab:', details.tabId);
    console.log('Time:', details.timeStamp);
  },
  { urls: ["<all_urls>"] }  // URL filter (required)
);

// Filter to specific URLs
chrome.webRequest.onCompleted.addListener(
  (details) => { /* only fires for matching URLs */ },
  { urls: ["*://*.example.com/*", "https://api.github.com/*"] }
);

// Filter by resource type
chrome.webRequest.onCompleted.addListener(
  (details) => { /* only XHR/fetch requests */ },
  { urls: ["<all_urls>"], types: ["xmlhttprequest"] }
);

Request Types

Reading Headers

// Read request headers
chrome.webRequest.onSendHeaders.addListener(
  (details) => {
    const cookies = details.requestHeaders?.find(h => h.name === 'Cookie');
    const auth = details.requestHeaders?.find(h => h.name === 'Authorization');
    console.log('Cookies:', cookies?.value);
    console.log('Auth:', auth?.value);
  },
  { urls: ["<all_urls>"] },
  ["requestHeaders"]  // Extra info spec — needed to access headers
);

// Read response headers
chrome.webRequest.onHeadersReceived.addListener(
  (details) => {
    const contentType = details.responseHeaders?.find(
      h => h.name.toLowerCase() === 'content-type'
    );
    console.log('Content-Type:', contentType?.value);
  },
  { urls: ["<all_urls>"] },
  ["responseHeaders"]
);

Blocking Requests (MV2 / Enterprise MV3)

// NOTE: In MV3, use declarativeNetRequest instead for public extensions
// webRequestBlocking only works in MV2 or MV3 with enterprise policy

// Cancel requests
chrome.webRequest.onBeforeRequest.addListener(
  (details) => {
    if (details.url.includes('tracking.js')) {
      return { cancel: true };
    }
  },
  { urls: ["<all_urls>"] },
  ["blocking"]
);

// Redirect requests
chrome.webRequest.onBeforeRequest.addListener(
  (details) => {
    return { redirectUrl: details.url.replace('http://', 'https://') };
  },
  { urls: ["http://*/*"] },
  ["blocking"]
);

// Modify request headers
chrome.webRequest.onBeforeSendHeaders.addListener(
  (details) => {
    details.requestHeaders.push({ name: 'X-Custom', value: 'my-extension' });
    return { requestHeaders: details.requestHeaders };
  },
  { urls: ["<all_urls>"] },
  ["blocking", "requestHeaders"]
);

Network Request Logger

import { createStorage, defineSchema } from '@theluckystrike/webext-storage';
import { createMessenger } from '@theluckystrike/webext-messaging';

const storage = createStorage(defineSchema({
  requestLog: 'string',    // JSON: Array<{ url, method, status, type, time }>
  logEnabled: 'boolean'
}), 'local');

// Log completed requests
chrome.webRequest.onCompleted.addListener(async (details) => {
  const enabled = await storage.get('logEnabled');
  if (!enabled) return;

  const raw = await storage.get('requestLog');
  const log = raw ? JSON.parse(raw) : [];
  log.unshift({
    url: details.url,
    method: details.method,
    status: details.statusCode,
    type: details.type,
    time: details.timeStamp
  });
  await storage.set('requestLog', JSON.stringify(log.slice(0, 1000)));
});

MV3 Migration: webRequest to declarativeNetRequest

| Feature | webRequest (MV2) | declarativeNetRequest (MV3) | |———|—————–|—————————| | Block requests | onBeforeRequest + blocking | Static/dynamic rules | | Modify headers | onBeforeSendHeaders + blocking | modifyHeaders action | | Redirect | Return { redirectUrl } | redirect action | | Observe only | All events | N/A (use webRequest) | | Performance | Extension code runs per request | Browser-native rule matching | | Cross-ref | This guide | docs/mv3/declarative-net-request.md |

Common Mistakes

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.