Introduction
Server-side rendering has always been an important part of building web applications that load quickly and work well across different devices.
In ASP.NET Core, Blazor has traditionally provided several rendering options, including interactive server rendering and WebAssembly-based rendering. Static Server-Side Rendering, commonly called static SSR, takes a simpler approach: the server generates the HTML and sends it to the browser without establishing an interactive Blazor circuit for that page.
That difference can matter.
If a page only needs to display content and does not require client-side interaction, sending ready-to-use HTML can reduce the amount of work the browser needs to perform. It can also reduce the amount of application-specific JavaScript and runtime infrastructure involved in making that page interactive.
With the improvements coming in ASP.NET Core 11, static SSR is worth looking at from a performance perspective rather than treating it as just another rendering option.
This article explains how static SSR works, how to measure its performance, and how to compare HTML payloads without making unsupported assumptions about the results.
What Is Static SSR?
Static SSR means that the server renders a component into HTML and returns that HTML as part of the HTTP response.
The basic flow looks like this:
Browser
|
| HTTP Request
v
ASP.NET Core Server
|
| Render component
v
Generated HTML
|
| HTTP Response
v
Browser displays HTML
There is no requirement for the page to become interactive after rendering.
For example, a simple Razor component can contain:
@page "/products"
<h1>Products</h1>
<ul>
@foreach (var product in Products)
{
<li>
@product.Name - @product.Price.ToString("C")
</li>
}
</ul>
@code {
private readonly List<Product> Products =
[
new("Laptop", 85000),
new("Monitor", 18000),
new("Keyboard", 2500)
];
private record Product(string Name, decimal Price);
}
The server renders the component and returns HTML that the browser can display directly.
This is different from an application where the browser first downloads a client-side runtime and then performs additional rendering work.
Static SSR vs Interactive Rendering
The most important question is not whether static SSR is faster in every situation. It is whether it is the right rendering mode for a particular page.
| Area | Static SSR | Interactive Server | WebAssembly |
|---|---|---|---|
| Initial HTML | Server-generated | Server-generated | Client-generated after startup |
| Browser runtime | Minimal | Requires interactive infrastructure | Requires WebAssembly runtime |
| Interactivity | No by default | Yes | Yes |
| Server connection | Not required for static rendering | Required for interactive circuit | Not required after download |
| Initial payload | Primarily HTML and required assets | HTML plus interactive infrastructure | HTML plus client application/runtime |
| Best fit | Content-focused pages | Interactive applications | Rich client-side applications |
The table should not be interpreted as a universal performance ranking.
A page that contains complex server-side processing may still have a high response time even if the browser receives static HTML. Similarly, a highly interactive application may gain little from making every page static.
The rendering strategy should match the workload.
Creating a Static SSR Page
A minimal Blazor Web App can be configured with static rendering.
For example, the application can register Razor components:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorComponents();
var app = builder.Build();
app.UseStaticFiles();
app.UseAntiforgery();
app.MapRazorComponents<App>();
app.Run();
The exact service and endpoint configuration can vary depending on the application and rendering modes being used.
A component can then be rendered without enabling an interactive render mode.
@page "/dashboard"
<h1>Dashboard</h1>
<p>Server-rendered dashboard content.</p>
The important idea is that the page does not automatically require an interactive client connection simply to display its content.
Why HTML Payload Size Matters
Startup performance is not determined by server response time alone.
The browser also needs to receive the response, parse the HTML, download required resources, construct the DOM, and render the page.
A simplified model is:
Request
|
v
Server Processing
|
v
HTML Response
|
v
Network Transfer
|
v
HTML Parsing
|
v
DOM Construction
|
v
Visual Rendering
A smaller response can reduce network transfer work, especially on slower connections.
However, HTML size is only one part of the overall page-load equation. Images, CSS, JavaScript, fonts, caching, compression, and server processing can all contribute to the final experience.
Measuring Response Size
The first useful experiment is to measure the actual HTTP response.
For example, you can use curl:
curl -o /dev/null -s -w \
"HTTP: %{http_code}\nSize: %{size_download} bytes\nTime: %{time_total}s\n" \
https://localhost:5001/products
For local development, the URL and port will depend on your ASP.NET Core configuration.
This gives you a basic measurement of:
HTTP status
Downloaded response size
Total request time
For more detailed analysis, browser developer tools can show request size, transferred size, response timing, and other network information.
Measuring Server Response Time
ASP.NET Core applications can expose timing information through logging and diagnostics.
For a simple test, keep the endpoint logic stable and compare the same page under different rendering configurations.
For example:
app.MapGet("/benchmark", async () =>
{
await Task.Delay(1);
return Results.Ok(new
{
Message = "Benchmark response"
});
});
The example above is intentionally simple. In a real benchmark, the server should perform the actual work performed by the application.
If the page loads data from a database, use representative database access.
If the page performs expensive calculations, include those calculations.
Otherwise, the benchmark measures an artificial scenario rather than the application users actually experience.
Measuring Static SSR With Browser Developer Tools
The browser's Network tab is one of the easiest places to start.
Open the application and inspect the document request.
Look at:
Request URL
Status code
Transferred size
Resource size
Waiting time
Content download time
Number of additional requests
The distinction between transferred size and resource size is useful.
Compression can make the amount transferred over the network smaller than the uncompressed HTML document.
For example:
HTML resource size: 85 KB
Transferred over HTTP: 19 KB
Those numbers represent different things.
When comparing payloads, record both when possible.
Join the conversation! Your thoughts help the community grow.