Best PracticesJanuary 10, 202410 min read

Essential Security Headers Every Web App Needs

A comprehensive guide to security headers like CSP, HSTS, and X-Frame-Options that protect your users from common attacks.

By zdelab Team

Essential Security Headers Every Web App Needs


Security headers are HTTP response headers that provide an additional layer of protection for your web applications. They help prevent common attacks like cross-site scripting (XSS), clickjacking, and man-in-the-middle attacks. This guide covers the essential security headers every web application should implement.


Why Security Headers Matter


Security headers act as instructions to browsers on how to handle your website's content. They can:

  • Prevent XSS attacks
  • Block clickjacking attempts
  • Enforce secure connections
  • Control resource loading
  • Protect against MIME-type sniffing

  • Critical Security Headers


    1. Content-Security-Policy (CSP)


    CSP helps prevent XSS attacks by controlling which resources the browser is allowed to load.


    Basic Implementation:

    Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';

    Recommended Configuration:

    Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none';

    Key Directives:

  • `default-src`: Fallback for other fetch directives
  • `script-src`: Controls script sources
  • `style-src`: Controls stylesheet sources
  • `img-src`: Controls image sources
  • `connect-src`: Controls fetch, XMLHttpRequest, WebSocket
  • `frame-ancestors`: Prevents clickjacking

  • 2. Strict-Transport-Security (HSTS)


    HSTS forces browsers to use HTTPS connections, preventing protocol downgrade attacks.


    Implementation:

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

    Parameters:

  • `max-age`: Duration in seconds (31536000 = 1 year)
  • `includeSubDomains`: Applies to all subdomains
  • `preload`: Allows inclusion in browser preload lists

  • 3. X-Frame-Options


    Prevents your site from being embedded in frames, protecting against clickjacking.


    Options:

    X-Frame-Options: DENY

    or

    X-Frame-Options: SAMEORIGIN

    Note: Modern CSP frame-ancestors directive is preferred, but X-Frame-Options provides backward compatibility.


    4. X-Content-Type-Options


    Prevents browsers from MIME-sniffing, forcing them to respect declared content types.


    Implementation:

    X-Content-Type-Options: nosniff

    This prevents browsers from interpreting files as different MIME types, reducing the risk of XSS attacks.


    5. Referrer-Policy


    Controls how much referrer information is sent with requests.


    Options:

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

    Common Values:

  • `no-referrer`: Never send referrer
  • `same-origin`: Only send referrer for same-origin requests
  • `strict-origin-when-cross-origin`: Send full URL for same-origin, origin only for cross-origin
  • `no-referrer-when-downgrade`: Default behavior

  • 6. Permissions-Policy (formerly Feature-Policy)


    Controls which browser features and APIs can be used.


    Example:

    Permissions-Policy: geolocation=(), microphone=(), camera=(), payment=(self)

    Common Features:

  • `geolocation`: Location access
  • `camera`: Camera access
  • `microphone`: Microphone access
  • `payment`: Payment API
  • `usb`: USB device access

  • 7. X-XSS-Protection


    Legacy header for older browsers. Modern browsers have built-in XSS protection.


    Implementation:

    X-XSS-Protection: 1; mode=block

    Note: This is less important with proper CSP implementation.


    Implementation Examples


    Nginx Configuration


    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    add_header X-Frame-Options "DENY" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(self)" always;

    Apache Configuration


    Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'none'"
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    Header always set X-Frame-Options "DENY"
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(self)"

    Express.js (Node.js)


    const helmet = require('helmet');
    app.use(helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          scriptSrc: ["'self'"],
          styleSrc: ["'self'", "'unsafe-inline'"],
          imgSrc: ["'self'", "data:", "https:"],
          fontSrc: ["'self'", "data:"],
          connectSrc: ["'self'"],
          frameAncestors: ["'none'"],
        },
      },
      hsts: {
        maxAge: 31536000,
        includeSubDomains: true,
        preload: true
      },
      frameguard: { action: 'deny' },
      noSniff: true,
      referrerPolicy: { policy: 'strict-origin-when-cross-origin' }
    }));

    Testing Your Security Headers


    Use these tools to verify your headers:

  • **Security Headers**: securityheaders.com
  • **Mozilla Observatory**: observatory.mozilla.org
  • **CSP Evaluator**: csp-evaluator.withgoogle.com

  • Common Mistakes


    1. Too Restrictive CSP: Can break legitimate functionality

    2. Missing includeSubDomains: HSTS should cover all subdomains

    3. Incorrect CSP Syntax: Can cause headers to be ignored

    4. Not Testing: Always test headers in staging before production


    Conclusion


    Security headers are a crucial part of web application security. By implementing these headers correctly, you can significantly reduce your application's attack surface and protect your users from common web vulnerabilities.


    Remember to:

  • Start with a permissive CSP and tighten gradually
  • Test thoroughly before deploying
  • Monitor for violations
  • Keep headers updated as your application evolves

  • Stay secure!

    Found this helpful?

    Scan your web application for security vulnerabilities and get actionable insights.