Case Study

From Grade B to Grade A:
How We Fixed NIS2 Compliance in 30 Minutes

We scanned our own websites with KENSAI and found critical NIS2 compliance gaps. Missing HTTP security headers brought two of our three domains to Grade B (0.786). Here's exactly what was wrong, what NIS2 articles were at risk, and how 30 minutes of Caddy configuration fixed everything.

๐Ÿ“… March 16, 2026 โฑ 6 min read ๐Ÿท NIS2 ยท Compliance ยท Case Study ยท Caddy

The Uncomfortable Truth

We build a security scanning platform. We preach NIS2 compliance to our customers. And then we ran KENSAI on our own domains โ€” and found that two out of three were non-compliant.

No vulnerabilities, no breaches. Just missing HTTP security headers โ€” the kind of silent gap that regulators check first and attackers exploit second. Under the NIS2 Directive (Art. 21, ยง2), these headers aren't optional for regulated entities. They're a baseline hygiene requirement.

Here's what we found, and how fast we fixed it.


The Scan Results

We ran KENSAI's automated NIS2 compliance scan across all three portfolio domains on March 15, 2026:

kensai.app
A
0.997 / 1.000
NIS2 Score
brnz.ai
B
0.786 / 1.000
NIS2 Score
codeforceai.com
B
0.786 / 1.000
NIS2 Score

kensai.app scored near-perfect at 0.997 because it was already running the correct Caddy header configuration. The other two domains โ€” brnz.ai and codeforceai.com โ€” were both at 0.786, each missing the same six critical security headers.


What Was Missing โ€” and Why It Matters for NIS2

Six HTTP response headers were absent from both failing domains. Each one maps directly to NIS2 Directive requirements:

Missing Header Purpose NIS2 Reference Status
Strict-Transport-Security Forces HTTPS, prevents protocol downgrade attacks Art. 21(2)(h) โ€” Secure communications โœ— MISSING
Content-Security-Policy Blocks XSS, code injection, data exfiltration Art. 21(2)(d) โ€” Supply chain security โœ— MISSING
X-Frame-Options Prevents clickjacking / UI redress attacks Art. 21(2)(b) โ€” Incident prevention โœ— MISSING
X-Content-Type-Options Stops MIME-sniffing attacks Art. 21(2)(b) โ€” Basic cyber hygiene โœ— MISSING
Referrer-Policy Controls referrer data leakage to third parties Art. 21(2)(e) โ€” Data security policies โœ— MISSING
Permissions-Policy Restricts browser API access (camera, mic, geolocation) Art. 25 โ€” Security by design โœ— MISSING
NIS2 Implication: Under Article 21(2), regulated entities must implement "appropriate and proportionate technical and organisational measures." Missing HSTS means unencrypted connections are possible. Missing CSP means XSS attacks can exfiltrate data. National competent authorities treat these as evidence of inadequate baseline security.
What Art. 21 and Art. 25 require: Art. 21 mandates risk management measures including policies on network security and access control. Art. 25 introduces "security by design" โ€” meaning security controls must be implemented proactively, not reactively. Missing HTTP security headers fail both requirements.

The Fix: Caddy Header Configuration

We use Caddy as our reverse proxy. The fix was a single, reusable header block that we could apply to all domains. Here's exactly what we added:

# Global header snippet โ€” add to all production sites
(security_headers) {
  header {
    # Force HTTPS โ€” NIS2 Art. 21(2)(h): secure communications
    Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # Block XSS & injection โ€” NIS2 Art. 21(2)(d): supply chain security
    Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://fonts.googleapis.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https:; frame-ancestors 'none'"

    # Prevent clickjacking โ€” NIS2 Art. 21(2)(b)
    X-Frame-Options "DENY"

    # Stop MIME-sniffing โ€” NIS2 Art. 21(2)(b)
    X-Content-Type-Options "nosniff"

    # Limit referrer leakage โ€” NIS2 Art. 21(2)(e)
    Referrer-Policy "strict-origin-when-cross-origin"

    # Restrict browser APIs โ€” NIS2 Art. 25: security by design
    Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), interest-cohort=()"

    # Remove server fingerprinting
    -Server
    -X-Powered-By
  }
}

Then we imported the snippet into each site block:

# brnz.ai
brnz.ai, www.brnz.ai {
  import security_headers
  reverse_proxy localhost:3001
}

# codeforceai.com
codeforceai.com, www.codeforceai.com {
  import security_headers
  reverse_proxy localhost:3002
}

Reload Caddy, verify headers in browser DevTools, done:

caddy reload --config /etc/caddy/Caddyfile

The Timeline: 30 Minutes Start to Finish

T+0:00
Initiated KENSAI scan on all three domains
Automated NIS2 compliance assessment queued and running.
T+0:04
Scan results in โ€” two domains flagged Grade B
brnz.ai and codeforceai.com scored 0.786 each. Six missing headers identified per domain.
T+0:07
Root cause analysis
kensai.app already had the header block. brnz.ai and codeforceai.com were missing it entirely.
T+0:15
Caddy configuration updated
Added the (security_headers) snippet and imported it into both site blocks. Committed to git.
T+0:17
Caddy reloaded โ€” zero downtime
caddy reload applied new headers instantly. No restart required.
T+0:22
Verification via curl
All six headers confirmed present in HTTP response. No regressions observed.
T+0:29
Re-scan completed โ€” Grade A confirmed
Both domains now scoring 0.997. NIS2 compliance restored. Post-mortem documented.

Before vs. After

โš  Before
B
Score: 0.786 / 1.000
  • HSTS missing
  • CSP missing
  • X-Frame-Options missing
  • X-Content-Type-Options missing
  • Referrer-Policy missing
  • Permissions-Policy missing
โœ“ After
A
Score: 0.997 / 1.000
  • HSTS with preload
  • CSP blocking injections
  • X-Frame-Options: DENY
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin
  • Permissions-Policy: locked down
Result: Both brnz.ai and codeforceai.com moved from Grade B (0.786) to Grade A (0.997) in under 30 minutes. The only change: six HTTP headers added via Caddy configuration. No code changes, no deployments, no downtime.

Key Lessons

1. Security headers are NIS2 compliance items, not optional extras

Every NIS2-regulated entity is expected to have basic HTTP security headers in place. Regulators are increasingly running automated scans during audits. A missing Strict-Transport-Security header isn't a minor omission โ€” it's evidence of inadequate risk management under Art. 21(2).

2. Even security companies have gaps

We build compliance tools and still had two Grade B sites. This isn't embarrassing โ€” it's a reminder that configuration drift happens. A domain launched quickly, a Caddyfile that wasn't synced, a missing import. Automated scanning catches what manual audits miss.

3. The fix is almost always simple

Six missing headers. Thirty lines of Caddy configuration. Thirty minutes of work. The gap between Grade B and Grade A is not a month-long project โ€” it's an afternoon. The hard part is knowing the gap exists in the first place.

4. Reusable snippets prevent future drift

By extracting the headers into a (security_headers) snippet, any new domain we add automatically gets full NIS2 header coverage by including a single import line. Compliance becomes the default, not an afterthought.


What This Means for Your Organisation

If you're under NIS2 โ€” or preparing for an audit โ€” your HTTP security headers are one of the first things regulators check. They're fast to scan, unambiguous to evaluate, and trivial to fix. There's no excuse for a Grade B when a Grade A takes 30 minutes.

Run the scan. Read the report. Fix what's flagged. Re-scan to confirm.

Check Your NIS2 Compliance โ€” Free

Run the same scan we ran on our own domains. Get your NIS2 compliance grade, see exactly which headers are missing, and get a remediation report you can act on immediately.

Start Free NIS2 Scan โ†’

๐Ÿ›ก๏ธ Is your website secure?

Discover vulnerabilities before attackers do.

Scan your website for free โ†’

Related Articles

March 2 Security Briefing: Ransomware Gangs Target March 2026 Security Regulations Update: NIS2 CISA Emergency Directive on Cisco SD-WAN