Security Headers

HTTP security headers represent a fundamental and frequently underused layer of defense for modern web applications, working as directives that instruct browsers to enable native protection mechanisms against a wide range of common attack vectors. These HTTP response headers act as a declarative security policy, establishing rules on how the browser should process and render page content, control script execution, manage cookies, and interact with third-party resources. When configured properly, headers such as Content-Security-Policy (CSP), Strict-Transport-Security (HSTS), X-Frame-Options and X-Content-Type-Options create significant barriers against Cross-Site Scripting (XSS), clickjacking, MIME sniffing, protocol downgrade and malicious code injection attacks. Correct implementation of these headers represents one of the most efficient ways to harden web applications, since it requires no changes to the application code and offers immediate protection against entire classes of vulnerabilities. Studies show that the vast majority of websites still do not properly implement these headers, leaving their users exposed to attacks that could be mitigated with relatively simple configurations on the web server. This negligence is especially critical considering that automated security scanning tools assess the presence and proper configuration of these headers as primary indicators of an application's security posture.

Content-Security-Policy (CSP)

CSP prevents XSS by defining a whitelist of trusted sources for scripts, styles, images and other resources.

      # Strict CSP (recommended)
      Content-Security-Policy:
      default-src 'self';
      script-src 'self' 'nonce-';
      style-src 'self' 'nonce-';
      img-src 'self' https: data:;
      font-src 'self';
      connect-src 'self' https://api.example.com;
      frame-ancestors 'none';
      base-uri 'self';
      form-action 'self';
      # CSP with report-only (testing mode)
      Content-Security-Policy-Report-Only:
      default-src 'self';
      report-uri /csp-violation-report
      

Critical CSP Directives

  • default-src: Fallback for other directives
  • script-src: Whitelist for JavaScript execution
  • style-src: Allowed CSS sources
  • frame-ancestors: Prevents clickjacking
  • upgrade-insecure-requests: Force HTTPS

Strict-Transport-Security (HSTS)

HSTS forces browsers to always use HTTPS, preventing downgrade attacks and SSL stripping.

      # HSTS with preload and subdomains
      Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
      # Steps for HSTS preload:
      1. Configure HSTS header with preload
      2. Submit domain at hstspreload.org
      3. Domain will be included in browser preload lists
      

X-Frame-Options

Prevents clickjacking by controlling whether a page can be rendered in a frame/iframe.

      # Deny all framing (most secure)
      X-Frame-Options: DENY
      # Allow only same origin
      X-Frame-Options: SAMEORIGIN
      # Allow specific domain (deprecated - use CSP frame-ancestors)
      X-Frame-Options: ALLOW-FROM https://trusted.com
      

X-Content-Type-Options

Prevents MIME sniffing attacks by forcing the browser to respect the declared Content-Type.

      # Disable MIME sniffing
      X-Content-Type-Options: nosniff
      

Referrer-Policy

Controls how much referrer information is sent in requests.

      # Recommended policy
      Referrer-Policy: strict-origin-when-cross-origin
      # Options:
      # - no-referrer: Never send referrer
      # - no-referrer-when-downgrade: No referrer on HTTPS→HTTP
      # - same-origin: Only send to same origin
      # - strict-origin: Only send origin (no path)
      

Permissions-Policy

Controls which browser features and APIs the page can use (replaces Feature-Policy).

      # Disable dangerous features
      Permissions-Policy:
      geolocation=(),
      microphone=(),
      camera=(),
      payment=(),
      usb=(),
      interest-cohort=()
      

Configuration per Server

Nginx

      # nginx.conf
      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 Content-Security-Policy "default-src 'self';" always;
      add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
      

Apache

      # .htaccess or httpd.conf
      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 Content-Security-Policy "default-src 'self';"
      

Node.js/Express

      // Use Helmet middleware
      const helmet = require('helmet');
      app.use(helmet());
      // Custom configuration
      app.use(helmet.contentSecurityPolicy({
      directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'nonce-'"],
      styleSrc: ["'self'", "'nonce-'"],
      }
      }));
      

Testing Security Headers

  • securityheaders.com: Scan and A+ grade rating
  • Mozilla Observatory: Comprehensive analysis
  • Chrome DevTools: Network tab → Headers
  • curl: curl -I https://example.com

Common Pitfalls

  • Overly permissive CSP with 'unsafe-inline' and 'unsafe-eval'
  • HSTS without includeSubDomains leaves subdomains vulnerable
  • Headers configured but not applied (always flag missing)
  • Testing only the homepage - headers should be on every page

Complete Checklist

  • [OK] Strict Content-Security-Policy configured
  • [OK] HSTS with 1-year max-age + includeSubDomains
  • [OK] X-Frame-Options: DENY or CSP frame-ancestors
  • [OK] X-Content-Type-Options: nosniff
  • [OK] Referrer-Policy configured
  • [OK] Permissions-Policy disabling unused features
  • [OK] Headers tested at securityheaders.com (A+ rating)
  • [OK] CSP tested with report-only mode first