Chrome Extension Snippet - Modify Response Header

Posted at

If you would like to 'fake give yourself CORS' for every request, modify cookies to remove the 'http' only directive so you could always edit cookies using javascript, or perhaps just add an X-Hello: World header to every response you get from anywhere, to fool your browser to think it is the real thing, got from a foo-server,
- here is what you need to do:

prepare manifest with the following permissions:
, "permissions"             : ["*://*/*"

allow your background script to run in the background:
, "background"              : { "scripts":    [ 
                              , "persistent": true 

in your background javascript file, add a listener to chrome.webRequest.onHeadersReceived

function headers_handler(response){
//shamelessly override/modify response.responseHeaders array (it's ok since it is just a local copy)
return {"responseHeaders": response.responseHeaders};

filters = {urls: [""]
,types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object"]
info_spec = ["responseHeaders", "blocking"];

chrome.webRequest.onHeadersReceived.addListener(headers_handler, filters, info_spec);

- if only want to work on HTML pages- limit the filters to just ["main_frame", "sub_frame"] .
- you may add "ping","other" to modify other resources too.. .
- you may keep info_spec set to both ["responseHeaders", "blocking"] even if you don't want to block anything, some resources say it helps for some reasons, since it makes some of the pipelines synchronous. but it will also requires the 'webRequestBlocking' permission.

do you want to debug the final-version of the response-headers after all the modification? simply add a listener to chrome.webRequest.onResponseStarted, go to chrome://extensions/, click the inspect the generated-background page, switch to console to see anything you write into the background-script's console.log.

- response.responseHeaders is an array of plain objects {name: '.....', value: '.....'}, you can modify, delete or add new entries onto the response.responseHeaders itself, since while in the function, it is just a local-copy, so it does not change anything, when done- return an object having the key responseHeaders with your the modify version.

- here is a (very limited) link to Chrome's documentations:

- here is a full example for a background script that modify the 'cache-control' (notify that header's name is always lower-case) to have an additions of the new stale-while.. directive.

it is part of my Boostaler (= boost+stale+er) Chrome extension (GitHub, Chrome Store).

/* ╔════════════════════════════════════════╗
   ║ background_response_manipulation ║
   ║ ║
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ */

regex_stalewhile = /stale-while-revalidate/i;
regex_staleiferror = /stale-if-error/i;
regex_firstcomma = /^\\,/;
regex_doublecomma = /,,+/g;

function headers_handler(response){
// extract original
var is_edited = false;

response.responseHeaders.forEach(function(item, index){
if("cache-control" !== return;

if(false === regex_stalewhile.test(item.value)) item.value += ",stale-while-revalidate=86400"; //edit existing
if(false === regex_staleiferror.test(item.value)) item.value += ",stale-if-error=259200";

item.value = item.value.replace(regex_firstcomma, "").replace(regex_doublecomma, ""); //fix

is_edited = true; //if false, there was no cache-control header

if(false === is_edited) response.responseHeaders.push({name:"cache-control", value: "stale-while-revalidate=86400,stale-if-error=259200"});

return {"responseHeaders": response.responseHeaders};

filters = {urls: ["<all_urls>"]
,types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object"]
info_spec = ["responseHeaders", "blocking"];

chrome.webRequest.onHeadersReceived.addListener(headers_handler, filters, info_spec);

//for debugging - will shows (readonly) the final version of the response-headers, after modified by ALL the extensions.
chrome.webRequest.onResponseStarted.addListener(function(response){ console.log(response); }, filters, ["responseHeaders"]);

here is a modification that will allow more/less "listening for requests"...
the third option is not accepted by Chrome, just Mozilla browsers.. but it's nice to try :)

          //,types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object"]
            ,types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "other"]   /* Chrome max value: */
          //,types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "other", "xbl", "xslt", "beacon", "xml_dtd", "media", "websocket", "csp_report", "imageset", "web_manifest"] /* Mozilla max value: */

since XMLHTTPRequest objects are usually called for specific request,
part from those whom are called regularly.. there is not much of a point in including it..

ping might have positive effect (although ping requests are forbidden now days in new-chrome versions..),
other can be anything really, maybe it includes media, beacon which is helpful to be stale'able.. :)

so really what we're having is:

filters = {urls: [""]
//,types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object"] /*original set*/
,types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "ping", "other"] /*all but not xhr, always keep the xhr most-updated...*/
//,types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "font", "object", "xmlhttprequest", "ping", "other"] /* Chrome max value: -- other might be ["xbl", "xslt", "beacon", "xml_dtd", "media", "websocket", "csp_report", "imageset", "web_manifest"] from: Mozilla max value: */