A stealth HTTP client library for .NET that can bypass Cloudflare and most TLS handshake security measures using advanced TLS fingerprinting. Supports multiple browser profiles (Chrome, Firefox, Safari) and includes cookie management, proxy support, and custom headers.
$ dotnet add package PhantomClientA stealth HTTP client library for .NET that can bypass Cloudflare and most TLS handshake security measures by leveraging advanced TLS fingerprinting capabilities.
PhantomClient is a .NET 9.0 library that enables you to make HTTP requests that closely mimic real browsers by replicating their TLS fingerprints, making it ideal for:
dotnet add package PhantomClient
Install-Package PhantomClient
using PhantomClientCore;
// Initialize the TLS service once at application start
await PhantomTLS.InitializeAsync();
// Create a new client instance
using var client = new PhantomClient();
// Make a simple GET request
var response = await client.GetAsync("https://example.com");
Console.WriteLine($"Status: {response.Status}");
Console.WriteLine($"Body: {response.Body}");
// Clean up when done (optional, at program exit)
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
// Initialize once
await PhantomTLS.InitializeAsync();
using var client = new PhantomClient(new PhantomClientOptions
{
ClientIdentifier = "chrome_120",
Timeout = 30000
});
var response = await client.GetAsync("https://api.example.com/data", new RequestOptions
{
Headers = new Dictionary<string, string>
{
["Accept"] = "application/json",
["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0",
["Accept-Language"] = "en-US,en;q=0.9"
}
});
if (response.IsSuccess)
{
Console.WriteLine($"Success: {response.Body}");
}
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
using System.Text.Json;
await PhantomTLS.InitializeAsync();
using var client = new PhantomClient();
var payload = new { username = "testuser", password = "secret123" };
var jsonBody = JsonSerializer.Serialize(payload);
var response = await client.PostAsync("https://api.example.com/login", new PostRequestOptions
{
Body = jsonBody,
Headers = new Dictionary<string, string>
{
["Content-Type"] = "application/json",
["Accept"] = "application/json"
}
});
Console.WriteLine($"Response: {response.Body}");
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
await PhantomTLS.InitializeAsync();
using var client = new PhantomClient();
var formData = new Dictionary<string, string>
{
["username"] = "testuser",
["password"] = "secret123",
["remember"] = "true"
};
var response = await client.PostFormAsync("https://example.com/login", formData);
if (response.IsSuccess)
{
Console.WriteLine("Login successful!");
}
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
await PhantomTLS.InitializeAsync();
using var client = new PhantomClient(new PhantomClientOptions
{
ClientIdentifier = "chrome_120", // Use latest Chrome fingerprint
Timeout = 30000
});
var response = await client.GetAsync("https://cloudflare-protected-site.com", new RequestOptions
{
Headers = new Dictionary<string, string>
{
["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0",
["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
["Accept-Language"] = "en-US,en;q=0.9",
["Accept-Encoding"] = "gzip, deflate, br",
["Sec-Fetch-Dest"] = "document",
["Sec-Fetch-Mode"] = "navigate",
["Sec-Fetch-Site"] = "none"
}
});
if (response.IsSuccess)
{
Console.WriteLine("Successfully bypassed protection!");
// Cookies are automatically stored in the client instance
var cookies = client.Cookies.GetCookies("https://cloudflare-protected-site.com");
foreach (var cookie in cookies)
{
Console.WriteLine($"{cookie.Key}: {cookie.Value}");
}
}
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
await PhantomTLS.InitializeAsync();
using var client = new PhantomClient();
// First request - Login and capture cookies
var loginResponse = await client.PostFormAsync("https://example.com/login",
new Dictionary<string, string>
{
["username"] = "user",
["password"] = "pass"
});
Console.WriteLine($"Login status: {loginResponse.Status}");
// Cookies are automatically stored in the client instance
// and will be sent with subsequent requests
// Second request - Use stored cookies automatically
var profileResponse = await client.GetAsync("https://example.com/profile");
Console.WriteLine($"Profile data: {profileResponse.Body}");
// Get all cookies for a domain
var cookies = client.Cookies.GetCookies("https://example.com");
foreach (var cookie in cookies)
{
Console.WriteLine($"{cookie.Key}: {cookie.Value}");
}
// Clear cookies for a specific domain
client.Cookies.ClearCookies("https://example.com");
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
await PhantomTLS.InitializeAsync();
// Configure client with proxy
using var client = new PhantomClient(new PhantomClientOptions
{
ClientIdentifier = "chrome_120",
Proxy = "http://username:password@proxy.example.com:8080",
Timeout = 30000
});
var response = await client.GetAsync("https://api.ipify.org?format=json");
Console.WriteLine($"Response via proxy: {response.Body}");
// You can also override proxy per request
var response2 = await client.GetAsync("https://example.com", new RequestOptions
{
Proxy = "http://different-proxy.com:8080"
});
await PhantomTLS.DestroyAsync();
Each PhantomClient instance maintains its own cookies and session:
using PhantomClientCore;
await PhantomTLS.InitializeAsync();
// Create two separate clients with different configurations
using var client1 = new PhantomClient(new PhantomClientOptions
{
ClientIdentifier = "chrome_103"
});
using var client2 = new PhantomClient(new PhantomClientOptions
{
ClientIdentifier = "firefox_120"
});
// Set cookies in client1
await client1.GetAsync("https://httpbin.org/cookies/set?session=abc123");
// client2 has its own isolated cookie container
var client1Cookies = client1.Cookies.GetCookies("https://httpbin.org");
var client2Cookies = client2.Cookies.GetCookies("https://httpbin.org");
Console.WriteLine($"Client1 cookies: {client1Cookies.Count}"); // Has cookies
Console.WriteLine($"Client2 cookies: {client2Cookies.Count}"); // Empty (isolated)
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
await PhantomTLS.InitializeAsync();
using var client = new PhantomClient();
// GET request
var getResponse = await client.GetAsync("https://httpbin.org/get");
// POST request
var postResponse = await client.PostAsync("https://httpbin.org/post",
new PostRequestOptions { Body = "{\"data\":\"value\"}" });
// PUT request
var putResponse = await client.PutAsync("https://httpbin.org/put",
new PostRequestOptions { Body = "{\"update\":\"value\"}" });
// DELETE request
var deleteResponse = await client.DeleteAsync("https://httpbin.org/delete");
// PATCH request
var patchResponse = await client.PatchAsync("https://httpbin.org/patch",
new PostRequestOptions { Body = "{\"field\":\"value\"}" });
// HEAD request (no body in response)
var headResponse = await client.HeadAsync("https://httpbin.org/get");
// OPTIONS request
var optionsResponse = await client.OptionsAsync("https://httpbin.org/get");
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
await PhantomTLS.InitializeAsync();
using var client = new PhantomClient();
// Follow redirects (default behavior)
var response1 = await client.GetAsync("https://httpbin.org/redirect/2");
Console.WriteLine($"Final URL: {response1.Url}"); // Shows final URL after redirects
// Don't follow redirects
var response2 = await client.GetAsync("https://httpbin.org/redirect/1", new RequestOptions
{
FollowRedirect = false
});
Console.WriteLine($"Status: {response2.Status}"); // Will be 302 or similar
await PhantomTLS.DestroyAsync();
using PhantomClientCore;
await PhantomTLS.InitializeAsync();
using var client = new PhantomClient(new PhantomClientOptions
{
ClientIdentifier = "chrome_120",
Timeout = 15000
});
async Task<TlsResponse?> MakeRequestWithRetry(string url, int maxRetries = 3)
{
for (int i = 0; i < maxRetries; i++)
{
try
{
var response = await client.GetAsync(url);
if (response.IsSuccess)
return response;
Console.WriteLine($"Attempt {i + 1} failed with status: {response.Status}");
}
catch (Exception ex)
{
Console.WriteLine($"Attempt {i + 1} error: {ex.Message}");
}
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, i))); // Exponential backoff
}
return null;
}
var result = await MakeRequestWithRetry("https://example.com");
if (result != null)
{
Console.WriteLine("Success!");
}
await PhantomTLS.DestroyAsync();
static Task InitializeAsync()
static Task DestroyAsync()
public PhantomClient(PhantomClientOptions? options = null)
Creates a new client instance. Each instance maintains its own session and cookies.
CookieContainer Cookies { get; }
Task<TlsResponse> GetAsync(string url, RequestOptions? options = null)
Task<TlsResponse> PostAsync(string url, PostRequestOptions? options = null)
Task<TlsResponse> PostFormAsync(string url, Dictionary<string, string> formData, RequestOptions? options = null)
Task<TlsResponse> PutAsync(string url, PostRequestOptions? options = null)
Task<TlsResponse> PatchAsync(string url, PostRequestOptions? options = null)
Task<TlsResponse> DeleteAsync(string url, RequestOptions? options = null)
Task<TlsResponse> HeadAsync(string url, RequestOptions? options = null)
Task<TlsResponse> OptionsAsync(string url, RequestOptions? options = null)
public class PhantomClientOptions
{
public string ClientIdentifier { get; set; } = "chrome_103";
public int Timeout { get; set; } = 30000;
public string? Proxy { get; set; }
public Dictionary<string, string>? DefaultHeaders { get; set; }
public bool InsecureSkipVerify { get; set; } = false;
}
public class RequestOptions
{
public Dictionary<string, string>? Headers { get; set; }
public Dictionary<string, string>? Cookies { get; set; }
public bool FollowRedirect { get; set; } = true;
public string? Proxy { get; set; }
}
public class PostRequestOptions : RequestOptions
{
public string? Body { get; set; }
public Dictionary<string, string>? FormData { get; set; }
}
public class TlsResponse
{
public int Status { get; set; }
public string StatusText { get; set; }
public Dictionary<string, string> Headers { get; set; }
public string Body { get; set; }
public Dictionary<string, string> Cookies { get; set; }
public string Url { get; set; }
public bool IsSuccess { get; } // true if status 200-299
}
Dictionary<string, string> GetCookies(string url)
void ClearCookies(string url)
PhantomClient supports multiple browser fingerprints via the ClientIdentifier property:
chrome_103, chrome_104, chrome_105, chrome_106, chrome_107chrome_108, chrome_109, chrome_110, chrome_111, chrome_112chrome_116_PSK, chrome_116_PSK_PQ, chrome_117, chrome_120firefox_102, firefox_104, firefox_105, firefox_106firefox_108, firefox_110, firefox_117, firefox_120safari_15_6_1, safari_16_0safari_ipad_15_6, safari_ios_15_5, safari_ios_15_6, safari_ios_16_0opera_89, opera_90zalando_android_mobile, nike_ios_mobile, mesh_ios, mesh_androidPhantomTLS.InitializeAsync() once at application startPhantomClient instance and reuse it for multiple requestsusing statements or call Dispose() when donenode --versionTimeout in PhantomClientOptionsPhantomClient instance has its own cookie containerclient.Cookies.GetCookies(url) to inspect stored cookiesMIT License - Copyright © 2025 Riadh Chebbi. All rights reserved.
Contributions are welcome! Please feel free to submit issues or pull requests.
This library is intended for legitimate purposes such as testing, research, and automation of your own services. Always respect websites' Terms of Service and robots.txt files. The authors are not responsible for misuse of this library.