News · 10 November 2026
OwaspHeaders.Core 11 Arrives Alongside .NET 11
OwaspHeaders.Core 11 targets .NET 10 and 11, moves configuration to a delegate that is checked at startup, and ships an agent skill inside the package.

Leeds, UK. RJJ Software has released version 11 of OwaspHeaders.Core, its open-source security middleware for ASP.NET Core, on 10th November 2026: the day Microsoft makes .NET 11 generally available. The release follows a public release candidate, 11.0.0-rc.1, which was published to NuGet on 6th October 2026 so that teams could build against it before the final version shipped.
Version 11 is a major release. It drops two .NET versions, changes how the middleware is configured, moves configuration errors from the first request to application startup, fixes long-standing defects in report-only Content-Security-Policy, and installs an agent skill into the repositories of the projects which use it.
ℹ️ Note
Version 11 contains breaking changes. Before updating the package, read Upgrading to version 11, which lists everything a project moving from version 10 needs to change, what to expect on the first build, and how to opt out of the parts it does not want.
Version 11 Follows .NET
Version 11 targets .NET 10, a long-term support release, and .NET 11, a standard-term support release. Support for .NET 8 and .NET 9 has been dropped.
That follows from the project’s security policy: a release of OwaspHeaders.Core is supported for as long as Microsoft supports the .NET versions it runs on, and no longer. .NET 8 and .NET 9 both leave Microsoft’s support lifecycle today, the same day .NET 11 arrives, so carrying them forward would mean shipping a package for runtimes nobody should be running in production.
The 11.x line is supported until 14th November 2028, on both .NET 10 and .NET 11. Microsoft gives the two runtimes the same end-of-support date, even though .NET 10 is a long-term support release and .NET 11 is not.
Teams that cannot move off .NET 8 or .NET 9 yet should stay on the 10.x line, which remains supported until 14th November 2028 by way of its .NET 10 target. Restoring version 11 into a project that targets net8.0 or net9.0 fails with NU1202, so nobody will end up on it by accident.
Released Ahead of .NET 11
Version 11.0.0 was published on the afternoon of 10th November, UK time, ahead of Microsoft’s announcement of .NET 11 general availability at .NET Conf later the same day. Its net11.0 build was compiled and tested against .NET 11 RC 3 (SDK 11.0.100-rc.3.26522.148), the newest .NET 11 release available at the time. Publishing first was a deliberate choice, and the risk it carries is small:
- The package has no NuGet dependencies, only a framework reference to ASP.NET Core. Nothing in it is pinned to the release candidate; a project runs it on whichever ASP.NET Core 11 runtime is installed, which will be the final one once it ships.
- ASP.NET Core keeps the same assembly version, 11.0.0.0, from its release candidates through to general availability, so a library compiled against RC 3 loads on the final runtime without being rebuilt.
- RC 3 is a “go-live” release, which Microsoft supports for production use, and its API is frozen. Changes between it and the final release are bug fixes. OwaspHeaders.Core also relies only on long-established parts of ASP.NET Core: middleware,
HttpContextand response headers. - The
net10.0build is compiled against the released .NET 10, so projects on .NET 10 are not affected by the timing at all.
What remains is that the tests ran on RC 3 rather than on the final runtime, so a bug fix in the final release could, in principle, change behaviour the tests rely on. The test suite will be re-run once the final release ships. A version published to NuGet can be unlisted but never replaced, so any fix that turns out to be needed will arrive as 11.0.1.
Waiting would have gained little. .NET 8 and .NET 9 reach end of support today either way, so teams moving to .NET 10 can upgrade now, and teams moving to .NET 11 can do so on day one without waiting for this package to catch up.
A New Way to Configure the Middleware
Until now, configuring the middleware meant building a configuration object and then handing it over. In version 11, a delegate is passed straight to UseSecureHeadersMiddleware:
// Version 10, still supported in version 11
// with a CS0618 warning
var config = SecureHeadersMiddlewareBuilder
.CreateBuilder()
.UseHsts()
.UseXFrameOptions()
.SetUrlsToIgnore(["/health"])
.Build();
app.UseSecureHeadersMiddleware(config);
// Version 11
app.UseSecureHeadersMiddleware(opt =>
{
opt.UseHsts();
opt.UseXFrameOptions();
opt.SetUrlsToIgnore(["/health"]);
});
It is the same pattern ASP.NET Core developers already use for Cross-Origin Resource Sharing (CORS) with app.UseCors(policy => ...). Teams that want the OWASP-recommended set of headers and only need to change one or two of them can start from opt.UseRecommendedDefaults(), which replaces BuildDefaultConfiguration():
app.UseSecureHeadersMiddleware(opt =>
{
opt.UseRecommendedDefaults();
opt.SetUrlsToIgnore(["/health"]);
});
Calling app.UseSecureHeadersMiddleware() with no arguments still applies the recommended set, exactly as before, and produces no warnings.
The old build-then-pass API still works in version 11, but four of its members are now marked [Obsolete], so a project using them will see CS0618 warnings on its first build. Those warnings are deliberate; they are how the package tells a team that it is time to migrate. The deprecated members are removed in version 12, which gives every consumer a full major version to make the move.
Teams that need some breathing room can silence CS0618 in their own project file while they migrate. The package does not do this for them, because the only way a package can suppress that warning in someone else’s build would also switch off obsolete warnings from every other package and from the team’s own code. The upgrade guide explains how to keep that suppression temporary.
The deprecated overload ignored its urlIgnoreList argument whenever a configuration object was also passed, whereas opt.SetUrlsToIgnore(...) always applies. A project that passed both will find its ignore list taking effect for the first time.
Configuration Is Checked at Startup
In version 10, a misconfigured application started cleanly, deployed green, and then threw when the first real request arrived. In version 11, configuration is validated while the request pipeline is built, so an invalid configuration stops the application from starting.
Teams can also check a configuration from a unit test without starting a host. SecureHeadersBuilder.BuildAndValidate takes the same delegate as UseSecureHeadersMiddleware, so a single named method can serve both Program.cs and the test suite:
static void ConfigureSecureHeaders(SecureHeadersBuilder opt)
=> opt.UseRecommendedDefaults();
// in Program.cs
app.UseSecureHeadersMiddleware(ConfigureSecureHeaders);
// in a test: throws if the configuration is invalid
SecureHeadersBuilder.BuildAndValidate(ConfigureSecureHeaders);
Invalid values are now rejected as soon as they are configured. Before version 10.5.1, a null entry in the list passed to SetUrlsToIgnore threw on every request; version 11 rejects a null, empty or whitespace entry with an ArgumentException at startup.
The public API is now fully annotated for nullable reference types, so projects that enable them get accurate compile-time warnings wherever a value might be null. And the properties on the configuration object can no longer be assigned directly; a configuration can only be built through the builder. That closes a null-reference crash present in every earlier version, which version 10.4.0 mitigated but could not fix without the API change that a major version allows.
When the project asked its users which features version 11 should include, one reply put that null-reference fix at the top of the list, on the grounds that reliability beats new features.
Version 11 ships a number of features that I’ve wanted to include for a long time, the main two being the configuration delegate and validating the configuration at startup. I’d rather an application fail fast at startup than fail at runtime.
Security and Correctness Fixes
Report-only Content-Security-Policy now works as documented. Report-only mode lets a team trial a policy in production: the browser reports what the policy would have blocked, without blocking anything. Three defects had been present in that mode since it was added in 2019, and the most significant meant that SetCspUris and SetCspSandBox were silently ignored in report-only mode. The Content-Security-Policy-Report-Only header went out with no directives, so it reported nothing.
Version 11 fixes all three. Report-only headers now carry the directives configured for them, which means browsers will start sending violation reports to the configured report-uri once a project upgrades. Teams using report-only mode should make sure that endpoint is ready for the traffic. Passing useXContentSecurityPolicy: true to either report-only method now throws at startup, because the legacy X-Content-Security-Policy header has no report-only form; the upgrade guide shows where that option belongs instead.
The Expect-CT header is gone. OWASP deprecated it, browser support fell away long before that, and the remaining opt-in methods were marked obsolete in version 10.3. Version 11 removes it entirely. Any call to UseExpectCt should be deleted, and nothing replaces it.
Teams jumping straight from a version earlier than 10.5.0 will also pick up a change to Cache-Control. Its default value is now no-cache, no-store, max-age=0, which ASP.NET Core’s antiforgery protection accepts without overriding it or logging a warning, and the options passed to UseCacheControl now combine rather than the first one winning. The upgrade guide has a section for those teams, covering how to restore caching where a project relied on the old behaviour.
An Agent Skill That Ships in the Package
Version 11 is the first release of OwaspHeaders.Core that writes files into the repositories of the projects using it, so this section sets out what it writes, why, and what it never does.
Coding agents such as Claude Code, GitHub Copilot and Codex learn from code written against older versions of libraries. Version 11 breaks exactly the code an agent trained on versions 8 to 10 will write. Worse, the most damaging mistakes an agent can make with this package compile cleanly. Setting a security header by hand with Response.Headers.Append(...), or adding 'unsafe-inline' to a Content-Security-Policy to make a page work, will pass every test while weakening the protection the package exists to provide.
So the package now carries an agent skill: a single page of markdown, written for coding agents, which gives them the version 11 API and tells them not to hand-write the headers the middleware owns. When a project that references OwaspHeaders.Core builds, the skill is copied, along with a .gitignore, into .agents/skills/owaspheaders-core/ and .claude/skills/owaspheaders-core/ at the root of the repository.
The idea comes from Sander ten Brinke’s post on shipping an agent skill that installs itself. Sander also shaped the design. An early draft had the skill point agents at the project’s documentation online for the detail; he argued instead for a skill that is complete in itself, and that is the version that shipped.
For anyone reviewing the package before adopting it:
- The install is a plain MSBuild
Copytask. No compiled code or scripts run during the build. - Both files come from inside the NuGet package and nothing is downloaded, so the guidance always matches the installed version. It is replaced whenever the package version changes.
- The
.gitignoreignores the folder’s contents, itself included, sogit statusstays clean. - The package writes nothing on the CI systems it detects, including GitHub Actions, Azure Pipelines and GitLab, or in IDE design-time builds. Detection is a heuristic. On a CI system it does not recognise, with a read-only source tree, the build fails with
MSB3021rather than running code to work around it; the fix is to opt out. - Adding
<OwaspHeadersCoreAgentSkill>false</OwaspHeadersCoreAgentSkill>to a project file, or toDirectory.Build.props, stops future installs. Opting out does not remove a skill that is already installed, so delete the two folders as well.
The skill cannot drift away from the API it describes, either. Every C# sample in it must match a sample that is compiled as part of the package’s own test suite, so a change that breaks the guidance fails the package’s own tests.
A skill with out-of-date guidance is worse than none, because agents treat a skill as authoritative and do exactly what it says whether or not it still describes how the code works. RJJ Software has written about what happens when nobody owns those files. This skill has an owner, the package, and it is rewritten every time the package version changes. The Agent Skill documentation covers every rule, including how to choose different install locations.
How the Release Was Built
Every release of OwaspHeaders.Core now creates a GitHub release that carries the NuGet package and its provenance bundle: a signed record of which workflow, commit and repository produced the package. The release candidate on 6th October was the first time that process ran for real, and version 11.0.0 is the second. It sits alongside the build attestations the project already publishes, which RJJ Software has described in Attestations: The Missing Link Between Your Code and Production Trust, and which anyone can verify with the GitHub CLI.
OwaspHeaders.Core’s OpenSSF Scorecard rating rose from 7.1 at the start of September to 8.5 on the day version 11 was released, and Scorecard’s check for signed releases, which looks for exactly that provenance, now scores 10 out of 10.
The project holds the OpenSSF Best Practices “passing” badge, and the version 11 work met a number of the criteria for the higher silver and gold levels: enforced coding standards, repeatable builds and published debug symbols for silver, and test coverage for gold. At release, the test suite covered 91% of lines and 96% of branches, and it runs on every pull request and again before every release. The project now meets 84% of the silver criteria and 43% of the gold ones.
The public API is now tracked with Microsoft’s PublicApiAnalyzers, so any unintended change to it fails the build before it can reach a release.
I hope that the combination of PublicApiAnalyzers, which lets users see exactly what changed in the package’s public API, and attestations gives consumers the confidence they need from a security package. I’m really looking forward to hearing back from users with their thoughts on both that and the auto-installed skill, which they can opt out of.
While there are a lot of changes in this release, I hope that the combination of new features and bug fixes makes it easier to use; it certainly feels like it to me.
Upgrading
The Upgrading to version 11 guide is the full checklist, and each change it lists links to the matching entry in the changelog. It suggests working in four steps:
- Update the package and build, then fix the compiler errors.
- Work through the new warnings:
CS0618for the deprecated configuration API and, in projects with nullable reference types enabled, the new nullable warnings. - Start the application and run the tests, since a configuration problem now stops the host from starting.
- Check the repository for the agent skill, and opt out if it is not wanted.
To install or update the package:
dotnet add package OwaspHeaders.Core --version 11.0.0
About OwaspHeaders.Core
OwaspHeaders.Core adds the security headers recommended by the OWASP Secure Headers Project to ASP.NET Core applications, in a single line of configuration. It is free, open source, and has been downloaded more than 2.2 million times on NuGet, having passed two million downloads in September 2026. The project is maintained by Jamie Taylor, CTO at RJJ Software, who held Microsoft’s Most Valuable Professional (MVP) award for Developer Technologies for four consecutive years.
OwaspHeaders.Core is part of RJJ Software’s commitment to open-source security tools that make the web safer for everyone. If your team needs an application security review, or help bringing coding agents into its workflow without weakening what it ships, schedule a consultation.