DEV Community

Alex Spinov
Alex Spinov

Posted on

5 Security Headers Your Website Is Missing (and How to Add Them in 2 Minutes)

I scanned the top 100 websites on the Tranco list last week. You know how many had all recommended security headers?

Twelve.

The other 88 were missing at least one critical security header that takes 2 minutes to add.

What Are Security Headers?

Security headers are HTTP response headers that tell browsers how to behave when handling your site's content. They prevent XSS, clickjacking, MIME sniffing, and other common attacks.

Here are the 5 most important ones — and how to add each in under 2 minutes.

1. Content-Security-Policy (CSP)

What it prevents: Cross-Site Scripting (XSS), data injection attacks

Missing from: 72% of websites in my scan

Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'
Enter fullscreen mode Exit fullscreen mode

Add it:

Express.js:

const helmet = require('helmet');
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", "data:", "https:"],
  }
}));
Enter fullscreen mode Exit fullscreen mode

Nginx:

add_header Content-Security-Policy "default-src 'self'; script-src 'self'" always;
Enter fullscreen mode Exit fullscreen mode

Next.js (next.config.js):

const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: "default-src 'self'; script-src 'self'"
  }
];

module.exports = {
  async headers() {
    return [{ source: '/:path*', headers: securityHeaders }];
  }
};
Enter fullscreen mode Exit fullscreen mode

2. Strict-Transport-Security (HSTS)

What it prevents: Protocol downgrade attacks, cookie hijacking

Missing from: 34% of HTTPS websites

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Enter fullscreen mode Exit fullscreen mode

This tells browsers to ONLY connect via HTTPS for the next year. Without it, the first request might be HTTP — vulnerable to man-in-the-middle attacks.

Add it:

// Express.js
app.use(helmet.hsts({ maxAge: 31536000, includeSubDomains: true }));
Enter fullscreen mode Exit fullscreen mode
# Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
Enter fullscreen mode Exit fullscreen mode

3. X-Content-Type-Options

What it prevents: MIME type sniffing attacks

Missing from: 41% of websites

X-Content-Type-Options: nosniff
Enter fullscreen mode Exit fullscreen mode

Without this header, browsers might interpret a file as a different MIME type than declared. An attacker could upload a .jpg that's actually JavaScript — and the browser would execute it.

Add it:

app.use(helmet.noSniff());
Enter fullscreen mode Exit fullscreen mode
add_header X-Content-Type-Options "nosniff" always;
Enter fullscreen mode Exit fullscreen mode

4. X-Frame-Options

What it prevents: Clickjacking attacks

Missing from: 38% of websites

X-Frame-Options: DENY
Enter fullscreen mode Exit fullscreen mode

This prevents your site from being embedded in an iframe. Without it, attackers can overlay invisible buttons on your site and trick users into clicking them.

Add it:

app.use(helmet.frameguard({ action: 'deny' }));
Enter fullscreen mode Exit fullscreen mode
add_header X-Frame-Options "DENY" always;
Enter fullscreen mode Exit fullscreen mode

5. Permissions-Policy

What it prevents: Unauthorized access to browser features (camera, microphone, geolocation)

Missing from: 89% of websites

Permissions-Policy: camera=(), microphone=(), geolocation=(), interest-cohort=()
Enter fullscreen mode Exit fullscreen mode

This explicitly disables browser features your site doesn't need. The interest-cohort=() part opts out of Google's FLoC tracking.

Add it:

app.use(helmet.permittedCrossDomainPolicies());
// Or manually:
app.use((req, res, next) => {
  res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
  next();
});
Enter fullscreen mode Exit fullscreen mode

The All-in-One Solution

If you're using Express.js, just install Helmet:

npm install helmet
Enter fullscreen mode Exit fullscreen mode
const helmet = require('helmet');
app.use(helmet());  // Adds ALL security headers with sane defaults
Enter fullscreen mode Exit fullscreen mode

That's it. One line. All 5 headers (and more) are set.

Check Your Site Right Now

# Quick check any website's security headers
curl -sI https://yoursite.com | grep -iE 'content-security|strict-transport|x-content-type|x-frame|permissions-policy'
Enter fullscreen mode Exit fullscreen mode

Or use these free online tools:

  • securityheaders.com
  • observatory.mozilla.org

A Script to Audit Multiple Sites

import requests

def check_security_headers(url):
    headers_to_check = [
        'Content-Security-Policy',
        'Strict-Transport-Security', 
        'X-Content-Type-Options',
        'X-Frame-Options',
        'Permissions-Policy'
    ]

    try:
        resp = requests.head(url, timeout=10, allow_redirects=True)
    except requests.RequestException as e:
        return {"url": url, "error": str(e)}

    results = {}
    for header in headers_to_check:
        results[header] = header in resp.headers

    score = sum(results.values())
    grade = {5: 'A+', 4: 'A', 3: 'B', 2: 'C', 1: 'D', 0: 'F'}

    print(f"{url}: {grade.get(score, 'F')} ({score}/5 headers)")
    for header, present in results.items():
        status = 'OK' if present else 'MISSING'
        print(f"  [{status}] {header}")

    return results

# Check your sites
sites = [
    "https://example.com",
    "https://yourapp.com",
]

for site in sites:
    check_security_headers(site)
    print()
Enter fullscreen mode Exit fullscreen mode

How many security headers does your site have? Run the curl command above and share your score!

I build security tools for developers — check out awesome-devsec-tools for a full toolkit.

Follow for weekly security tips.

Top comments (0)