DEV Community

Raji Sherifdeen ayinla
Raji Sherifdeen ayinla

Posted on

The Day "Standard Security" Wasn't Enough: A Deep Dive into HTTP Headers

While I was working on a recent repository, I found that the code had a massive setup for helmet.js.

Before I was onboarded on this project, I used to just call app.use(helmet()) and call it a day. I treated it like a "black box"—a magic spell that made my app secure. But this project used a deep configuration object that I didn't understand.

My curiosity got the best of me. I realized that by using the default settings, I was only scratching the surface of web security. After hours of research and testing, I’ve broken down the advanced headers you actually need to know.


1. The "Traffic Controller": Content Security Policy (CSP)

The default Helmet CSP is a good start, but it often breaks modern front-ends (like those using inline styles or Google Fonts).

The Concept: CSP tells the browser exactly which sources of content (scripts, CSS, images) are trusted. If a hacker tries to inject a script from malicious-site.com, the browser will simply refuse to run it.

Practical Example:
If your app uses Google Fonts and a specific API, a "standard" setup won't cut it. You need a granular policy:

app.use(
  helmet.contentSecurityPolicy({
    directives: {
      "default-src": ["'self'"],
      "script-src": ["'self'", "trusted-scripts.com"],
      "style-src": ["'self'", "fonts.googleapis.com"],
      "img-src": ["'self'", "data:", "images.com"],
      "connect-src": ["'self'", "api.example.com"],
      "upgrade-insecure-requests": [],
    },
  })
);

Enter fullscreen mode Exit fullscreen mode

2. The "Strict Parent": HSTS (Strict Transport Security)

You might think redirecting HTTP to HTTPS is enough. It’s not. There is a small window called a Man-in-the-Middle (MitM) attack where a hacker can intercept the request before the redirect happens.

The Concept: HSTS tells the browser: "Don't even try to use HTTP for the next year. Only talk to me via HTTPS."

Practical Example:

app.use(
  helmet.hsts({
    maxAge: 31536000, // 1 year in seconds
    includeSubDomains: true, // Apply to all subdomains
    preload: true, // Request to be included in browser HSTS preload lists
  })
);

Enter fullscreen mode Exit fullscreen mode

3. The "Privacy Guard": Referrer-Policy

When a user clicks a link on your site that leads to another website, your URL is often sent in the "Referer" header. If your URL contains sensitive data (like /reset-password?token=123), that external site now has your token.

The Concept: This header controls how much information is shared when a user leaves your site.

Practical Example:

// Only sends the origin (domain) rather than the full URL when moving to another site
app.use(helmet.referrerPolicy({ policy: "strict-origin-when-cross-origin" }));

Enter fullscreen mode Exit fullscreen mode

4. The "Feature Lockdown": Permissions-Policy

This is one of the newer, more powerful headers. It’s formerly known as Feature-Policy.

The Concept: It allows you to disable browser features that your site doesn't need. If your site doesn't use the camera or microphone, why leave them accessible? If a malicious script ever runs on your site, this header ensures it can't turn on the user's webcam.

Practical Example:

// Note: In newer versions of Helmet, this is often set manually
app.use((req, res, next) => {
  res.setHeader(
    "Permissions-Policy",
    "camera=(), microphone=(), geolocation=()"
  );
  next();
});

Enter fullscreen mode Exit fullscreen mode

5. The "Anti-Sniffer": X-Content-Type-Options

Browsers try to be "smart" by guessing the file type (MIME type) of a file. If a user uploads a text file containing JavaScript code, a browser might try to execute it as a script.

The Concept: Setting this to nosniff forces the browser to stick to the Content-Type sent by the server. If the server says it's an image, the browser treats it as an image—period.

Practical Example:

// This is included in default helmet(), but vital to understand
app.use(helmet.noSniff());

Enter fullscreen mode Exit fullscreen mode

Summary Table: What does what?

Header Purpose Real-world Analogy
CSP Controls where scripts/styles come from A guest list for a private party
HSTS Forces HTTPS Only opening the door for armored trucks
Referrer-Policy Hides your URL from other sites Using a shredder on sensitive documents
Permissions-Policy Disables hardware (Camera/Mic) Disabling the mic on a laptop you don't use

Conclusion

Switching from app.use(helmet()) to a custom configuration was a turning point in my career. It moved me from "coding things that work" to "engineering things that are secure."

The next time you start a project, don't just copy-paste your security middleware. Take 10 minutes to define exactly what your app needs. Your users will thank you.

How do you handle security headers in your apps? Do you use a library or set them manually? Let's chat in the comments!

Top comments (0)