EnglishEN EspañolES FrançaisFR DeutschDE ItalianoIT PortuguêsPT 中文ZH 日本語JA 한국어KO РусскийRU NederlandsNL
← Back to all articles

Security Headers for Small-Business Websites: CSP, HSTS and the Rest, With Copy-Paste Config

A small-business website needs six response headers: Strict-Transport-Security, Content-Security-Policy, X-Content-Type-Options, Referrer-Policy, Permissions-Policy and Cross-Origin-Opener-Policy, with X-Frame-Options kept only as a fallback for old browsers. Three headers that still appear in old guides, X-XSS-Protection, Expect-CT and Public-Key-Pins, are deprecated or removed and should go. This guide explains each header in plain terms, gives copy-paste configuration for Apache .htaccess, Nginx and Cloudflare, shows how to roll out a Content Security Policy without breaking your analytics or checkout, and answers whether any of it affects SEO.

Key facts

  • 31536000 — Seconds (one year): the minimum HSTS max-age for the preload list
  • Report-Only — The CSP mode to start in, so nothing breaks while you test
  • 3 to drop — X-XSS-Protection, Expect-CT and Public-Key-Pins
  • 2014 — Year Google made HTTPS a ranking signal; headers themselves are not one

The headers that matter, and what each one does

Security headers are instructions your server sends with each page that tell the browser to switch on protections it would not use by default. They cost nothing, take minutes to add, and close off whole classes of attack: protocol downgrades, clickjacking, content-type confusion and many cross-site scripting (XSS) payloads.

  • Strict-Transport-Security (HSTS): only connect to this site over HTTPS, and do not let the visitor click through certificate errors.
  • Content-Security-Policy (CSP): only load scripts, styles, frames and other resources from the sources listed. The main defence against XSS and injected skimming scripts.
  • X-Content-Type-Options: nosniff: trust the declared Content-Type; block scripts and stylesheets served with the wrong type.
  • Referrer-Policy: how much of the current URL is sent to other sites when a visitor clicks away.
  • Permissions-Policy: which browser features (camera, microphone, geolocation and so on) the page and its iframes may use.
  • Cross-Origin-Opener-Policy (COOP): whether windows from other sites that your page opens, or that open your page, can keep a reference to it.

Here is a safe baseline that will not break a typical brochure site, booking site or small shop. The CSP line is deliberately minimal; the full policy is built in the CSP section.

Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Cross-Origin-Opener-Policy: same-origin-allow-popups
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'; object-src 'none'; base-uri 'self'

HSTS and the preload list

Strict-Transport-Security tells the browser to use HTTPS for your domain for max-age seconds, upgrading any http:// link automatically (MDN: Strict-Transport-Security). Browsers ignore it when it arrives over plain HTTP, so keep your normal 301 redirect from HTTP to HTTPS as well.

  • max-age=31536000 is one year, the usual production value.
  • includeSubDomains applies the rule to every subdomain. Only add it once every subdomain (mail, shop, staging, an old blog) works over HTTPS.
  • preload is a non-standard directive that signals you want your domain built into browsers’ HSTS lists.

Preload is a one-way door. To be accepted at hstspreload.org, a site must serve a valid certificate, redirect HTTP to HTTPS on the same host, serve all subdomains over HTTPS, and send a header with max-age of at least 31536000, includeSubDomains and preload. The site warns that inclusion “cannot easily be undone” and that removal takes months to reach users. For most small businesses, a one-year HSTS header without preload gives almost all of the benefit with none of the lock-in.

Roll it out in steps: start with max-age=300 (five minutes), check nothing breaks, then raise it to a week, then a year.

Content-Security-Policy without breaking your site

A Content Security Policy lists where each type of resource may load from, and the browser blocks everything else (MDN: Content-Security-Policy). It is the most valuable header and the easiest to get wrong, so roll it out in three stages.

1. Start in Report-Only. Content-Security-Policy-Report-Only applies the policy in “dry run”: nothing is blocked, but violations appear in the browser console and are sent to your reporting endpoint. Here is a starting policy for a site that uses Google Analytics 4 through the Google tag and takes payments with Stripe.js; the hosts come from Google’s tag CSP guide and Stripe’s security guide:

Reporting-Endpoints: csp-endpoint="https://www.example.com/csp-report.php"
Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self' https://www.googletagmanager.com https://js.stripe.com https://*.js.stripe.com; connect-src 'self' https://www.googletagmanager.com https://*.google-analytics.com https://*.google.com https://api.stripe.com; img-src 'self' data: https://www.googletagmanager.com https://*.google-analytics.com https://*.stripe.com; frame-src https://js.stripe.com https://*.js.stripe.com https://hooks.stripe.com; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; report-uri /csp-report.php; report-to csp-endpoint

MDN lists report-to (with a Reporting-Endpoints header) as the modern mechanism and report-uri as deprecated but still supported, so sending both, as above, covers more browsers. Leave it running for a week or two and browse every template yourself: home, contact form, product, cart, checkout, booking.

2. Handle inline scripts with nonces or hashes. The Google tag snippet and most theme code include inline <script> blocks, which a policy without 'unsafe-inline' blocks. MDN’s guidance: use nonces when your server generates each page, and hashes for static pages where it cannot (MDN: CSP guide). A nonce must be different for every response and must not be predictable. In PHP:

<?php
// Generate a fresh, unguessable nonce for every response
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic'; object-src 'none'; base-uri 'none'; frame-ancestors 'self'");
?>
<script nonce="<?= $nonce ?>" src="/js/site.js"></script>
<script nonce="<?= $nonce ?>">
  // inline code, e.g. the Google tag snippet, gets the same nonce
</script>

With 'strict-dynamic', scripts loaded by a nonced script are trusted too, which is how the Google tag can load its own code. Google’s guide says Tag Manager propagates the nonce to scripts it adds. A hash ('sha256-...') is the hash of the exact inline script; change one character and you must recompute it.

3. Enforce. When the reports only show browser extensions and noise, rename the header to Content-Security-Policy. Keep the reporting so you notice when a new plugin or embed starts failing.

frame-ancestors belongs in every policy. It controls which sites may embed your pages in a frame, the defence against clickjacking. frame-ancestors 'none' is similar to X-Frame-Options: DENY; 'self' allows only your own site. It does not fall back to default-src, and it is ignored in a <meta> tag, so it must be sent as an HTTP header.

nosniff, Referrer-Policy, Permissions-Policy and COOP

These four headers are low-risk and can usually go live the same day.

  • X-Content-Type-Options: nosniff. MDN: the browser blocks a script or stylesheet whose MIME type does not match, and stops guessing types for other responses, which prevents uploaded files being run as HTML (MDN). Make sure your server sends correct Content-Type headers for .js and .css files.
  • Referrer-Policy: strict-origin-when-cross-origin. Sends the full URL within your site, only the origin to other HTTPS sites, and nothing to HTTP sites. It is already the browser default when no policy is set (MDN); sending it explicitly protects visitors on older browsers and stops a plugin quietly loosening it. Do not use no-referrer if you want other sites’ analytics to see that traffic came from you.
  • Permissions-Policy. It replaced the old Feature-Policy. The syntax is feature=(allowlist); empty parentheses () disable a feature everywhere (MDN). Disable only what you do not use. A store locator needs geolocation; wallet buttons and embedded video may need other features, so test checkout and embeds after every change. MDN marks it as not Baseline, so browsers that do not support it simply ignore it.
  • Cross-Origin-Opener-Policy: same-origin-allow-popups. COOP isolates your page from windows on other sites. same-origin is the strictest value but breaks flows that rely on a popup talking back to your page; same-origin-allow-popups keeps that working for popups you open, such as a sign-in or payment window (MDN). Skip Cross-Origin-Embedder-Policy: require-corp on a shop: Stripe states it does not currently support cross-origin isolated sites.

X-Frame-Options vs frame-ancestors. X-Frame-Options supports only DENY and SAMEORIGIN (the old ALLOW-FROM is obsolete and ignored), and MDN points to CSP frame-ancestors as the modern, more flexible replacement (MDN). Send frame-ancestors as the real control, and keep X-Frame-Options: SAMEORIGIN alongside it only for old browsers. Make sure the two agree.

Deprecated headers to remove

Old checklists, hosting panels and some CDN presets still add headers that modern browsers ignore or that can cause harm.

  • X-XSS-Protection. MDN marks it deprecated and non-standard, and warns that it “can create XSS vulnerabilities in otherwise safe websites”. Remove it, or if a scanner insists, set it to 0. CSP replaces it (MDN).
  • Expect-CT. Deprecated; Chromium dropped it from version 107 because Certificate Transparency is now enforced by default (MDN).
  • Public-Key-Pins (HPKP). Removed from Chrome in version 72; Google cited very low adoption and the risk of denial of service and hostile pinning (Chrome 72 deprecations). A mistaken pin could lock visitors out of your site, so delete it if you find it.
  • Feature-Policy. Replaced by Permissions-Policy.
  • X-Powered-By and detailed Server headers. Not deprecated, but they advertise your PHP or server version for no benefit.

A Cloudflare-specific note: Cloudflare’s “Add security headers” Managed Transform, according to its reference page, still adds x-xss-protection: 1; mode=block and expect-ct: max-age=86400, enforce along with nosniff, X-Frame-Options: SAMEORIGIN and Referrer-Policy: same-origin (Cloudflare docs). Setting your own headers with a Transform Rule, as below, gives you modern values instead.

Copy-paste config for Apache and Nginx

Apache and LiteSpeed (.htaccess). The Header directive from mod_headers is allowed in .htaccess when the host permits FileInfo overrides, which most shared hosts do. Use always so the headers are also sent on redirects and error pages, not only on successful responses. The HSTS line is limited to HTTPS requests with an expression:

# .htaccess (needs mod_headers; "always" also covers redirects and error pages)
<IfModule mod_headers.c>
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" "expr=%{HTTPS} == 'on'"
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
  Header always set Cross-Origin-Opener-Policy "same-origin-allow-popups"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set Content-Security-Policy "frame-ancestors 'self'; object-src 'none'; base-uri 'self'"

  # Remove legacy headers a host or plugin may add
  Header always unset X-XSS-Protection
  Header always unset Expect-CT
  Header always unset Public-Key-Pins
  Header always unset X-Powered-By
</IfModule>

If a header still does not appear, your host may be setting it elsewhere (a control panel toggle or a PHP header() call); check with curl as shown in the testing section. For a CSP that needs nonces, send it from PHP rather than .htaccess, because a static file cannot generate a fresh nonce per response.

Nginx. Add these to the HTTPS server block. Use the always parameter, otherwise Nginx only adds the headers to a limited set of success and redirect status codes.

# inside the HTTPS server { } block
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
add_header Cross-Origin-Opener-Policy "same-origin-allow-popups" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Content-Security-Policy "frame-ancestors 'self'; object-src 'none'; base-uri 'self'" always;

location /downloads/ {
    # nginx 1.29.3+: keep the server-level headers when adding one here
    add_header_inherit merge;
    add_header Cache-Control "no-store" always;
}

Watch the inheritance trap: an add_header inside a location block makes Nginx drop all add_header directives inherited from the server level for that location. Either repeat the security headers in that location, put them in a file you include, or on Nginx 1.29.3 and later use add_header_inherit merge; as shown.

Cloudflare: Transform Rules and the HSTS setting

If your site sits behind Cloudflare, you can set every header at the edge without touching the server.

  1. HSTS: use the built-in setting under SSL/TLS, Edge Certificates, “HTTP Strict Transport Security (HSTS)”. It offers a max-age of up to 12 months, an “Apply to subdomains” option, a preload option and a No-Sniff option that sends X-Content-Type-Options: nosniff (Cloudflare docs). Cloudflare warns that after enabling HSTS you must not switch the DNS record to DNS-only, pause Cloudflare or disable SSL before the max-age expires, or the site becomes unreachable.
  2. Everything else: go to Rules, Overview, Create rule, and choose Response Header Transform Rule (Cloudflare docs).
  3. Apply it to all incoming requests, or filter by hostname if you have several sites on the zone.
  4. For each header choose Set static and paste the name and value from the baseline above. Set overwrites any value your origin sends, which is what you want here; Remove deletes a header, useful for X-XSS-Protection or X-Powered-By coming from the origin. One rule can modify up to 30 response headers.
  5. Deploy, then test.

Leave the “Add security headers” Managed Transform switched off if you use your own rule, so the two do not fight over the same headers. A nonce-based CSP still has to come from your server or a Cloudflare Worker, because a static rule cannot generate a new nonce for each response.

How to test, and whether headers affect SEO

Test from outside after every change, and recheck after plugin updates and host moves.

  • MDN HTTP Observatory at developer.mozilla.org/en-US/observatory. Mozilla moved the Observatory onto MDN in July 2024 and retired the old observatory.mozilla.org version that October. It grades your HTTP headers and cookies from A+ downwards and explains each failing test. It checks HTTP configuration only; it does not look for vulnerable plugins or outdated software.
  • securityheaders.com, now part of Snyk, grades the same headers on an A+ to F scale. Its free web scanner is still online; reports say the paid API was retired in 2026.
  • Your own terminal and browser. curl shows exactly what the server sends, and the browser console shows CSP violations as they happen:
    curl -sI https://www.example.com/ | grep -iE "strict-transport|content-security|x-content-type|referrer-policy|permissions-policy|cross-origin|x-frame"

Do security headers help SEO? Not directly. Google announced HTTPS as a ranking signal in August 2014 and described it as lightweight; HSTS supports that by keeping every visit on HTTPS. Google’s page experience documentation asks whether pages are served securely, but says there is no single page experience signal, and it does not mention security headers at all (Google: page experience). CSP, COOP, Referrer-Policy and the rest are not ranking factors.

They matter to a business for other reasons: a strict CSP is one of the few defences against injected card-skimming scripts on checkout pages, and a hacked site that serves spam or malware will lose rankings far faster than any header could gain them. The real SEO risk runs the other way: a CSP that blocks your analytics, consent banner or checkout script costs data and sales, which is why the Report-Only stage is not optional. If you also run agent-facing endpoints such as an MCP server, see the MCP server security checklist.

Product facts: the AI Indexing Pack

Stated plainly, so it can be checked against the product page:

  • What it is: the AI Indexing Pack is a set of files you upload to your own website. It is sold by Built2WinWeb as a one-time purchase, with no subscription.
  • What it ships: (1) a working MCP server using the Streamable HTTP transport, with typed output schemas and tool annotations; (2) Markdown content negotiation, so agents can request a clean text version of each page; (3) llms.txt, agents.txt and agent cards; (4) the full /.well-known/ discovery tree; (5) OAuth protected-resource metadata; (6) bot access control that verifies crawler identity; (7) a README with a verification checklist.
  • Requirements: PHP 7.4 or newer, Apache or LiteSpeed with .htaccess support, FTP access, and a domain you control. Installation takes about 30 minutes.
  • What it is not: it is not a WordPress plugin (that is the separate WordPress edition, which needs WordPress 6.0+ and PHP 8.0+), it does not write content for you, it does not guarantee rankings or citations, and it is not a managed service.
  • Free routes in: sites built on AIWebBuilding get the core built in, and a 50-day streak on FreeAIBoost unlocks the pack for one domain. Current prices are on the product pages.

Related reading

Frequently asked questions

Which security headers should a small-business website use?

Strict-Transport-Security, Content-Security-Policy (with frame-ancestors), X-Content-Type-Options: nosniff, Referrer-Policy, Permissions-Policy and Cross-Origin-Opener-Policy. X-Frame-Options: SAMEORIGIN can be kept as a fallback for old browsers.

Should I still use X-XSS-Protection?

No. MDN marks it deprecated and warns it can create XSS vulnerabilities in otherwise safe sites. Remove it, or set it to 0, and rely on Content-Security-Policy instead.

Should I submit my domain to the HSTS preload list?

Only if every subdomain will support HTTPS for the long term. Preload requires a max-age of at least 31536000 with includeSubDomains and preload, and removal from the list takes months to reach users.

How do I add a Content Security Policy without breaking my site?

Send it as Content-Security-Policy-Report-Only first, collect violation reports while you test every page type, allow the sources you genuinely use, handle inline scripts with nonces or hashes, then switch to the enforcing header.

Do security headers improve Google rankings?

Not directly. HTTPS is a lightweight ranking signal, and HSTS helps keep visits on HTTPS, but CSP and the other security headers are not ranking factors. They protect visitors and the business.

Want your headers set up and tested?

We add and test security headers on custom and WordPress sites, including a Content Security Policy that keeps your analytics and checkout working.

Get a free quote