A key advantage Fiddler has over a traditional packet sniffer or in-browser network monitor is the ability to modify the traffic. This can be done manually (using breakpoints and Inspectors) or automatically, using FiddlerScript or extensions.
In today’s post, I’d like to introduce one such extension—ContentBlock. This extension allows you to block certain requests to simulate the behavior of users who are browsing with ad-blockers or other methods.
After ContentBlock.dll is installed in Fiddler’s Scripts folder and Fiddler is restarted, a new top-level menu appears, titled ContentBlock. This menu exposes the following options:
The Enabled checkbox controls whether the extension operates at all. It’s a best-practice to include such an option in all extensions so that users don’t need to uninstall your extension when they aren’t using it.
The Block Paths checkbox controls whether Fiddler will block requests whose URL path component contains /ad/, /ads/, or /advert.
The Edit Blocked Hosts… item enables the user to modify the list of hostnames to which all requests are blocked.
The Always Block Flash option will block all requests which are issued by Flash plugin.
The Block X-Domain Flash will block download of .SWF files when the SWF file is hosted by a different domain than the request’s Referer HTTP header.
When the Short-Circuit Redirects option is set, Fiddler will attempt to detect URLs that are likely to be used only for logging purposes (e.g. http://example.com/logger.php?goto=http://targetsite.com/page123.htm). When such a request is seen, Fiddler will strip off the front of the URL and will return a HTTP/307 redirection to the target site, causing the request to bypass the logging page.
The Hide Blocked Sessions item controls whether Fiddler hides blocked requests from the Session list. If not set, the blocked requests will instead be listed using a strikethrough font:
Under the Hood
Even if you’re not interested in testing blocked content, the extension’s source code (included in the ContentBlock.zip file) demonstrates a number of important concepts.
Adding Menu Items
Fiddler’s Main menu and the context menu on the Web Sessions list are easily extended to add the extensions’ options. Simply construct the desired menu items, then merge them to the existing Fiddler menus in the extension’s OnLoad handler:
public void OnLoad()
* NB: You might not get called here until ~after~ one of the
* IAutoTamper methods was called. This is okay for us, because
* we created our mnuContentBlock in the constructor and its simply
* not visible anywhere until this method is called and we merge it
* onto the Fiddler Main menu. */
Returning a Local File
Returning a different file for a given request is simple. In the extension’s AutoTamperRequestBefore method, evaluate the request and if it matches a block rule, set the Session object’s X-ReplyWithFile flag with the name of the target file to return:
// First look for static prefixes:
(sHost.StartsWith("ad.") || sHost.StartsWith("ads.")
// Then check the list of blocked hosts:
// Set the ReplyWithFile Session flag so that Fiddler will return the specified
// 1x1 GIF response rather than hitting the server
oSession["x-replywithfile"] = "1pxtrans.dat";
When Fiddler sees this flag set, it will simply return the specified file instead of sending the request to the server.
Inside the snippet above, you may have noticed the hlBlockedHosts object. This is an instance of a Fiddler.HostList class. This class allows extensions to easily store and match against a list of hosts.
A simple wildcarding prefix (*) is supported, as follows:
|example.com ||Any request whose host is exactly “example.com” matches. “www.example.com” does not match. |
|*example.com ||Any request whose host ends with “example.com” matches. So both “example.com” and “www.example.com” match. |
|*.example.com ||Any request whose host ends with “.example.com” matches. So “www.example.com” matches, but “example.com” does not. |
If a port is specified in the rule (e.g. *.example.com:443), then it will be matched, otherwise any port will match.
The HostLists also recognize four special tokens:
|* ||All hosts match. |
|<loopback> ||Matches 127.0.0.1, ::1, localhost and localhost. |
|<local> ||All dotless hostnames match. |
|<nonlocal> ||All non-dotted hostnames match. |
The BlockList is easily initialized from a semicolon-delimited string. In this case, we use a Preference to store the value so that it is easily saved and reloaded between Fiddler instances:
string sList = FiddlerApplication.Prefs.GetStringPref("ext.ContentBlocker.BlockHosts", null);
When Fiddler is unloading our extension, we ensure that all of the extensions’ Preferences are updated with the current settings.
public void OnBeforeUnload()
// Because we're caching all setting values locally rather than
// immediately storing them back to Preferences, we now need to
// serialize those setting values back into the Preferences system.
// If we always simply wrote/queried the preferences directly, we
// wouldn't need this step or the corresponding deserialization when
// our extension starts up. The downside is that there's a slight
// performance overhead in querying the Preferences system's Dictionary
// (including its internal locking used for thread-safety).
Fiddler automatically ensures that the updated Preferences are stored to the registry on exit.
Returning Simple Errors
In some cases, you might not want to return a local file at all, instead preferring to return a simple string and error code. This is easily accomplished with the FailSession method.
"Fiddler - ContentBlock", "Blocked Flash");
oSession.state = SessionStates.Done;
The code above returns a simple HTTP/404 response, bypassing the server.
In other cases, you may need to generate a more complicated HTTP response, with headers and/or body that are dynamically determined. The utilCreateResponseAndBypassServer method generates an empty response which you can modify using the Fiddler API:
if (ixSubURI > 0)
// It looks like the URL contains an embedded URL target; often this is used
// for tracking purposes. We will skip sending the request to the original
// server and instead return a redirect pointed directly at the target server.
// The client will then contact that host directly.
oSession.responseCode = 307;
oSession.oResponse.headers.HTTPResponseStatus = "307 Short-Circuited Redirect";
string sTarget = oSession.PathAndQuery.Substring(ixSubURI);
if (bNeedDecode) sTarget = Utilities.UrlDecode(sTarget, Encoding.UTF8);
oSession.oResponse.headers["OriginalURI"] = oSession.fullUrl;
oSession.oResponse.headers["Date"] = DateTime.Now.ToString("R");
oSession.oResponse.headers["Location"] = sTarget;
In the example above, we generate a HTTP/307 response with a Location header generated from the original request’s URL.
Tweak to Taste
Fiddler’s rich extensibility model allows alteration of web traffic to meet almost any need. I hope the sample in this post helps you achieve your own goals!