HTTP Security Headers for Business Websites: What Each One Does and How to Set It Safely

15 min read 2,867 words
Security Headers for Business Websites: The HTTP Headers That Make a Real Difference featured image

HTTP security headers tell a visitor's browser how to handle certain risks on your website. For a small business site, start with correct content types, a deliberate referrer policy and protection against unwanted framing; add HTTPS-only behaviour and restrictions on browser features after checking what the site uses. A header that looks strict on a scanner can still break a booking form, embedded widget or login flow.

Which HTTP security headers does a small business website need?

For a typical public business website, review X-Content-Type-Options, Referrer-Policy, protection against framing, and Strict-Transport-Security first. A suitable Permissions-Policy can restrict unused browser capabilities. A content security policy deserves its own staged review, particularly on sites using third-party scripts. This is a starting list, not a set of values to paste without checking the site.

  • X-Content-Type-Options: use nosniff once scripts, stylesheets and other resources have correct Content-Type responses. This follows MDN's MIME verification guidance.
  • Referrer-Policy: consider strict-origin-when-cross-origin, the documented browser default, if the site's referral and privacy requirements allow it. See MDN's referrer policy reference.
  • Frame protection: use an enforced frame-ancestors rule where appropriate, with X-Frame-Options as a compatible fallback if the rules agree. OWASP's HTTP headers guidance recommends not allowing the page in a frame, so list any legitimate embedding before applying it.
  • Strict-Transport-Security: add HSTS only after HTTPS works on every relevant hostname; start with a short lifetime and expand it after verification. Login cookies still need their own Secure flag; the guide to PHP secure cookies covers it.
  • Permissions-Policy: disable only capabilities that pages and embedded services do not need, such as camera access on an ordinary brochure site.

Security headers are browser instructions, not a replacement for application validation, access control or updates. Their presence also does not prove that the underlying site is secure.

Can security headers break my website?

Yes. nosniff can stop a stylesheet with an incorrect MIME type from loading; framing rules can block a booking widget; a restrictive browser-feature policy can disable camera or location access. HSTS has a different risk: after a browser remembers it, a broken HTTPS connection can prevent that visitor from reaching the site. Test the feature, not just the response header.

A common failure is applying one rule to every response without separating the main website from an embedded booking page or a partner portal. For instance, X-Frame-Options: DENY blocks framing even by the website's own origin, while SAMEORIGIN permits same-origin framing; neither can grant a specific unrelated partner domain. Those behaviours are defined in MDN's X-Frame-Options reference.

Another failure is adding the same header at both the hosting platform and the origin server. The browser then receives conflicting or duplicate instructions, which can be harder to diagnose than an absent header. Before making changes, identify who currently sets each response header and keep a copy of that configuration.

Is X-Frame-Options still needed if I use a content security policy?

Not for browsers that support and enforce a suitable CSP frame-ancestors directive. X-Frame-Options can still be useful as a fallback for older browsers, provided it expresses the same framing decision. A CSP header without frame-ancestors does not replace it, and a report-only policy does not provide the same blocking protection, as the W3C CSP processing model specifies.

The distinction matters: frame-ancestors controls which sites may embed your page, whereas frame-src controls which frames your page may load. MDN's frame-ancestors documentation also confirms that default-src does not supply a fallback for this directive, and that a policy set in an HTML meta element cannot carry it, so send it as a response header. Merely finding the words Content-Security-Policy in response headers therefore answers very little about clickjacking protection.

For pages that should never be framed, Content-Security-Policy: frame-ancestors 'none' and X-Frame-Options: DENY describe compatible restrictions. If your own site embeds a page, the corresponding pair is frame-ancestors 'self' and X-Frame-Options: SAMEORIGIN. If a trusted third party must embed the page, configure its permitted origin in the enforced framing policy and review whether an X-Frame-Options fallback would block that legitimate use. Check with an actual embed before releasing the restriction.

How do I check which HTTP security headers my site sends?

Use the browser's Network panel to inspect the response headers for the exact page, then compare those values with the same URL from a command-line HTTP client. Check the home page, login and booking routes, static resources and an error response: headers can differ by path or response status. Look for missing, duplicated or contradictory values rather than relying on a single score. MDN's HTTP Observatory can scan a public page; treat each item it reports as something to check on the site itself.

In Chrome, open developer tools, select Network, reload the page and select the document request. Under Headers, read Response Headers, not Request Headers. Chrome's Network panel documentation describes this view. Repeat the inspection for a stylesheet, a script, a redirect and a form response. The check succeeds when you can account for the header values on every route you intend to protect.

For a repeatable text view, the current curl manual documents -D for received headers and -o for the response body. The command below suits Linux and macOS shells and reports only the first response, because curl follows a redirect only when given -L. Replace the example domain with your own publicly accessible page:

curl -sS -D - -o /dev/null https://example.com/

This requests the page and writes its response headers to the terminal. Run it separately against the homepage, login and booking URLs. If a page redirects, inspect both the initial URL and the final destination; do not assume a header found on a redirect is also present on the rendered document. A HEAD-only check can miss differences from the normal GET response.

Record the current header names and values before editing anything. If a CDN, hosting dashboard or reverse proxy is involved, compare the public response with what the origin is configured to send.

What the individual headers actually change

Strict-Transport-Security: remember HTTPS

HSTS tells a browser that has received the header over HTTPS to use HTTPS for future visits to that host. It does not fix a missing certificate or the first insecure visit; normal redirects still matter. MDN's HSTS reference says browsers ignore this header on HTTP responses and remember the policy for the number of seconds in max-age.

Start only after checking the website's HTTP-to-HTTPS redirects and certificate validity. If that groundwork is incomplete, the separate guide to HTTPS and TLS for a business website covers it without treating HSTS as a repair for an insecure connection.

For a cautious first release, web.dev's encryption guidance gives max-age=300 as a short test period. MDN notes that each response carrying the header moves the expiry forward again, so the five minutes count from the last response that carried it. Leave out includeSubDomains while inventorying subdomains, and leave out preload during initial rollout. Check the HTTPS document response and revisit the site in a browser after entering an HTTP URL; only lengthen the lifetime when all relevant routes and hostnames work. As MDN explains, reversing HSTS requires a successful HTTPS response with max-age=0; removing the header alone does not clear an existing browser policy.

X-Content-Type-Options: respect the declared file type

With X-Content-Type-Options: nosniff, browsers refuse to treat a stylesheet as CSS or a script as JavaScript if its declared MIME type is incompatible. This helps limit attacks involving mislabeled content, but it exposes broken server mappings too. A script sent as plain text may stop working even though its file extension looks correct.

Inspect Content-Type on real JavaScript and CSS responses before enabling the header. Fix any incorrect types at the source, add nosniff, then reload pages with developer tools open and check the Console and Network panels for blocked resources. If a dependency fails, restore the previous header setting while correcting that resource's type. File upload handling is a different security problem; keep that material with the PHP file upload tutorial, rather than trying to solve it with one browser header.

Referrer-Policy: limit URL information sent elsewhere

The referrer policy controls how much of the current page's address accompanies requests and navigation. With strict-origin-when-cross-origin, a cross-origin HTTPS request normally carries only the origin, not the full path and query string; an HTTPS-to-HTTP downgrade sends no referrer. Same-origin requests can still carry the full address, as MDN's policy behaviour table explains.

This is a reasonable explicit policy for many websites, but it is not a privacy guarantee. Sensitive tokens should not be placed in URLs in the first place. After applying the policy, inspect outbound requests and verify any referral reporting or integrations that rely on that information; revert the policy if a legitimate flow depends on details it no longer receives, then decide whether the dependency should change.

Permissions-Policy: restrict browser capabilities

Permissions-Policy can prevent a document from accessing selected browser capabilities and can also affect embedded frames. The MDN guide documents the empty allowlist syntax, such as camera=(), microphone=() and geolocation=(). These are sensible restrictions only if the affected pages do not need those features. An embedded frame from another origin also needs the feature named in the iframe's allow attribute, so a provider's widget may need its embed code changed as well.

Check what forms, maps, video calls and third-party widgets actually request before disabling anything. Then check the relevant feature in a browser that supports its directive, not merely whether the header appears in the response. MDN notes that support varies by feature, so absence of a visible failure in one browser is not proof of universal enforcement.

Other headers: deploy because of a need, not a checklist

Cross-Origin-Opener-Policy can isolate a browsing context from other windows, but a restrictive value can interfere with popup-based authentication or payment flows. Cross-Origin-Embedder-Policy can block resources that have not opted into cross-origin loading. Google's security headers reference explains both trade-offs. Add either only when the application needs its protection and the affected flows have been tested.

Do not bulk-add legacy headers for a larger scanner score. OWASP advises against using Public-Key-Pins and Expect-CT, and recommends leaving X-XSS-Protection absent or explicitly disabled with 0. A strong full content security policy can mitigate script injection, but it needs a separate inventory, staged rollout and browser checks. Adding a restrictive one without testing belongs on the same list of avoidable mistakes as turning on HSTS preload immediately.

How to set HTTP security headers without losing access

Put headers at the layer that controls the final public response: hosting or CDN rules, the web server, or the application, depending on your setup. On managed hosting without server access, use the host's or CDN's header settings, or ask the host to make the change. Prefer one accountable place for each header. Change only a small group at a time, verify the public response and functional behaviour, and preserve an immediate way to restore the previous configuration.

  • Step 1: Inventory. List the hostnames, externally embedded pages, checkout or booking integrations, login methods and routes that may behave differently. Save the current configuration and a copy of the public response headers. Check that you can reach the host's administration panel or server through a separate path before you change anything.
  • Step 2: Stage the low-risk candidates. Check MIME types, then apply nosniff and the chosen referrer policy in a staging copy or narrowly scoped site rule. Inspect response headers and exercise scripts, CSS, links and forms. If something fails, remove only those new rules and compare behaviour with the saved baseline.
  • Step 3: Restrict framing with evidence. Identify pages intended for embedding, then apply compatible X-Frame-Options and enforced frame-ancestor rules where possible. Check both legitimate embedding and an unwanted cross-site embed. If a partner site can no longer show your page in its frame, revert the framing change before refining the allowed origins. A booking widget from another provider that refuses to load inside your page is controlled by that provider's own headers, which your settings cannot change.
  • Step 4: Add optional feature restrictions. Disable only unused capabilities, checking the actual affected feature and all embedded services in supported browsers. If location or camera access is needed, remove the blocking directive and confirm that the feature works again.
  • Step 5: Roll out HSTS last. After HTTPS checks, use a short max-age on one hostname, remembering that HSTS applies to every path on that host, check the returned header and browser behaviour, and then increase the lifetime in stages. Keep includeSubDomains and preload out until all subdomain dependencies are known. If a fault appears, restore valid HTTPS and serve max-age=0 as described in MDN's HSTS reference; affected browsers must connect securely to receive that reversal. A hostname covered by a parent domain's includeSubDomains policy cannot clear that rule this way, and a preloaded domain needs the preload list removal process.

For an Apache HTTP Server site, the current 2.4 mod_headers documentation defines Header always set for response headers, including error responses. The official release listing identifies 2.4.69 as current on 5 November 2026. After confirming the module is enabled and an existing upstream rule will not conflict, this is a scoped example for ordinary document responses:

Header onsuccess unset X-Content-Type-Options
Header always set X-Content-Type-Options "nosniff"
Header onsuccess unset Referrer-Policy
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header onsuccess unset X-Frame-Options
Header always set X-Frame-Options "SAMEORIGIN"

These values are not a universal site policy. Place them in the appropriate site configuration, not blindly in every virtual host. If another site must embed any of your pages, leave out the two X-Frame-Options lines until those pages have their own framing rule. Apache documents separate onsuccess and always header tables, which is why the matching unset lines matter when an existing value may live in the first table. Before enabling the change, run apachectl configtest, which parses the configuration files, then inspect the resulting responses and check a framed page. Apache reads .htaccess files each time a document is requested, so that test does not cover them; after an .htaccess edit, load a page and check for a server error. If the check fails or a feature breaks, restore the saved directives, reload the known-good configuration and confirm the old headers are back.

For nginx, the official add_header reference describes the always parameter and inheritance rules. Since nginx 1.29.3, the add_header_inherit directive can change those rules, and the current stable 1.30 branch includes it. A header defined at a more specific location can affect what is inherited, so examine the relevant server and location blocks rather than copying the Apache syntax. Stage one change, test the configuration with nginx -t, reload only if valid, and inspect the public response on both successful and error routes. Restore the saved block and reload if headers disappear or a route fails.

What to check after deployment

A response showing the intended headers is only the first half of acceptance. Open the pages that matter commercially: product pages, enquiry forms, booking confirmation, account login and any embedded payments or maps. Check for missing styles, script failures, failed redirects, blocked frames and changed browser permissions. If an error appears, reverse the header change that preceded it before adjusting another setting.

Compare a normal page with a redirect, a missing URL and an asset request. Do the same through the public hostname rather than trusting an origin-only test. Header rules may differ at the CDN, reverse proxy, application or error handler, so a header on the homepage does not demonstrate consistent protection. Keep the before-and-after inventory with the deployment record, along with the exact rollback location.

Do not wait for a scanner to give a favourable score before testing functionality. A policy may be syntactically valid yet unsuitable for how your business site works. The practical success condition is a matching response policy, working user journeys and a recovery procedure that has been documented before release.

Keep the rollout reversible

Start with a saved baseline, make one change, check it in a real browser and keep a way back. If you need help deciding where to apply the rules, ask about website care with security checks; the goal remains working pages with appropriate HTTP security headers.

Frequently Asked Questions

Do I need every HTTP security header on a basic website?
No. Start with the headers relevant to the site's browser behaviour and add restrictive policies only after checking what the pages use. An ordinary contact website needs a different rollout from a booking application with third-party embeds.
Will HTTP security headers stop a website from being hacked?
No. They reduce specific browser risks but do not repair insecure code, missed updates or weak access controls. Nicolaie Cristea provides website care and security checks for WordPress and custom PHP websites, alongside other maintenance work.
Why does my header checker say X-Frame-Options is missing?
It may be absent because an enforced frame-ancestor restriction already governs embedding. Check the actual policy and whether older-browser compatibility matters before adding another rule. A missing header by itself is not proof of an exploitable page.
Can I add HTTP security headers without changing application code?
Often, yes, when the hosting platform or web server lets you change response headers. The right place depends on where the public response is assembled.