Skip to content

Security Headers

The Granit.Http.SecurityHeaders module provides centralized HTTP security response header management. It suppresses the Kestrel Server header to prevent server fingerprinting and injects defensive headers on every response per the OWASP Secure Headers Project.

Included in Bundle.Essentials — active by default for all Granit applications.

StandardReference
OWASP Secure Headers ProjectResponse header best practices
OWASP ASVS V14.4HTTP Security Headers
ISO 27001 A.8.9Configuration management
CWE-200Information Exposure
HeaderDefault valuePurpose
Server(removed)Prevent fingerprinting
X-Content-Type-OptionsnosniffPrevent MIME sniffing
X-Frame-OptionsDENYPrevent clickjacking
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForce HTTPS (1 year)
Referrer-Policystrict-origin-when-cross-originControl referrer leakage
Permissions-Policycamera=(), microphone=(), geolocation=(), payment=(), accelerometer=(), gyroscope=(), magnetometer=(), usb=()Restrict browser features
X-XSS-Protection0Disable legacy XSS auditor
Cross-Origin-Opener-Policysame-originSpectre isolation
Cross-Origin-Resource-Policysame-originResource sharing control
Cross-Origin-Embedder-Policy(not set)Optional Spectre isolation
Content-Security-Policy(not set)Application-specific

The module is included in Bundle.Essentials. Add the middleware in Program.cs:

app.UseGranitExceptionHandling(); // 1st — catch errors
app.UseGranitSecurityHeaders(); // 2nd — headers on ALL responses
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

All headers are configurable via appsettings.json:

{
"SecurityHeaders": {
"SuppressServerHeader": true,
"EnableContentTypeOptions": true,
"XFrameOptions": "DENY",
"ReferrerPolicy": "strict-origin-when-cross-origin",
"DisableXssProtection": true,
"PermissionsPolicy": "camera=(), microphone=(), geolocation=(), payment=(), accelerometer=(), gyroscope=(), magnetometer=(), usb=()",
"ContentSecurityPolicy": null,
"EnableHsts": true,
"HstsMaxAgeSeconds": 31536000,
"HstsIncludeSubDomains": true,
"HstsPreload": false,
"CrossOriginOpenerPolicy": "same-origin",
"CrossOriginEmbedderPolicy": null,
"CrossOriginResourcePolicy": "same-origin"
}
}

Set the header value to null or the enable flag to false:

{
"SecurityHeaders": {
"XFrameOptions": null,
"EnableContentTypeOptions": false
}
}

CSP is highly application-specific and is not set by default. Configure it per application:

{
"SecurityHeaders": {
"ContentSecurityPolicy": "default-src 'self'; script-src 'self'"
}
}

The BFF module sets frame-ancestors 'none' on login/callback routes independently.

To submit your domain to the HSTS Preload List:

{
"SecurityHeaders": {
"HstsPreload": true,
"HstsIncludeSubDomains": true,
"HstsMaxAgeSeconds": 31536000
}
}

COEP is not set by default because it can break cross-origin resources (fonts, images, scripts). Enable it only when all cross-origin resources include Cross-Origin-Resource-Policy:

{
"SecurityHeaders": {
"CrossOriginEmbedderPolicy": "require-corp"
}
}

The Server header suppression applies only when the application runs on Kestrel. When deployed behind a reverse proxy (nginx, Envoy, YARP), the proxy manages its own Server header. Ensure the proxy is also configured to suppress it.