Content Security Policy (CSP) is a web security standard that prevents cross-site scripting (XSS) and data injection attacks by restricting which resources a webpage can load. When using Puppeteer-Sharp for web scraping or automation, CSP policies can interfere with script injection and resource loading.
Understanding CSP Challenges
CSP headers can block: - Inline scripts and styles - External resource loading - Dynamic code execution - JavaScript injection
These restrictions can prevent Puppeteer-Sharp from executing custom scripts or loading certain resources during automation.
Methods to Handle CSP
1. Bypassing CSP (Recommended for Testing)
The most straightforward approach is to disable CSP entirely using SetBypassCSPAsync()
:
using PuppeteerSharp;
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
// Download Chromium if not already available
await new BrowserFetcher().DownloadAsync();
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[] { "--no-sandbox", "--disable-setuid-sandbox" }
});
var page = await browser.NewPageAsync();
// Bypass CSP before navigation
await page.SetBypassCSPAsync(true);
await page.GoToAsync("https://example.com");
// Now you can inject scripts without CSP restrictions
var result = await page.EvaluateExpressionAsync<string>(
"document.title"
);
Console.WriteLine($"Page title: {result}");
await browser.CloseAsync();
}
}
2. Monitoring CSP Headers
To understand CSP policies without bypassing them, monitor response headers:
using PuppeteerSharp;
using System;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var browser = await Puppeteer.LaunchAsync();
var page = await browser.NewPageAsync();
// Listen for response events
page.Response += (sender, e) =>
{
var response = e.Response;
// Check for CSP headers
var cspHeader = response.Headers
.FirstOrDefault(h => h.Key.Equals("Content-Security-Policy",
StringComparison.OrdinalIgnoreCase));
var cspReportHeader = response.Headers
.FirstOrDefault(h => h.Key.Equals("Content-Security-Policy-Report-Only",
StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrEmpty(cspHeader.Value))
{
Console.WriteLine($"CSP Policy for {response.Url}:");
Console.WriteLine($" {cspHeader.Value}");
}
if (!string.IsNullOrEmpty(cspReportHeader.Value))
{
Console.WriteLine($"CSP Report-Only for {response.Url}:");
Console.WriteLine($" {cspReportHeader.Value}");
}
};
await page.GoToAsync("https://example.com");
await browser.CloseAsync();
}
}
3. Working with CSP-Protected Content
When you need to respect CSP while still performing automation:
using PuppeteerSharp;
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var browser = await Puppeteer.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GoToAsync("https://example.com");
try
{
// Try to execute script - may fail due to CSP
var result = await page.EvaluateExpressionAsync<string>(
"document.querySelector('h1').textContent"
);
Console.WriteLine($"Script executed: {result}");
}
catch (Exception ex)
{
Console.WriteLine($"Script blocked by CSP: {ex.Message}");
// Fallback: Use DOM methods that don't violate CSP
var title = await page.GetTitleAsync();
var content = await page.GetContentAsync();
Console.WriteLine($"Page title: {title}");
Console.WriteLine($"Content length: {content.Length}");
}
await browser.CloseAsync();
}
}
4. Using Request Interception
For advanced scenarios, intercept and modify requests before they reach CSP enforcement:
using PuppeteerSharp;
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var browser = await Puppeteer.LaunchAsync();
var page = await browser.NewPageAsync();
// Enable request interception
await page.SetRequestInterceptionAsync(true);
page.Request += async (sender, e) =>
{
var request = e.Request;
// Modify headers to remove CSP-related headers
var headers = request.Headers.ToDictionary(h => h.Key, h => h.Value);
headers.Remove("Content-Security-Policy");
headers.Remove("Content-Security-Policy-Report-Only");
await request.ContinueAsync(new Payload
{
Headers = headers
});
};
await page.GoToAsync("https://example.com");
await browser.CloseAsync();
}
}
Alternative Approaches
Using Chrome Launch Arguments
Disable CSP at the browser level:
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
Args = new[]
{
"--disable-web-security",
"--disable-features=VizDisplayCompositor",
"--disable-ipc-flooding-protection"
}
});
Proxy-Based Header Modification
For production scenarios where you need granular control:
// Using a proxy to strip CSP headers
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Args = new[] { "--proxy-server=http://localhost:8080" }
});
Best Practices
- Use CSP bypass only for testing: Don't bypass CSP in production scraping
- Respect robots.txt and terms of service: CSP bypass doesn't override legal restrictions
- Monitor for CSP violations: Log when scripts are blocked to understand site behavior
- Implement fallback strategies: Have non-script alternatives when CSP blocks execution
- Consider rate limiting: CSP-protected sites may have additional bot detection
Common CSP Directives That Affect Puppeteer-Sharp
script-src 'self'
: Blocks external script injectionscript-src 'unsafe-inline'
: Required for inline script executionscript-src 'unsafe-eval'
: Needed for dynamic code evaluationconnect-src
: Restricts network requests from scripts
Understanding these directives helps you predict when CSP will interfere with your automation scripts and plan accordingly.