Introduction
ASP.NET Core has a feature called "Hosting Startup". It allows us to run additional code when an ASP.NET Core Web application starts up. We can also add middleware through this feature.
Using Hosting Startup, we can add middleware to an already-built ASP.NET Core Web application without rebuilding it. The middleware is implemented in a separate assembly that is built independently.
The official documentation about Hosting Startup is available here.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/platform-specific-configuration
How Hosting Startup Works
Let me explain how Hosting Startup works in more detail.
- Prepare a pre-built assembly (a Hosting Startup assembly) that implements the
IHostingStartupinterface and is marked with theHostingStartupassembly attribute. - Set the environment variable
ASPNETCORE_HOSTINGSTARTUPASSEMBLIESto the name of the Hosting Startup assembly created in step 1. - Run the separately-built ASP.NET Core application.
- When the ASP.NET Core application starts up, the
IHostingStartup.Configure(IWebHostBuilder builder)method in the Hosting Startup assembly (specified by the environment variable) is called. Inside this method, we can use theIWebHostBuilderobject passed as an argument to register additional services to the DI container and perform other initialization tasks.
To add middleware to the ASP.NET Core HTTP request pipeline, we need a few more steps.
- Inside the
IHostingStartup.Configure(IWebHostBuilder)method of the Hosting Startup assembly, register a class that implementsIStartupFilterwith the DI container. - After the host is built in the main ASP.NET Core application, when the HTTP request pipeline is being constructed, the
IStartupFilterclass registered above is instantiated and itsIStartupFilter.Configure(Action<IApplicationBuilder> next)method is called. This method returns a function that registers middleware. - The "function that registers middleware" returned from the above method is then executed. This is how the middleware specified in the Hosting Startup assembly gets added to the target ASP.NET Core application.
Implementing a Hosting Startup Assembly and Seeing It in Action
Let's actually implement a Hosting Startup assembly that adds middleware and see it load when an ASP.NET Core Web application starts up.
1. Prepare a Simple ASP.NET Core Web Application
First, let's prepare a simple ASP.NET Core Web application. Open a terminal and run the following dotnet commands to create a new ASP.NET Core Web application and run it.
$ dotnet new web -n MyAspNetCoreApp -f net10.0
$ dotnet build ./MyAspNetCoreApp
$ dotnet ./MyAspNetCoreApp/bin/Debug/net10.0/MyAspNetCoreApp.dll
Open another terminal and check if it works using the curl command.
$ curl -i http://localhost:5000/
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Sat, 14 Feb 2026 02:17:10 GMT
Server: Kestrel
Transfer-Encoding: chunked
Hello World!
Looks good! Now, press Ctrl + C to stop the running ASP.NET Core Web application for now.
2. Implement the Hosting Startup Assembly
Next, let's implement the Hosting Startup assembly. A Hosting Startup assembly is just a regular class library. So run the following dotnet commands to create a new class library project. Additionally, add the Microsoft.AspNetCore.Hosting package to the project, since we need the types for implementing a Hosting Startup assembly. Then open it in VSCode and start implementing.
$ dotnet new classlib -n MyHostingStartup -f net10.0
$ dotnet add package Microsoft.AspNetCore.Hosting --project ./MyHostingStartup
$ code ./MyHostingStartup
We will work in reverse order from how things are called at runtime. Let's first implement the IStartupFilter class, which "returns a function that registers middleware".
Add a file named "MyStartupFilter.cs" to the Hosting Startup project and write the following code.
// MyHostingStartup/MyStartupFilter.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
public class MyStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
// Return a function that adds middleware
return app =>
{
app.Use(async (context, nextMiddleware) =>
{
// In this sample, add a custom header to the HTTP response
context.Response.Headers.Add("X-Custom-Header", "MyCustomValue");
// Call the next middleware in the HTTP request pipeline
await nextMiddleware.Invoke();
});
// Call the next middleware registration process
next(app);
};
}
}
In this implementation, as an example, the middleware adds a response header called "X-Custom-Header" to every HTTP response.
Next, add a class that implements the IHostingStartup interface. This class is called when the ASP.NET Core Web application starts up, and it registers the IStartupFilter class above with the DI container. Add a file named "MyStartup.cs" to the project and write the following code.
// MyHostingStartup/MyStartup.cs
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
[assembly: HostingStartup(typeof(MyStartup))]
public class MyStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
// Register IStartupFilter to the service collection
builder.ConfigureServices(services =>
{
services.AddSingleton<IStartupFilter, MyStartupFilter>();
});
}
}
That is all the source code we need. Let's build it with the dotnet command.
$ dotnet build ./MyHostingStartup
3. Run the ASP.NET Core Web Application with the Hosting Startup Assembly
Now, let's set the environment variable ASPNETCORE_HOSTINGSTARTUPASSEMBLIES to the assembly name "MyHostingStartup" that we just built.
$ export ASPNETCORE_HOSTINGSTARTUPASSEMBLIES=MyHostingStartup
If you are using PowerShell, run the following instead.
PS> $env:ASPNETCORE_HOSTINGSTARTUPASSEMBLIES="MyHostingStartup"
With this setting in place, let's run the ASP.NET Core Web application that we created earlier again.
$ dotnet ./MyAspNetCoreApp/bin/Debug/net10.0/MyAspNetCoreApp.dll
But unfortunately, it does not start successfully. We get an error saying "the assembly 'MyHostingStartup' was not found", as shown below.
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
crit: Microsoft.AspNetCore.Hosting.Diagnostics[11]
Hosting startup assembly exception
System.InvalidOperationException: Startup assembly MyHostingStartup failed to execute. See the inner exception for more details.
---> System.IO.FileNotFoundException: Could not load file or assembly 'MyHostingStartup, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
File name: 'MyHostingStartup, Culture=neutral, PublicKeyToken=null'
at System.Reflection.RuntimeAssembly.InternalLoad(AssemblyName assemblyName, StackCrawlMark& stackMark, AssemblyLoadContext assemblyLoadContext, RuntimeAssembly requestingAssembly, Boolean throwOnFileNotFound)
at System.Reflection.Assembly.Load(AssemblyName assemblyRef)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.ExecuteHostingStartups()
--- End of inner exception stack trace ---
Actually, this is expected. The .NET runtime does not have enough information to determine where to load the assembly MyHostingStartup.dll, so it fails with a "file not found" error.
4. Make the Hosting Startup Assembly Loadable by Specifying the Dependency
So, we need to add MyHostingStartup.dll as a dependency of the ASP.NET Core Web application and tell the runtime where to find it (so it can be resolved to an absolute path). To do this, we need to take care of a couple of things.
First, to make things simple, let's place the Hosting Startup assembly in the same folder as the ASP.NET Core Web application assembly. We can either copy the built Hosting Startup assembly file manually or rebuild the Hosting Startup project with the output folder set to the same folder as the ASP.NET Core Web application output, like this.
$ dotnet build ./MyHostingStartup -o ./MyAspNetCoreApp/bin/Debug/net10.0
Next, create a JSON file that describes the .NET dependency. The file name can be anything, but here we will call it "additional.deps.json". Let's create the file in the current folder and open it in an editor like VSCode.
$ echo "" > additional.deps.json
$ code ./additional.deps.json
In this JSON file, write the following to declare that MyHostingStartup.dll is a dependency.
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v10.0"
},
"targets": {
".NETCoreApp,Version=v10.0": {
"MyHostingStartup": {
"runtime": {
"MyHostingStartup.dll": {}
}
}
}
},
"libraries": {
"MyHostingStartup": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
After saving this JSON file and closing the editor, we set the environment variable DOTNET_ADDITIONAL_DEPS to the path of this dependency JSON file so that it is loaded at runtime.
$ export DOTNET_ADDITIONAL_DEPS=./additional.deps.json
Or if you are using PowerShell,
PS> $env:DOTNET_ADDITIONAL_DEPS = './additional.deps.json'
After doing this, let's run the ASP.NET Core Web application project again. This time, it should start without any errors.
$ dotnet ./MyAspNetCoreApp/bin/Debug/net10.0/MyAspNetCoreApp.dll
Let's check with the curl command to see if the Hosting Startup assembly was really loaded and the middleware was added.
$ curl -i http://localhost:5000/
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: Sat, 14 Feb 2026 04:10:47 GMT
Server: Kestrel
Transfer-Encoding: chunked
X-Custom-Header: MyCustomValue
Hello World!
Looking at the curl output above, we can see that the "X-Custom-Header" response header has been added. This confirms that the Hosting Startup assembly MyHostingStartup.dll was loaded and its IHostingStartup.Configure(IWebHostBuilder builder) method was called.
Conclusion
Using the Hosting Startup feature, we can add middleware to an existing ASP.NET Core Web application without rebuilding it. The middleware is implemented in a separately-built assembly and added at runtime.
The basic steps to implement a Hosting Startup assembly are to build a class library that contains a class implementing the required interfaces and the assembly attribute following the conventions, and then start the ASP.NET Core Web application with the assembly name specified in the ASPNETCORE_HOSTINGSTARTUPASSEMBLIES environment variable.
However, because of how .NET application execution works, we need to explicitly specify assembly dependencies along with the location of the assembly files in a JSON file. The Hosting Startup assembly is no exception. We need to declare the dependency in a JSON file from the perspective of the target ASP.NET Core Web application and specify that JSON file in the DOTNET_ADDITIONAL_DEPS environment variable.
So in short, the Hosting Startup feature lets us add middleware to an existing ASP.NET Core Web application to extend its functionality. I think this is a feature that opens up many possibilities depending on your ideas. Please give it a try!
Top comments (0)