-
Notifications
You must be signed in to change notification settings - Fork 655
Home
A lightweight, asynchronous HTTP(S) proxy server for .NET. This wiki documents the major features and the most common APIs. For the full type reference, see the API documentation.
- Getting started
- Endpoints
- Decrypting HTTPS
- Intercepting requests and responses
- Modifying bodies
- Custom and redirected responses
- Streaming bodies
- HTTP/2
- Tunnel (CONNECT) interception
- Upstream proxies
- Authentication
- Performance and pooling
- Supported frameworks
Install from NuGet:
dotnet add package Titanium.Web.ProxyStart an explicit proxy that logs every requested URL:
using System;
using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
using var proxyServer = new ProxyServer();
proxyServer.BeforeRequest += OnRequest;
var endPoint = new ExplicitProxyEndPoint(IPAddress.Loopback, 8000, decryptSsl: true);
proxyServer.AddEndPoint(endPoint);
// Create and trust the root certificate used to decrypt HTTPS traffic.
proxyServer.CertificateManager.EnsureRootCertificate(
userTrustRootCertificate: true,
machineTrustRootCertificate: false);
proxyServer.Start();
Console.WriteLine("Proxy listening on 127.0.0.1:8000. Press Enter to stop.");
Console.ReadLine();
proxyServer.Stop();
static Task OnRequest(object sender, SessionEventArgs e)
{
Console.WriteLine(e.HttpClient.Request.Url);
return Task.CompletedTask;
}Configure your client to use 127.0.0.1:8000 as its HTTP and HTTPS proxy.
Add one or more endpoints before calling Start():
-
ExplicitProxyEndPoint— the client is configured to use the proxy (standardHTTP_PROXY/ system proxy setup). SupportsCONNECTtunneling. -
TransparentProxyEndPoint— traffic is redirected to the proxy without the client knowing (e.g. via routing/NAT). SetGenericCertificateNamefor the server name to present. -
SocksProxyEndPoint— SOCKS4/SOCKS5 endpoint.
proxyServer.AddEndPoint(new ExplicitProxyEndPoint(IPAddress.Loopback, 8000));
proxyServer.AddEndPoint(new TransparentProxyEndPoint(IPAddress.Loopback, 8001, decryptSsl: true)
{
GenericCertificateName = "example.com"
});
proxyServer.AddEndPoint(new SocksProxyEndPoint(IPAddress.Loopback, 1080));To inspect HTTPS traffic the proxy generates per-host certificates signed by its own root certificate, which the client must trust.
// Generate (if needed) and trust the root certificate for the current user.
proxyServer.CertificateManager.EnsureRootCertificate(
userTrustRootCertificate: true,
machineTrustRootCertificate: false);Useful CertificateManager members:
-
RootCertificate/RootCertificateName/PfxFilePath— the CA used for signing. -
CreateRootCertificate(...),TrustRootCertificate(...),RemoveTrustedRootCertificate(...). -
SaveFakeCertificates— cache generated leaf certificates on disk. -
CertificateEngine—BouncyCastleor the built-in engine.
Only decrypt endpoints where you need to see content; leave decryptSsl: false to pass HTTPS through as an opaque tunnel.
Subscribe to the proxy lifecycle events. All handlers are async.
proxyServer.BeforeRequest += OnRequest; // before the request is sent upstream
proxyServer.BeforeResponse += OnResponse; // after response headers are received
proxyServer.AfterResponse += OnAfterResponse;SessionEventArgs exposes HttpClient.Request and HttpClient.Response, headers, the URL, client/process info, and per-session UserData.
Task OnRequest(object sender, SessionEventArgs e)
{
var request = e.HttpClient.Request;
request.Headers.AddHeader("X-Proxy", "titanium");
return Task.CompletedTask;
}Read and replace the whole body (buffers it in memory):
async Task OnResponse(object sender, SessionEventArgs e)
{
if (e.HttpClient.Response.ContentType?.Contains("text/html") == true)
{
var body = await e.GetResponseBodyAsString();
e.SetResponseBodyString(body.Replace("http://", "https://"));
}
}For large or unbounded bodies, prefer the streaming APIs below instead of GetResponseBody().
Answer the client directly, without contacting the server:
proxyServer.BeforeRequest += (sender, e) =>
{
if (e.HttpClient.Request.Url.Contains("blocked.example"))
e.Ok("<html><body>Blocked</body></html>");
return Task.CompletedTask;
};-
e.Ok(html)/e.Ok(bytes)— send a200response. -
e.Respond(response)— send an arbitraryResponse. -
e.Redirect(url)— send a redirect. -
e.TerminateServerConnection()— close the upstream connection instead of reusing it.
When you supply a response after the server was already contacted, the original server body is drained so the connection can be reused; see Draining bodies.
Inspect or modify bodies chunk-by-chunk, or generate a response body from scratch, without buffering it in memory — ideal for large downloads or endless streams (e.g. server-sent events).
proxyServer.OnResponseBodyWrite += (sender, e) =>
{
e.BodyBytes = Transform(e.BodyBytes); // modify each chunk as it streams
return Task.CompletedTask;
};See the dedicated Streaming Bodies page for OnRequestBodyWrite/OnResponseBodyWrite, RespondStreaming, draining, and the HTTP/1.x vs HTTP/2 details.
Enable HTTP/2 support (frames are relayed for decrypted h2 connections):
proxyServer.EnableHttp2 = true;The body-streaming and synthetic-streaming APIs work over HTTP/2 as well as HTTP/1.x — see Streaming Bodies.
On an ExplicitProxyEndPoint, decide per-CONNECT whether to decrypt:
explicitEndPoint.BeforeTunnelConnectRequest += (sender, e) =>
{
var host = e.HttpClient.Request.RequestUri.Host;
if (host.EndsWith("bank.example"))
e.DecryptSsl = false; // pass through without decrypting
return Task.CompletedTask;
};
explicitEndPoint.BeforeTunnelConnectResponse += (sender, e) => Task.CompletedTask;Chain through another proxy, globally or per request:
proxyServer.UpStreamHttpProxy = new ExternalProxy("upstream.example", 8888);
proxyServer.UpStreamHttpsProxy = new ExternalProxy("upstream.example", 8888);
// Or resolve the upstream proxy dynamically:
proxyServer.GetCustomUpStreamProxyFunc = async args =>
{
return new ExternalProxy("upstream.example", 8888);
};
// Detect and reuse the system's configured proxy:
proxyServer.ForwardToUpstreamGateway = true;ExternalProxy supports HTTP, HTTPS, and SOCKS4/5, with optional credentials.
-
Proxy authentication (Basic):
proxyServer.ProxyBasicAuthenticateFunc = async (args, userName, password) => userName == "user" && password == "secret";
-
Windows authentication (Kerberos/NTLM) to upstream servers:
proxyServer.EnableWinAuth = true;
-
Mutual TLS: provide the client certificate via
ClientCertificateSelectionCallback, and validate server certificates withServerCertificateValidationCallback.
-
EnableConnectionPool— reuse idle upstream TCP connections (enabled by default). Only connections that are safe to reuse under HTTP (persistent, body fully received, not authenticated to a specific identity) are pooled; set tofalseto force a fresh connection per client. -
ConnectionTimeOutSeconds,TcpTimeWaitSeconds,ReuseSocket— tune connection lifetime. -
BufferPool/BufferSize— reuse I/O buffers. -
CertificateManager.SaveFakeCertificates— cache generated certificates.
- .NET Framework 4.6.2
- .NET 8
- .NET 10