How to Load CSS from a URL in C#
Load remote CSS in C# by fetching its text with HttpClient, or let a browser apply it with a stylesheet link. Choose the right method for ASP.NET, Blazor, or Playwright.

To make a web page use CSS hosted at a URL, put a <link rel="stylesheet" href="..."> element in the page’s <head>. The browser downloads and applies the stylesheet. To read the CSS response body in C# for processing, use HttpClient, for example await httpClient.GetStringAsync(url). These solve different problems: downloading CSS in C# does not style a browser document.
This distinction determines the right implementation for Razor Pages, Blazor, standalone .NET code, and browser automation. If your end goal is a screenshot of a styled URL, you also need a browser renderer; the ScreenshotNeo option near the end handles that in one request.
1. Choose what “load CSS” means
| Goal | Use | Result |
|---|---|---|
| Style a page served by your app | An HTML stylesheet link in the rendered document | The browser fetches and applies the CSS |
| Read or process remote CSS in .NET | HttpClient.GetStringAsync |
A C# string containing the response body |
| Check HTTP status or headers | HttpClient.GetAsync |
An inspectable HttpResponseMessage |
| Inject CSS in browser automation | Playwright .NET Page.AddStyleTagAsync |
A style or link element added to the page frame |
Use the first route for normal website styling. Use the second or third when the application needs the CSS data itself. Use the fourth for a test or automation task. They operate at different layers and are not interchangeable.

2. Add a remote stylesheet to an ASP.NET page
Render the link in the document head. For a Razor view or page, ordinary HTML markup is sufficient:
<head>
<link rel="stylesheet" href="https://example.com/site.css" />
</head>
The browser makes the stylesheet request when it loads the document. C# is responsible for producing the markup; the browser applies the rules. Inspect the rendered HTML to confirm that the expected URL is present.
Keep the link in the supported markup or asset pipeline. In particular, Razor CSS isolation uses build-time bundling; importing CSS inside Razor code blocks is not a substitute for the supported stylesheet reference pattern.
When to use a relative URL
If the stylesheet is part of your application, a local or app-relative asset path is often easier to deploy consistently than a third-party URL. Use an absolute URL when the CSS is intentionally hosted elsewhere and that host permits the browser to retrieve it. The browser must be able to reach the chosen URL from the user’s network.
3. Fetch CSS content as a string with HttpClient
For a small standalone example, create a client and await the request:
using System;
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var client = new HttpClient();
string css = await client.GetStringAsync("https://example.com/site.css");
Console.WriteLine(css);
}
}
GetStringAsync performs an asynchronous GET and returns the response body as a string. Network and request failures, as well as unsuccessful HTTP responses, can throw. This is convenient when the body is all you need and an exception is an acceptable failure path.
Inspect status and headers
Use GetAsync if you need to distinguish a 404 from a server error, inspect content metadata, or handle a response before consuming its body:
using System.Net.Http;
using System.Threading.Tasks;
static async Task<string> DownloadCssAsync(HttpClient client, string url)
{
using HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
EnsureSuccessStatusCode turns a non-success status into an exception. If you want custom behavior, check response.StatusCode first and return or log an appropriate result instead. Dispose the response after reading it, as the example does.
Use the application’s configured client
In a long-running ASP.NET application, use the app’s normal HttpClient configuration and lifetime management rather than constructing a new client for every request in a tight loop. A simple console program can own one client for its operation; a web application should follow its established dependency-injection and client configuration. This also gives the application one place to configure request behavior and policies.
4. Blazor stylesheet paths depend on the project
There is no single stylesheet path that fits every Blazor app. The right static asset convention depends on whether the project is a Blazor Web App, standalone WebAssembly, or Blazor Server, and on the .NET version. Follow the matching Microsoft static-file guidance for the project.
For Blazor Web Apps on .NET 9 or later, a documented asset reference can look like @Assets["app.css"]. Other project and framework combinations use different conventions. Put head content in the appropriate document head or use the framework’s head-content mechanism. Before debugging CSS rules, verify the deployed URL and asset convention for the actual hosting model.
5. Use @import only for stylesheet composition
A stylesheet can import another stylesheet with an @import rule. This is useful when composing CSS files, but it is not the clearest default for linking a page to one external stylesheet. Place imports before ordinary style rules, subject to the CSS specification’s exceptions. For a page-level reference, prefer the HTML <link> element.
6. Add a stylesheet with Playwright for .NET
When a browser automation scenario needs to modify a page after navigation, Playwright .NET provides Page.AddStyleTagAsync. It accepts a URL or CSS content and adds the corresponding link or style tag to the page frame. A minimal shape is:
using Microsoft.Playwright;
// Assume page is an initialized IPage and is already on the target document.
await page.AddStyleTagAsync(new PageAddStyleTagOptions
{
Url = "https://example.com/site.css"
});
This is useful for visual tests or controlled browser automation. It does not change how the production app serves styles. Confirm the API signature against the Playwright .NET package version used by your project.
7. Troubleshoot a stylesheet that does not load or apply
| Symptom | Likely cause | What to check or change |
|---|---|---|
HttpRequestException or another request failure |
DNS, network, TLS certificate, invalid response, timeout, or unsuccessful status | Check the URL from the machine running .NET. Use GetAsync to inspect status and headers, and handle expected failures. |
| HTTP 404 or 403 | Wrong path, missing file, or host access policy | Verify the exact URL and whether the host permits the request. |
| C# returns text but the page stays unstyled | The app downloaded CSS, but the HTML document never references or inserts it | Emit a stylesheet link, or use a browser API to add the stylesheet. |
| The link is present but no CSS request appears | The wrong document was rendered or the browser cannot reach the stylesheet host | Inspect the final HTML and browser network results; confirm the URL is correct. |
| The CSS loads but has no visible effect | Rules may not match the page, another rule may override them, or the stylesheet may not contain the expected declarations | Inspect the loaded response, selectors, and computed styles in browser developer tools. |
| Works locally but fails after deployment | Different network access, host configuration, or asset path in the deployed environment | Check the request from the environment that actually makes it: the user’s browser for a link, or the server for HttpClient. |
| Blazor asset is missing | Path convention does not match the app type or .NET version | Use the static asset guidance for that exact project setup. |
| Playwright adds no expected styling | The page frame, target URL, or automation timing is wrong | Confirm navigation completed, the URL is reachable from the browser, and the CSS selector rules match the document. |
A browser stylesheet request and a server-side HttpClient request originate in different network contexts. A URL can be accessible from one and unavailable from the other. When diagnosing, first identify which machine is making the request.
8. Performance, reliability, and cost considerations
For ordinary page styling, a browser-managed stylesheet link avoids fetching the same CSS once in C# and then figuring out how to pass it to the document. Keep the stylesheet reasonably focused and avoid adding unnecessary requests. If C# must process CSS, use asynchronous I/O and reuse the application’s configured client. Avoid loading an unbounded response into memory if the remote resource may be large; a string-returning method necessarily materializes the body as text.
Reliability depends on the remote host and the request context. Plan for network failures, non-success status codes, and version-specific asset paths. If the stylesheet is essential, decide what the page should do when it is unavailable and whether hosting a controlled copy is appropriate. No performance benchmark is implied here: actual latency depends on the network, host, and response size.
There is no special license charge for the C# or HTML patterns shown here. Operational costs can still arise from your hosting, outbound requests, and any third-party service that serves the CSS. For screenshot workflows, account for the capture service and required browser setup separately.
9. Or skip the browser setup
If the actual goal is to capture a website as an image or PDF, downloading CSS alone does not render the page. ScreenshotNeo is a website screenshot API and MCP server: one GET request takes a URL and returns a PNG, JPEG, WebP, or PDF. It handles browser rendering for the capture.

With C#, you can make the same HTTP request with HttpClient and save the returned bytes:
using System.Net.Http;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var client = new HttpClient();
var url = "https://api.screenshotneo.com/v1/shot?access_key=YOUR_API_KEY&url=https%3A%2F%2Fstripe.com";
byte[] image = await client.GetByteArrayAsync(url);
await File.WriteAllBytesAsync("shot.webp", image);
}
}
See the ScreenshotNeo API docs for the request parameters. The equivalent command-line request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Or use Python:
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
Or Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server gives Claude, Cursor, and other MCP clients the take_screenshot, get_page_info, and capture_pdf tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Every feature is available on every plan.
Sign up free for 1,000 screenshots a month, with no card.
10. Frequently asked questions
Does HttpClient apply CSS to a web page?
No. It retrieves the response for C# to process. A browser applies CSS when the document references it or browser automation inserts it.
Should I use GetStringAsync or GetAsync?
Use GetStringAsync when you only need the body and its failure behavior suits your code. Use GetAsync when status codes, headers, or custom error handling matter.
Can I use a URL from a private network?
Only if the machine making the request can reach it. A user’s browser, your server, and a hosted screenshot service can have different network access.
Can I use this approach for a PDF?
Fetching a CSS file does not create a PDF. A browser-based print or capture workflow must render the page; ScreenshotNeo supports returning a PDF from a URL.
Where can I confirm the correct Blazor path?
Check Microsoft’s static file guidance for your app type and target .NET version, since the conventions vary.


