WebRequest API

Use the chrome.experimental.webRequest module to intercept, block, or modify requests in-flight. This module is still very much experimental. For information on how to use experimental APIs, see the chrome.experimental.* APIs page.

Manifest

You must declare the "experimental" permission in the extension manifest to use the webRequest settings API. For example:

{
  "name": "My extension",
  ...
  "permissions": [
    "experimental"
  ],
  ...
}

Life-cycle of requests

The webRequest API defines the following events:

onBeforeRequest (optionally synchronous)
Fires when a request is about to occur. This is sent before any TCP connection is made and can be used to cancel or redirect requests.
onBeforeSendHeaders (optionally synchronous)
Fires when a request is about to occur and the initial headers are prepared. The event is intended to allow extensions to add, modify and delete request headers (*). The onBeforeSendHeaders event is passed to all subscribers, so different subscribers may attempt to modify the request, see section conflict resolution for details how this is handled. This event can still be used to cancel the request.
onSendHeaders
Fires after all extensions had a chance of modifying the request headers and presents the final (*) version. The event is triggered, before the headers are sent to the network. This event is informational and handled asynchronously. It does not allow to modify or cancel the request.
onResponseStarted
Fires when the first byte of the response body is received. For HTTP requests, this means that the status line and response headers are available. This event is informational and handled asynchronously. It does not allow to modify or cancel the request.
onBeforeRedirect
Fires before a redirect is about to be executed. A redirection can be triggered by a HTTP response code or by an extension. This event is informational and handled asynchronously. It does not allow to modify or cancel the request.
onCompleted
Fires when a request has been processed successfully.
onErrorOccurred
Fires when a request could not be processed successfully.
The webRequest API gurantees that for each request either onComplete or onErrorOccurred is fired as the final event.

The life-cycle of successful requests can be illustrated as follows:

  |
  v
onBeforeRequest --------------------------------
  |          ^             |                    | [data and file URLs]
  |          |             | [redirection       |
  |           -------      |  from extension]   |
  v                  |     |                    |
onBeforeSendHeaders  |     |                    |
  |     ^            |     |                    |
  v     | [auth]     |     |                    |
onSendHeaders        |     |                    |
  |     |            |     |                    |
  |     v            |     |                    |
  |    onBeforeRedirect <--                     |
  |                                             |
  v                                             |
onResponseStarted <-----------------------------
  |
  v
onCompleted

[auth] = In case of an basic/digest authentication request from server, we send
another HTTP request with the respective headers.
Note that this diagram does not capture a bug that will be fixed soon: If extensions redirect a URL request via the webRequest API, this redirection does not trigger onBeforeRedirect. Instead the request is cancelled and a new request is started at onBeforeRedirect. See http://crbug.com/79520.

(*) Note that the webRequest API presents an abstraction of the network stack to the extension. Internally, one URL request can be split into several HTTP requests (for example to fetch individual byte ranges from a large file) or can be handled by the network stack without communicating with the network. For this reason, the API does not provide the final HTTP headers that are sent to the network. For example all headers that are related to caching are invisible to the extension.

This is a list of headers that are currently not provided to the onBeforeSendHeaders signal. The list is not guaranteed to be complete nor stable:

Concepts of the webRequest API

The signals of the webRequest API follow certain concepts and patterns that shall be described in the following.

Request IDs

Each request is identified by a request ID. This ID is unique within a browser session and the context of an extension. It remains constant during the the life-cycle of a request and can be used to match signals for the same request.

Subscription

For each signal XXX of the webRequest API, the API provides a function chrome.experimental.webRequest.XXX.addListener() with the following signature.

var callback = function(details) {...};
var opt_filter = {...};
var opt_extraInfoSpec = [...];

chrome.experimental.webRequest.XXX.addListener(
  callback, opt_filter, opt_extraInfoSpec);

Each addListener() call takes a mandatory callback function as the first parameter. This callback function is passed a dictionary containing information about the current URL request. The information in this dictionary depends on the specific event type as well as the content of opt_extraInfoSpec.

If the optional opt_extraInfoSpec array contains the string 'blocking' (only allowed for specific signals), the callback function is handled synchronously. That means that the request is blocked until the callback function returns. In this case, the callback can return a BlockingResponse that determines the further life-cycle of the request. Depending on the context, this response allows cancelling or redirecting a request (onBeforeRequest), or cancelling or modifying headers (onBeforeSendHeaders).

Depending on the specific signal, opt_extraInfoSpec may contain further strings that indicate that specific information shall be passed to the extension. This is used to provide detailed information on requests data only if explicitly requested.

The optional RequestFilter opt_filter allows to limit the requests for which events are triggered in various dimensions:

URLs
URL patterns like *://www.google.com/foo*bar.
Types
Request types like main_frame (a document that is loaded for a top-level frame), sub_frame (a document that is loaded for an embedded frame), image (an image on a web site) and others. See RequestFilter.
Tab IDs
The ID that identifies a specific tab in a window.
Window IDs
The ID that identifies a specific window.

Conflict resolution

In the current implementation of the webRequest API, a request is considered as canceled if at least one extension instructs to cancel the request. If an extension cancels a request, all extensions are notified by an onErrorOccurred event. Only one extension is allowed to redirect a request or modify a header at a time. If more than one extension attempts to modify the request, the most recently installed extension wins while all others are ignored. An extension is currently not notified, if its instruction to modify or redirect has been ignored.

A note about timestamps

It's important to note that some technical oddities in the OS's handling of distinct Chrome processes can cause the clock to be skewed between the browser itself and extension processes. That means that WebRequest's events' timeStamp property is only guaranteed to be internally consistent. Comparing one event to another event will give you the correct offset between them, but comparing them to the current time inside the extension (via (new Date()).getTime(), for instance) might give unexpected results.

Examples

The following example illustrates how to block all requests to www.evil.com:

chrome.experimental.webRequest.onBeforeRequest.addListener(
  function(details) {
    return {cancel: details.url.indexOf("://www.evil.com/") != -1};
  },
  {},
  ["blocking"]);

The following example achives the same goal in a more efficient way because requests that are not targeted to www.evil.com do not need to be passed to the extension:

chrome.experimental.webRequest.onBeforeRequest.addListener(
  function(details) { return {cancel: true}; },
  {"*://www.evil.com/*"},
  ["blocking"]);

The following example illustrates how the User-Agent header can be deleted from all requests:

chrome.experimental.webRequest.onBeforeSendHeaders.addListener(
  function(details) {
    delete details.requestHeaders['User-Agent'];
    return {requestHeaders: details.requestHeaders};
  },
  {},
  ["blocking"]);

Tracking frames

For efficiency reason, the webRequest API does not pass the URL of the frame that issued a request to each request. If this information is required, for example to distinguish between first and third party requests, this example shows how to track the URLs of frames.

// dictionary "windowId" -> "tabId"-"frameId" -> "frameUrl"
var frameUrl = {};

function recordFrameUrl(windowId, tabId, frameId, frameUrl) {
  if (!frameUrl[windowId]) {
    frameUrl[windowId] = {};
  }
  frameUrl[windowId][tabId + "-" + frameId] = frameUrl;
}

function getFrameUrl(windowId, tabId, frameId, frameUrl) {
  return (frameUrl[windowId] || {})[tabId + "-" + frameId];
}

chrome.experimental.webRequest.onBeforeRequest.addListener(
  function(d) {
    if (d.type == 'main_frame' || d.type == 'sub_frame') {
      recordFrameUrl(d.windowId, d.tabId, d.frameId, d.frameUrl);
    }
    var frameUrl = getFrameUrl(d.windowId, d.tabId, d.frameId);
    // Use the frameUrl e.g. to selectively cancel requests.
    // Attention: The frameUrl can be undefined in some cases. Requests may not
    // originate from a frame (e.g. requests from extensions or shared workers).
  });

chrome.windows.onRemoved.addListener(
  function(windowId) {delete frameUrl[windowId];}
  );