PhantomClient 1.0.9
dotnet add package PhantomClient --version 1.0.9
NuGet\Install-Package PhantomClient -Version 1.0.9
<PackageReference Include="PhantomClient" Version="1.0.9" />
<PackageVersion Include="PhantomClient" Version="1.0.9" />
<PackageReference Include="PhantomClient" />
paket add PhantomClient --version 1.0.9
#r "nuget: PhantomClient, 1.0.9"
#:package PhantomClient@1.0.9
#addin nuget:?package=PhantomClient&version=1.0.9
#tool nuget:?package=PhantomClient&version=1.0.9
PhantomClient
A stealth HTTP client library for .NET that can bypass Cloudflare and most TLS handshake security measures by leveraging advanced TLS fingerprinting capabilities.
What is PhantomClient?
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:
- Web scraping protected sites
- API testing with anti-bot protection
- Automated testing of sites with Cloudflare or similar protection
- Bypassing TLS fingerprinting detection
Features
- ✅ Multiple browser fingerprint profiles (Chrome, Firefox, Safari, etc.)
- ✅ Support for all HTTP methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
- ✅ Automatic cookie management and persistence per client instance
- ✅ Proxy support (HTTP, SOCKS5)
- ✅ Custom timeout configuration
- ✅ Header order preservation
- ✅ Form data and JSON body support
- ✅ Easy-to-use async/await API
- ✅ Built on .NET 9.0 with modern C# practices
Installation
NuGet Package Manager
dotnet add package PhantomClient
Package Manager Console
Install-Package PhantomClient
Prerequisites
- .NET 9.0 SDK or later
- Node.js (v14 or later) installed on the system
Quick Start
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();
Usage Scenarios
Scenario 1: Simple GET Request with Custom Headers
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();
Scenario 2: POST Request with JSON Data
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();
Scenario 3: POST Request with Form Data
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();
Scenario 4: Bypassing Cloudflare Protection
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();
Scenario 5: Session Management with Cookies
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();
Scenario 6: Using Proxy
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();
Scenario 7: Multiple Isolated Client Instances
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();
Scenario 8: All HTTP Methods
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();
Scenario 9: Redirect Handling
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();
Scenario 10: Error Handling and Retries
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();
API Reference
PhantomTLS Class (Static)
Methods
static Task InitializeAsync()- Initializes the TlsService. Must be called before using the library.
static Task DestroyAsync()- Cleans up the TlsService service. Optional, typically called at program exit.
PhantomClient Class
Constructor
public PhantomClient(PhantomClientOptions? options = null)
Creates a new client instance. Each instance maintains its own session and cookies.
Properties
CookieContainer Cookies { get; }- Cookie container for automatic cookie management
Methods
Task<TlsResponse> GetAsync(string url, RequestOptions? options = null)- Makes an HTTP GET request
Task<TlsResponse> PostAsync(string url, PostRequestOptions? options = null)- Makes an HTTP POST request with JSON or raw body
Task<TlsResponse> PostFormAsync(string url, Dictionary<string, string> formData, RequestOptions? options = null)- Makes an HTTP POST request with form data (application/x-www-form-urlencoded)
Task<TlsResponse> PutAsync(string url, PostRequestOptions? options = null)- Makes an HTTP PUT request
Task<TlsResponse> PatchAsync(string url, PostRequestOptions? options = null)- Makes an HTTP PATCH request
Task<TlsResponse> DeleteAsync(string url, RequestOptions? options = null)- Makes an HTTP DELETE request
Task<TlsResponse> HeadAsync(string url, RequestOptions? options = null)- Makes an HTTP HEAD request
Task<TlsResponse> OptionsAsync(string url, RequestOptions? options = null)- Makes an HTTP OPTIONS request
PhantomClientOptions Class
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;
}
RequestOptions Class
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; }
}
PostRequestOptions Class
public class PostRequestOptions : RequestOptions
{
public string? Body { get; set; }
public Dictionary<string, string>? FormData { get; set; }
}
TlsResponse Class
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
}
CookieContainer Class
Methods
Dictionary<string, string> GetCookies(string url)- Retrieves cookies for a specific URL/domain
void ClearCookies(string url)- Clears cookies for a specific URL/domain
Available Browser Fingerprints
PhantomClient supports multiple browser fingerprints via the ClientIdentifier property:
Chrome
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_120
Firefox
firefox_102,firefox_104,firefox_105,firefox_106firefox_108,firefox_110,firefox_117,firefox_120
Safari
safari_15_6_1,safari_16_0safari_ipad_15_6,safari_ios_15_5,safari_ios_15_6,safari_ios_16_0
Other
opera_89,opera_90zalando_android_mobile,nike_ios_mobile,mesh_ios,mesh_android
Best Practices
- Initialize once: Call
PhantomTLS.InitializeAsync()once at application start - Reuse client instances: Create one
PhantomClientinstance and reuse it for multiple requests - Dispose properly: Always use
usingstatements or callDispose()when done - Match headers to fingerprint: Use realistic headers that match your chosen browser fingerprint
- Respect rate limits: Add delays between requests to avoid detection
- Handle cookies: Each client instance automatically manages cookies - use separate instances for isolated sessions
Troubleshooting
"Node.js not found" error
- Ensure Node.js is installed and available in your system PATH
- Verify installation:
node --version
Timeout errors
- Increase
TimeoutinPhantomClientOptions - Check your network connection and proxy settings
Cookie issues
- Remember that each
PhantomClientinstance has its own cookie container - Use
client.Cookies.GetCookies(url)to inspect stored cookies
License
MIT License - Copyright © 2025 Riadh Chebbi. All rights reserved.
Contributing
Contributions are welcome! Please feel free to submit issues or pull requests.
Disclaimer
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.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Jering.Javascript.NodeJS (>= 7.0.0)
- Microsoft.Extensions.DependencyInjection (>= 9.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.