Skip to main content
Build Custom Fiddler Inspectors for Deep HTTP Debugging
Advanced Techniques7 min read

Build Custom Fiddler Inspectors for Deep HTTP Debugging

Learn how to build production-ready custom Fiddler inspectors for JWT, Protobuf, GraphQL, and more — with step-by-step C# code, troubleshooting tips, and HTTPS decryption safety checks.

Share:

Why Custom Inspectors Elevate Your Fiddler Debugging Workflow

Fiddler isn’t just a passive HTTP proxy — it’s a programmable debugging platform. While built-in inspectors like Raw, TextView, and JSON Visualizer cover common use cases, real-world API testing, microservice troubleshooting, and security validation often demand deeper context: custom headers, encrypted payload previews, or domain-specific syntax highlighting. That’s where custom inspectors shine. They transform Fiddler from a generic traffic viewer into an extensible, domain-aware fiddler debugging environment — especially critical when analyzing complex https decryption flows or validating edge-case request patterns.

Unlike session filters or AutoResponder rules, inspectors operate at the UI layer, giving you full control over how each request or response is rendered, parsed, validated, or even modified in real time. And because they’re written in C# and compiled as .NET assemblies, they integrate seamlessly with FiddlerCore, enabling reuse in headless automation scenarios.

This tutorial walks you through building, testing, and deploying a production-ready custom inspector — no guesswork, no outdated APIs. You’ll learn how to hook into Fiddler’s rendering pipeline, parse binary payloads safely, and avoid common pitfalls that break http debugging reliability.

Prerequisites and Setup

Before writing code, ensure your environment supports Fiddler extensibility:

  • Fiddler v5.0+ (v6.x recommended; legacy v4 uses different extension models)
  • .NET SDK 6.0 or later (Fiddler extensions target net6.0 or net8.0)
  • Visual Studio 2022 or VS Code + C# extension
  • Fiddler’s Extensibility SDK: Install via NuGet Package Manager — search for FiddlerCore (v5.0+). Do not use Fiddler.dll directly — it’s deprecated and unstable.

💡 Tip: Always reference FiddlerCore instead of copying Fiddler.exe's assembly. This ensures version compatibility and avoids GAC-related load failures during fiddler proxy startup.

Create a new Class Library project:

 dotnet new classlib -n MyCustomInspector -f net6.0
 cd MyCustomInspector
 dotnet add package FiddlerCore --version 5.0.1

Then add a reference to System.Windows.Forms (required for UI inspectors) and set <UseWindowsForms>true</UseWindowsForms> in your .csproj.

Step-by-Step: Building a JWT Inspector

Let’s build a practical example: a JWT Inspector that decodes and validates JSON Web Tokens in Authorization headers or request bodies — invaluable for fiddler tutorial scenarios involving OAuth2 or token-based auth debugging.

1. Implement IInspector2 Interface

All custom inspectors must implement Fiddler.IInspector2. Create JwtInspector.cs:

using Fiddler;
using System.Windows.Forms;

public class JwtInspector : IInspector2
{
    private readonly Control _uiControl = new JwtInspectorUI();

    public string[] SupportedContentTypes => new[] { "application/jwt", "text/plain" };
    public bool HandlesContentType(string sContentType) => 
        sContentType?.Contains("jwt") == true || 
        sContentType?.Contains("bearer") == true;

    public Control Control => _uiControl;
    public void Clear() => (_uiControl as JwtInspectorUI)?.Clear();
    public void AssignSession(Session oSession) => (_uiControl as JwtInspectorUI)?.LoadSession(oSession);
}

Note: SupportedContentTypes hints to Fiddler which sessions this inspector should appear for. HandlesContentType() gives you runtime flexibility — e.g., inspecting raw Base64 strings inside text/plain responses.

2. Build the UI Control

Create JwtInspectorUI.cs, inheriting from UserControl:

using System;
using System.Text;
using System.Windows.Forms;
using Newtonsoft.Json;

public partial class JwtInspectorUI : UserControl
{
    private TextBox _txtHeader, _txtPayload, _txtSignature;
    private Label _lblStatus;

    public JwtInspectorUI()
    {
        InitializeComponent();
        Dock = DockStyle.Fill;
    }

    private void InitializeComponent()
    {
        _txtHeader = new TextBox { ReadOnly = true, Multiline = true, ScrollBars = ScrollBars.Vertical };
        _txtPayload = new TextBox { ReadOnly = true, Multiline = true, ScrollBars = ScrollBars.Vertical };
        _txtSignature = new TextBox { ReadOnly = true, Size = new System.Drawing.Size(300, 20) };
        _lblStatus = new Label { AutoSize = true };

        var tlp = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 2, RowCount = 4 };
        tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
        tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 75F));
        tlp.Controls.Add(new Label { Text = "Header:" }, 0, 0);
        tlp.Controls.Add(_txtHeader, 1, 0);
        tlp.Controls.Add(new Label { Text = "Payload:" }, 0, 1);
        tlp.Controls.Add(_txtPayload, 1, 1);
        tlp.Controls.Add(new Label { Text = "Signature:" }, 0, 2);
        tlp.Controls.Add(_txtSignature, 1, 2);
        tlp.Controls.Add(_lblStatus, 0, 3);
        Controls.Add(tlp);
    }

    public void LoadSession(Session oSession)
    {
        try
        {
            var token = ExtractJwtFromSession(oSession);
            if (!string.IsNullOrEmpty(token))
            {
                var parts = token.Split('.');
                if (parts.Length == 3)
                {
                    var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(parts[0]));
                    var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(parts[1]));

                    _txtHeader.Text = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(headerJson), Formatting.Indented);
                    _txtPayload.Text = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(payloadJson), Formatting.Indented);
                    _txtSignature.Text = parts[2];
                    _lblStatus.Text = "✅ Valid JWT structure";
                    _lblStatus.ForeColor = System.Drawing.Color.Green;
                }
                else
                {
                    throw new ArgumentException("Invalid JWT segment count");
                }
            }
            else
            {
                Clear();
                _lblStatus.Text = "⚠ No JWT found";
                _lblStatus.ForeColor = System.Drawing.Color.Orange;
            }
        }
        catch (Exception ex)
        {
            _lblStatus.Text = $"❌ Parse error: {ex.Message}";
            _lblStatus.ForeColor = System.Drawing.Color.Red;
        }
    }

    private string ExtractJwtFromSession(Session oSession)
    {
        // Check Authorization header first
        if (oSession.oRequest.headers.ExistsAndContains("Authorization", "Bearer "))
        {
            return oSession.oRequest.headers.GetFirstValue("Authorization").Replace("Bearer ", "").Trim();
        }

        // Then check request body (e.g., POST with 'token' field)
        if (!string.IsNullOrEmpty(oSession.RequestBodyAsString))
        {
            var body = oSession.RequestBodyAsString;
            var match = System.Text.RegularExpressions.Regex.Match(body, @"\"token\":\"([A-Za-z0-9_-]{3,}\.){2}[A-Za-z0-9_-]+\"");
            return match.Success ? match.Groups[1].Value : null;
        }

        return null;
    }

    private static byte[] Base64UrlDecode(string input)
    {
        string padded = input.Length % 4 == 0 ? input : input + new string('=', 4 - input.Length % 4);
        return Convert.FromBase64String(padded.Replace('-', '+').Replace('_', '/'));
    }

    public void Clear()
    {
        _txtHeader.Clear();
        _txtPayload.Clear();
        _txtSignature.Clear();
        _lblStatus.Text = "";
    }
}

3. Register the Inspector

Fiddler discovers inspectors via FiddlerApplication.OnInspectorsAvailable. In Program.cs or AssemblyInfo.cs, add:

using Fiddler;

public static class Startup
{
    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    public static void Initialize()
    {
        FiddlerApplication.OnInspectorsAvailable += (oSender, oEvArgs) =>
        {
            oEvArgs.Inspectors.Add(new JwtInspector());
        };
    }
}

Then call Startup.Initialize() early — ideally in your extension’s OnBeforeInitialize handler or via [LoaderOptimization(LoaderOptimization.MultiDomainHost)] on the entry point.

Compiling and Installing the Extension

Build your project:

dotnet build -c Release

Copy the resulting MyCustomInspector.dll to Fiddler’s Inspectors folder:

  • Windows: %USERPROFILE%\Documents\Fiddler2\Inspectors\
  • macOS (Fiddler Everywhere): Not supported — custom inspectors require Fiddler Classic on Windows.

Restart Fiddler. Navigate to any session with a JWT (e.g., GET /api/me with Authorization: Bearer ey...). Click the Inspectors tab — your JwtInspector appears alongside TextView and WebForms.

🔍 Pro tip: Use Fiddler’s QuickExec box (Ctrl+Q) and type prefs set fiddler.ui.inspectors.showall true to force all inspectors to render — helpful during development.

Troubleshooting Common Pitfalls

  • Inspector doesn’t appear? Verify your DLL targets net6.0, not netstandard2.0. Fiddler Classic loads only .NET Framework/.NET Core-compatible assemblies — mismatched runtimes silently fail.
  • UI freezes or throws InvalidOperationException? All UI updates must occur on the UI thread. Wrap _txtHeader.Text = ... in Invoke((MethodInvoker)delegate { ... }); if called outside the main thread.
  • HTTPS decryption breaks after installing inspector? Ensure your extension doesn’t intercept or modify Session objects during AssignSession() — never call oSession.utilDecodeResponse() or mutate headers here. That belongs in BeforeResponse handlers.
  • FiddlerCore version conflicts? Use dotnet list package to confirm no transitive FiddlerCore downgrade. Lock to 5.0.1+ explicitly.

For deeper diagnostics, enable Fiddler’s Log Viewer (Help > Fiddler Options > Enable FiddlerScript Logging) and watch for Failed to load inspector messages.

Extending Further: Beyond JWT

Once you’ve mastered the inspector pattern, consider these advanced extensions:

  • Protobuf Inspector: Parse .proto-annotated binary payloads using Google.Protobuf and reflection-based schema inference.
  • GraphQL Query Visualizer: Syntax-highlight and auto-format GraphQL queries/mutations with error detection for malformed fragments.
  • TLS Handshake Analyzer: Hook into FiddlerApplication.OnNotification to surface ALPN, SNI, and cipher suite details per session — extremely useful for https decryption root-cause analysis.

All follow the same architecture: implement IInspector2, render state safely, and register at startup. Avoid heavy parsing in AssignSession() — cache decoded data and defer expensive ops to background threads with Task.Run().

Conclusion: Turn Fiddler Into Your Domain-Specific Debugger

Custom inspectors are among the most underused yet highest-leverage features in the Fiddler ecosystem. They let you move beyond generic http debugging, turning every session into a contextual, actionable view — whether you're reverse-engineering third-party APIs, validating token hygiene, or auditing legacy SOAP payloads.

Remember: great inspectors are fast, safe, and silent. They never crash Fiddler, never block the UI thread, and gracefully degrade when input is malformed. Start small — extend the JWT inspector to validate exp timestamps or highlight insecure alg:none tokens — then scale to multi-format analyzers.

You now have the foundation to build inspectors that align precisely with your team’s fiddler debugging workflows. For more advanced integrations — like injecting inspectors into CI pipelines via FiddlerCore or combining them with more tutorials on automated certificate pinning bypass — explore our browse Advanced Techniques tutorials. Need help adapting this for your stack? contact us — we ship production-grade inspectors weekly.

Key takeaways:

  • Always use FiddlerCore NuGet, never embed Fiddler.exe assemblies.
  • HandlesContentType() is your dynamic gatekeeper — use it to scope inspection logic.
  • Never mutate Session objects inside inspectors — treat them as read-only views.
  • Test with both HTTP and HTTPS traffic to verify https decryption compatibility.
  • Log errors but never throw unhandled exceptions — they kill inspector availability.
Share:

Related Topics

fiddler tutorialfiddler debugginghttp debuggingfiddler proxyhttps decryption

Get Fiddler Tips & Tutorials

Stay updated with the latest Fiddler tutorials, HTTP debugging guides, request modification tips, and web traffic analysis techniques.

Free forever. New tutorials published daily.

Related Articles