Security · Pulse Team

Security Headers in Umbraco 17: What They Are, Why They Matter, and How to Get Them Right

If you’ve ever run your website through a security scanner and been greeted with a list of “missing headers”… you’re not alone.

Security headers can feel a bit invisible. They don’t change how your site looks. They don’t add features. But they quietly protect your users, your data, and your reputation every single time someone visits your site.

Let’s break them down in a way that actually makes sense - no jargon, no overcomplication.

What are Security Headers?

Security headers are instructions your website sends to a browser (like Chrome or Edge) telling it how to behave safely when handling your content.

Think of them like rules you give to a visitor before they walk into your house:

  • Don’t open unknown doors
  • Only trust certain sources
  • Never leave sensitive information lying around

They’re sent as part of your HTTP response - completely behind the scenes - but they shape how the browser handles things like scripts, cookies, and connections.

Why Do You Need Them?

Because modern websites aren’t just pages - they’re applications.

And applications get attacked.

Without security headers, your Umbraco site is more vulnerable to things like:

Cross-Site Scripting (XSS)

Malicious scripts injected into your site that run in your users’ browsers.

Clickjacking

Your site being loaded invisibly inside another site to trick users into clicking things they didn’t intend.

Data Interception

Sensitive data being intercepted if connections aren’t properly enforced as secure.

Cookie Theft

Session cookies being accessed in unsafe ways.

Security headers don’t replace good coding practices - but they add a powerful extra layer of defence.

The Key Security Headers (Explained Simply)

Here are the main ones you should care about in Umbraco 17:

1. Content-Security-Policy (CSP)

What it does: Controls what content (scripts, images, styles) your site is allowed to load.

Why it matters: This is your strongest defence against XSS attacks.

Example:

Content-Security-Policy: default-src 'self'; img-src 'self' https:;

In plain English: “Only load content from my own site, except images which can come from HTTPS sources.”

2. X-Frame-Options

What it does: Stops your site being embedded in an iframe.

Why it matters: Prevents clickjacking.

Example:

X-Frame-Options: SAMEORIGIN

3. X-Content-Type-Options

What it does: Stops browsers from guessing file types.

Why it matters: Prevents certain types of attacks where files are misinterpreted.

Example:

X-Content-Type-Options: nosniff

4. Strict-Transport-Security (HSTS)

What it does: Forces browsers to always use HTTPS.

Why it matters: Protects against man-in-the-middle attacks.

Example:

Strict-Transport-Security: max-age=31536000; includeSubDomains

5. Referrer-Policy

What it does: Controls how much referral information is shared.

Why it matters: Protects user privacy.

Example:

Referrer-Policy: strict-origin-when-cross-origin

6. Permissions-Policy

What it does: Controls access to browser features (camera, microphone, etc.).

Why it matters: Reduces risk of abuse.

Example:

Permissions-Policy: geolocation=(), microphone=()

How to Add Security Headers in Umbraco 17

Umbraco 17 runs on ASP.NET Core, which makes this relatively clean to implement.

The best place to add headers is in your middleware pipeline.

Basic Example (Program.cs)

var app = builder.Build();

app.Use(async (context, next) =>
{
    context.Response.Headers.Add("X-Content-Type-Options", "nosniff");
    context.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
    context.Response.Headers.Add("X-XSS-Protection", "0");
    context.Response.Headers.Add("Referrer-Policy", "strict-origin-when-cross-origin");
    context.Response.Headers.Add("Permissions-Policy", "geolocation=(), microphone=()");
    context.Response.Headers.Add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");

    await next();
});

app.Run();

What About CSP?

CSP is more complex - and that’s where most people get stuck.

Start simple. Then tighten over time.

context.Response.Headers.Add("Content-Security-Policy",
    "default-src 'self'; img-src 'self' https:; script-src 'self' 'unsafe-inline'");

Note: 'unsafe-inline' is not ideal long-term - but it’s often needed initially for Umbraco backoffice and scripts.

Keeping Your Headers Updated (This Bit Matters)

Setting headers once is not enough.

Your site changes. Dependencies change. Browsers change.

Here’s how to stay on top of it:

1. Automate Scans with Pulse for Umbraco

The easiest way to ensure your headers never slip is to monitor them directly inside your CMS. Pulse for Umbraco automatically scans your site for missing or misconfigured security headers (along with dozens of other health metrics) right from the Umbraco backoffice. It’s the best way to catch regressions before they become a live issue.

2. Test Regularly

Use tools like:

  • securityheaders.com
  • Mozilla Observatory

Run checks after every deployment.

3. Review CSP When Adding Features

Adding:

  • Google Tag Manager
  • Embedded videos
  • Third-party scripts

…will likely require CSP updates.

4. Avoid “Set and Forget”

Headers like CSP should evolve as your site becomes more secure.

A good goal:

  • Start permissive
  • Gradually lock things down

5. Use Report-Only Mode (Advanced Tip)

Before enforcing CSP fully:

Content-Security-Policy-Report-Only: ...

This lets you see what would break - without actually breaking it.

Common Mistakes to Avoid

  • Copy-pasting a CSP from another site
  • Locking CSP too tightly and breaking Umbraco backoffice
  • Forgetting to update headers when adding scripts
  • Ignoring warnings from security scans

Final Thought

Security headers aren’t flashy.

No one visiting your site will ever say:

Wow, great Strict-Transport-Security header.

But they will trust your site more - because it behaves safely, predictably, and responsibly.

And in a world where security matters more than ever, that’s a big win.