SiteGrader
Back to blog
·9 min read

Website Security Headers Explained: What They Are and Why You Need Them

A plain-English guide to HTTP security headers — HSTS, Content-Security-Policy, X-Frame-Options, and more — including how to add them to your site.

Most website owners focus on SSL certificates and strong passwords. But there is another layer of browser-level security that is almost always overlooked: HTTP security headers. These are response headers your web server sends with every page, instructing the browser to enable specific protections. They cost nothing to implement and can protect your visitors from a range of attacks.

What Are HTTP Security Headers?

When your browser requests a webpage, the server sends back the HTML along with a set of response headers — key-value pairs that carry metadata about the response. Security headers are a subset of these that tell the browser things like: "never load this page inside a frame," "refuse to guess the content type," and "only connect to me over HTTPS for the next year."

Browsers enforce these instructions. An attacker cannot override them from the outside — they are your server's instructions to the browser about how to handle your content.

Strict-Transport-Security (HSTS)

What it does: Tells the browser to only ever connect to your domain over HTTPS, for a specified period of time. Even if a user types http://yoursite.com, the browser will automatically upgrade to HTTPS without making the insecure request.

Example:

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

Why you need it: Without HSTS, an attacker on the same network (e.g., a coffee shop WiFi) can intercept the initial HTTP request before your server issues a redirect to HTTPS — a "SSL stripping" attack. HSTS closes this window entirely.

How to add it: Set this header in your web server config, CDN, or framework. In Next.js, add it to the headers() function in next.config.js. Start with a short max-age (e.g., 86400 for one day) while testing, then increase to 31536000 (one year) once confirmed working.

Content-Security-Policy (CSP)

What it does: Defines exactly which sources of content (scripts, styles, images, fonts, iframes) the browser is allowed to load. Any resource not on the approved list is blocked.

Example:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; img-src 'self' data: https:;

Why you need it: CSP is the primary defense against Cross-Site Scripting (XSS) attacks. If an attacker manages to inject malicious JavaScript into your page (via a form field, a compromised third-party library, or a database injection), CSP can prevent that script from executing or exfiltrating data.

How to add it: CSP is the most complex security header to configure correctly because it requires you to explicitly whitelist every source your site legitimately uses. Start with a report-only mode using Content-Security-Policy-Report-Only to identify what your site loads before enforcing. Use Google's CSP Evaluator to test your policy.

X-Frame-Options

What it does: Controls whether your page can be embedded inside an <iframe> on another website.

Example:

X-Frame-Options: SAMEORIGIN

Options:

  • DENY — Never allow framing from any origin
  • SAMEORIGIN — Allow framing only from your own domain

Why you need it: Without this header, an attacker can load your site invisibly inside a transparent iframe on their malicious page. They then trick users into clicking buttons on your site while thinking they are clicking something on the attacker's page — a "clickjacking" attack. For example, if your site has a "Delete account" button, a clickjacking attack could trick logged-in users into clicking it.

How to add it: X-Frame-Options: SAMEORIGIN is safe for virtually all sites and takes one line to add. Note: CSP's frame-ancestors directive supersedes X-Frame-Options in modern browsers, but X-Frame-Options provides fallback coverage for older browsers.

X-Content-Type-Options

What it does: Tells the browser not to "sniff" or guess the MIME type of a response — it must use the declared Content-Type header.

Example:

X-Content-Type-Options: nosniff

Why you need it: Old browsers would try to detect a file's type by examining its content, even if the server declared a different type. An attacker could upload a file disguised as a harmless image that the browser would execute as JavaScript. nosniff tells the browser to trust the declared content type and never execute content as a different type.

How to add it: This is one of the easiest security headers to add. It has no configuration options — just add the header and you are protected.

Permissions-Policy (formerly Feature-Policy)

What it does: Controls which browser APIs and features your page is allowed to use — camera, microphone, geolocation, payment, fullscreen, etc.

Example:

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

Why you need it: If your site embeds third-party scripts or iframes (analytics, ads, widgets), those third parties could potentially access powerful browser APIs unless you explicitly restrict them. A compromised third-party script requesting your users' microphone is an attack you want to prevent at the policy level.

Referrer-Policy

What it does: Controls how much information is included in the Referer header when a user clicks a link from your site to an external site.

Example:

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

Why you need it: If your URLs contain sensitive information (user IDs, session tokens, search queries), the full URL should not be leaked to external sites via the Referer header. strict-origin-when-cross-origin is the browser default in most modern browsers, but setting it explicitly ensures consistent behavior.

How to Add Security Headers in Next.js

In your next.config.js (or next.config.ts):

const securityHeaders = [
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'X-Frame-Options', value: 'SAMEORIGIN' },
  { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' },
  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
  { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
]

module.exports = {
  async headers() {
    return [{ source: '/(.*)', headers: securityHeaders }]
  },
}

Check Your Headers Now

Not sure which security headers your site is currently sending (or missing)? Run a free SiteGrader analysis on your URL. The security module checks for all the headers above, shows you exactly which ones are missing, and explains what each one does — with no technical knowledge required.

Ready to grade your website?

Run a free SiteGrader analysis in seconds — no account required.

Check my website free