HTTP security headers are one of the cheapest, highest-leverage hardening steps for any PHP application. They tell the browser how to behave defensively — which scripts to trust, whether to force HTTPS, and how to handle framing and content sniffing. This guide covers the headers that matter and how to set them cleanly from PHP.

Send headers before any output

PHP can only set headers before the response body starts. Put your header logic at the very top of a shared bootstrap file, before any HTML, whitespace, or BOM is emitted. If you see a headers already sent warning, something printed too early.

The core header set

  • Content-Security-Policy — the single most impactful header. Start in report-only mode, watch what breaks, then enforce. A strict policy blocks injected inline scripts, which shuts down most reflected XSS.
  • Strict-Transport-Security (HSTS) — forces HTTPS for future visits. Only send it once you are fully on HTTPS, because it is sticky.
  • X-Content-Type-Options: nosniff — stops browsers from guessing content types and running a text file as script.
  • Referrer-Policy: no-referrer or strict-origin-when-cross-origin — controls how much URL data leaks to other sites.
  • X-Frame-Options: DENY (or a CSP frame-ancestors directive) — prevents clickjacking by blocking your pages from being framed.
  • Permissions-Policy — turns off browser features you never use, such as camera, microphone, and geolocation.

A minimal, defensive starting point

A reasonable baseline sets nosniff, denies framing, sends a conservative referrer policy, disables unused device APIs, and ships a Content-Security-Policy that only allows resources from your own origin. Build the CSP up gradually: every time you add a third-party script, add its origin explicitly rather than loosening the whole policy with a wildcard.

Avoid the common mistakes

Do not use unsafe-inline in your script policy unless you have exhausted the alternatives — it defeats most of the protection CSP offers. Do not set HSTS with a long max-age before HTTPS is stable across every subdomain. And do not rely on a single header: layered headers work together, and gaps in one are often covered by another.

Verify what you actually shipped

Headers configured in code can be overridden by the web server, a proxy, or a CDN. Always test the live response, not just your source. Run your domain through a header scanner to confirm each directive is present and has the value you intended.

Security headers are defensive controls for sites you own or are authorized to harden. Set them, verify them on the live host, and re-check after any server or CDN change.