Library (embed)
For .NET library users — operators who want a reverse proxy or desktop debugger should start with the CLI or Inspector instead.
NuGet package Titanium.Web.Proxy (MIT). Target framework: .NET 10.
dotnet add package Titanium.Web.Proxy
# Prerelease when newer than the stable NuGet feed:
dotnet add package Titanium.Web.Proxy --prereleaseRelated MIT packages (also on NuGet): Titanium.Web.Proxy.Abstractions (shared contracts) and Titanium.Web.Proxy.Configuration (twp.yaml / dialect loaders — optional for library users). See migration 6→7.
Explicit man-in-the-middle (MITM) proxy
using System.Net;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.Models;
using var proxyServer = new ProxyServer();
proxyServer.BeforeRequest += async (sender, e) =>
{
// Inspect or rewrite e.HttpClient.Request
};
var endPoint = new ExplicitProxyEndPoint(IPAddress.Loopback, 8000, decryptSsl: true);
proxyServer.AddEndPoint(endPoint);
proxyServer.CertificateManager.EnsureRootCertificate(
userTrustRootCertificate: true,
machineTrustRootCertificate: false);
proxyServer.Start();Capabilities
- Intercept, modify, redirect, or block HTTP and HTTPS
- Explicit, transparent, and SOCKS4/5 endpoints
- HTTP/2 on by default (
ProxyServer.EnableHttp2); HTTP/3 opt-in (EnableHttp3, experimentalTWP001) - Upstream HTTP/HTTPS/SOCKS + system proxy detection
- Proxy auth, mTLS, Kerberos, NTLM
- Connection, certificate, and buffer pooling
- Built-in async logging and opt-in request timing.
Logging.AdditionalLoggerProvidersadds host sinks after the built-in ones; an empty list changes nothing, the proxy does not dispose those providers, and the default level stays Error.
Reverse proxy
Use ForwardHost on a transparent endpoint for a zero-cost terminate-lite path, or declarative routes/clusters (also used by the CLI). See Configuration and Protocol support.
API reference
- ProxyServer
- Full API tree under /api/
Examples in the repo
examples/Titanium.Web.Proxy.Examples.Basicexamples/Titanium.Web.Proxy.Examples.Wpfexamples/Titanium.Web.Proxy.Examples.WindowsService
System proxy and root CA (Windows / macOS / Linux)
ProxyServer.SetAsSystemProxy / RestoreOriginalProxySettings work on Windows (WinINET), macOS (networksetup), and Linux (GNOME + KDE + process environment). Unsupported platforms throw NotSupportedException.
CertificateManager.TrustRootCertificate installs into the current-user store on all platforms and additionally trusts for SSL on macOS (login keychain) and Linux (user NSS db). Check LastOsTrustResult for structured outcomes (e.g. missing certutil, Keychain Always Trust needed). InstallNssCertutilAndRetryUserTrust installs NSS tools via package manager / Homebrew after user consent. FirefoxCertificateTrust enables Windows ImportEnterpriseRoots or profile user.js (security.enterprise_roots.enabled) so Firefox trusts OS roots, and can import into the default Firefox NSS profile. TrustRootCertificateAsAdmin shows an OS admin prompt (UAC / macOS authentication / polkit) for machine-wide trust.
MITM hostname exclusions
Two layers (both work with system proxy on Windows, macOS, and Linux):
- OS bypass —
SystemProxySettings.BypassRules+SetAsSystemProxy(..., settings). Traffic never hits the proxy when System proxy is on. Factory seed:MitmExclusionDefaults.SystemProxyBypassRules(Microsoft identity / SSO / RDP). - Tunnel only —
BeforeTunnelConnectRequest→e.DecryptSsl = false, orMitmExclusionDefaults.ApplyDecryptExclusions(...). CONNECT stays visible; no decrypt. Factory seed:MitmExclusionDefaults.TunnelOnlyPinningDomains(dropbox.com,webex.com).
Use MitmExclusionMode.Merge (default, back-compat) to always re-inject factory hosts, or Replace so the caller lists are authoritative (you can remove identity hosts — risk breaking SSO while System proxy is on).
// Merge: factory identity hosts + extras
var settings = MitmExclusionDefaults.CreateSystemProxySettings(
proxyLoopback: true,
additionalBypassRules: ["*.corp.example.com"]);
proxyServer.SetAsSystemProxy(endPoint, ProxyProtocolType.AllHttp, settings);
// Replace: full list you provide (factory not re-added)
var custom = MitmExclusionDefaults.CreateSystemProxySettings(
proxyLoopback: true,
MitmExclusionDefaults.SystemProxyBypassRules.Append("*.corp.example.com"),
MitmExclusionMode.Replace);
MitmExclusionDefaults.ApplyDecryptExclusions(endPoint, () => true,
decryptSkipHosts: ["*.bank.example.com"],
decryptOnlyHosts: null,
MitmExclusionMode.Replace);CLI: when server.decryptSkipHosts and/or server.decryptOnlyHosts are present in twp.yaml, exclusions apply with Replace. Omit both to leave Merge defaults. Optional server.systemProxyBypassHosts / server.proxyLoopback build OS-bypass settings the same way (present ⇒ Replace; omit ⇒ Merge). Removing identity hosts from OS bypass can break Microsoft SSO while System proxy is enabled.
Decrypt failure bypass (learned tunnel)
Optional heuristic for hosts that reject the proxy’s TLS fingerprint under MITM (bot / WAF style). Default off on ProxyServer (library and RPS baselines unchanged). Titanium Inspector enables it by default.
proxyServer.EnableDecryptFailureBypass = true;
proxyServer.DecryptFailureBypassThreshold = 2; // strikes before later CONNECTs skip decrypt
proxyServer.DecryptFailureBypassTtl = TimeSpan.FromMinutes(30);
proxyServer.DecryptFailureBypassMaxEntries = 256; // approximate LRU
// Snapshot / manage
foreach (var e in proxyServer.GetDecryptFailureBypassEntries())
Console.WriteLine($"{e.Host} active={e.BypassActive}");
proxyServer.RemoveDecryptFailureBypass("www.example.com");
proxyServer.ClearDecryptFailureBypass();Behavior:
- Learns from origin TLS
AuthenticationExceptionfailures (not ALPN-only “no h2”, not TCP/DNS). - Also learns from MITM HTTPS responses with status 403 or 429 (bot/WAF after TLS succeeds), excluding synthetic
Respond/Ok/GenericResponse.- Document navigations (
Sec-Fetch-Dest: documentor Accept preferstext/html): activate bypass immediately and answer with a one-shot meta-refresh (200) plus connection close so the browser opens a new CONNECT over an opaque tunnel—seamless recovery without a manual reload. - Non-document (XHR/fetch): shared strike threshold (default 2); no body rewrite.
- Document navigations (
- Prefetch (MITM origin sockets) is not started for learned hosts. The current MITM CONNECT is never converted in place.
- If a cold awaited HTTP/2 capability probe fails with a learnable error, the same CONNECT can fall back to opaque relay before browser MITM (ClientHello was only peeked); session prefetch is not started on that failure path.
- Does not replace
MitmExclusionDefaultspinning/SSO lists. Does not auto-learn browser-leg pinning aborts. - Not a
twp.yamlkey in v1 — set onProxyServer(or use Inspector). UseShouldBypassDecryptForLearnedHostfor O(1) consults (avoid snapshotting the full list on hot paths). - Automation/CDP browsers may still see Akamai-style captchas after a successful tunnel (site bot score), which is outside MITM recovery.