Sending 15 separate JavaScript and CSS files to a mobile device over a 3G network is a recipe for terrible Time-To-Interactive (TTI) scores. Bundling combines multiple files into one to reduce HTTP requests, while Minification strips out whitespace, comments, and shortens variable names to reduce payload size. Together, they dramatically speed up application load times.
Historically, ASP.NET MVC 5 used BundleConfig.cs. In early .NET Core, developers shifted to using Node.js tools like Gulp or Webpack. Today, for pure ASP.NET Core MVC Server-Side applications, the community-standard NuGet package is LigerShark.WebOptimizer.Core, authored largely by ASP.NET Core team members.
dotnet add package LigerShark.WebOptimizer.Core
You must assemble physical files into virtual bundled paths, and enable minification conditionally based on environments.
var builder = WebApplication.CreateBuilder(args);
// 1. Register WebOptimizer Services
builder.Services.AddWebOptimizer(pipeline =>
{
// A. Combine two physical CSS files into one virtual bundle path
pipeline.AddCssBundle("/css/site.bundle.css", "css/bootstrap-custom.css", "css/site.css");
// B. Combine custom Javascript files
pipeline.AddJavaScriptBundle("/js/app.bundle.js", "js/utilities.js", "js/site.js");
// C. Minify the files ONLY in Staging/Production environments to allow
// for easier debugging with full source code during Development
if (!builder.Environment.IsDevelopment())
{
pipeline.MinifyCssFiles();
pipeline.MinifyJsFiles();
}
});
builder.Services.AddControllersWithViews();
var app = builder.Build();
// 2. CRITICAL MIDDLEWARE ORDER
app.UseWebOptimizer(); // Must execute BEFORE UseStaticFiles
app.UseStaticFiles(); // Serves normal files if they aren't part of a bundle
// ... Auth, Routing, etc.
app.Run();
To let the MVC Razor view engine recognize the bundler and automatically apply Cache-Busting tokens, add the WebOptimizer tag helpers.
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, WebOptimizer.Core <!-- Add this line -->
You reference the virtual bundle path you defined in `Program.cs`.
<head>
<!-- In memory at runtime, this processes the bundle.
It outputs: <link rel="stylesheet" href="/css/site.bundle.css?v=xyz123" /> -->
<link rel="stylesheet" href="~/css/site.bundle.css" />
</head>
<body>
@RenderBody()
<!-- Javascript at the bottom -->
<script src="~/js/app.bundle.js"></script>
</body>
When you update site.css and push to production, the user's browser will stubbornly use its cached, outdated version of the file, breaking your UI. WebOptimizer implicitly handles Cache Busting.
Every time you restart the app or change the source file, WebOptimizer appends a unique hash to the URL (e.g., /css/site.bundle.css?v=8fHeW...). Because the URL changes, the browser perfectly updates its cache, downloading the new file immediately.
Generates bundles entirely dynamically IN MEMORY during ASP.NET Core runtime. It never writes a physical .min.js file to your disk. Best used for standard Server-Side MVC / Razor Pages applications that use standard jQuery or Vanilla JS.
A Node.js build-step that physically generates bundled, minified JS chunks to disk before the ASP.NET Core app even runs. Required if your application involves React, Vue, SCSS compilation, or TypeScript transpilation.
Q: "Why does the `UseWebOptimizer()` middleware have to be placed *before* the `UseStaticFiles()` middleware?"
Architect Answer: "Because `WebOptimizer` intercepts the virtual routes we defined. If the browser requests `/css/site.bundle.css`, `WebOptimizer` must catch that request, generate the bundled content dynamically in memory, and return the HTTP response overriding the pipeline. If `UseStaticFiles()` came first, it would receive the request, look on the physical server hard drive, fail to find a physical file named `site.bundle.css`, and immediately return an HTTP 404 Not Found error, bypassing our bundler entirely."