Posted on 29/05/2026 13:00:02
Hi Ruwan,
A couple of things to untangle here, because the two approaches you've tried (an IPipeline and an IStartupFilter) behave quite differently in Dynamicweb 10 — and the IStartupFilter one is almost certainly why nothing runs on staging.
Why the IStartupFilter doesn't fire
Dynamicweb 10 does not build its request pipeline through the conventional Startup.Configure convention. It is built explicitly inside app.UseDynamicweb() (called from WebApplicationBuilder.SetupDynamicweb()). IStartupFilter instances only run if two things are true:
- They are registered in DI yourself, e.g. services.AddTransient<IStartupFilter, GeoRoutingStartupFilter>(), and
- The host actually invokes the Configure filter chain.
If you registered the filter from inside your IPipeline.RegisterServices, the ordering/timing won't reliably wrap Dynamicweb's pipeline, and if you registered it from a separate Program.cs that differs between your local run and staging, it simply won't be there on staging. Either way, IStartupFilter is the wrong tool here — drop it.
The supported way: just use your IPipeline
You already have the right vehicle. IPipeline.RegisterApplicationComponents(IApplicationBuilder app) is exactly where you call app.UseMiddleware<GeoRoutingMiddleware>() — no IStartupFilter needed:
public sealed class GeoRoutingPipeline : IPipeline
{
// Lower rank = earlier. See note below about ranks 1–100.
public int Rank => 101;
public void RegisterServices(IServiceCollection services, IMvcCoreBuilder mvcBuilder)
{
services.AddSingleton<GeoIpDatabase>(); // your GeoIP service
}
public void RegisterApplicationComponents(IApplicationBuilder app)
{
app.UseMiddleware<GeoRoutingMiddleware>();
}
public void RunInitializers() { }
}
How Dynamicweb wires this up (from PipelineExtensions):
- Pipelines are discovered through the AddIn manager (AddInManager.GetTypes<IPipeline>()), instantiated with AddInManager.GetInstance<IPipeline>(), then ordered by Rank and run via app.UsePipelines().
- All IPipeline.RegisterApplicationComponents calls run together, ordered by Rank, near the end of UseDynamicweb().
About "before the built-in middlewares" and ranks 1–100
This is the part worth being precise about. The rank only orders the pipeline registrations relative to each other inside UsePipelines(). The classic frontend (routing/URL handling/NotFound etc.) lives in ClassicFrontendPipeline with Rank = int.MaxValue, so any custom pipeline with a lower rank registers its middleware before the frontend's — which is what you want for geo-routing/redirect logic.
However, a number of framework middlewares (forwarded headers, restricted access, compression, file providers, extensibility, installation/license, rate limiter) are registered directly in UseDynamicweb() before UsePipelines() is ever called. You cannot get in front of those via IPipeline regardless of rank — and you generally don't want to (you'd be ahead of forwarded-headers handling, which would break the very client-IP detection a GeoIP middleware depends on).
So: use Rank => 101 (rank 101+ is fine for custom code; 1–100 is reserved), put UseMiddleware in RegisterApplicationComponents, and your middleware will sit ahead of the frontend routing while still benefiting from forwarded headers being resolved first.
Why it works locally but not on staging
Because discovery goes through the AddIn manager, the most common reason a pipeline runs locally but not on staging is that the assembly isn't being loaded on staging. Check:
- Your DLL is actually deployed to the AddIns location that's scanned — /Files/System/AddIns/Installed/<yourfolder>/ — and the folder isn't blacklisted.
- The class is public and has a public parameterless constructor (the AddIn manager instantiates it).
- The startup log. Each discovered pipeline logs Running pipeline: '<FullName>'. If you don't see GeoRoutingPipeline there on staging, it was never discovered (deployment/assembly issue). If you do see it but get a registration error, RegisterServices threw — failures there are caught and logged as RegisterServices threw an exception, and the pipeline is then skipped (added to "broken pipes"), so its middleware won't run.
Confirm the staging startup log first — that single line tells you whether this is a discovery problem (DLL not deployed) or a registration problem (exception in RegisterServices). My guess given "works locally, not on staging" is the former: the build with your middleware assembly didn't make it into the staging deployment's AddIns folder.
Best regards