HTTP response headers are a layer of browser-enforced security that most WordPress sites never configure. When a browser receives a response from your server, these headers tell it how to handle the content, whether to allow the page to be embedded in an iframe on another site, whether to sniff MIME types, and what browser features the page is allowed to access. Getting them right is a low-effort, high-impact hardening step that tools like Mozilla Observatory and Security Headers rate your site on.
The Code
WordPress provides the wp_headers filter specifically for adding or modifying HTTP response headers before they’re sent. This is cleaner than using header() directly and integrates properly with WordPress’s response pipeline. Add this to your functions.php or a site-specific plugin.
What Each Header Does
X-Content-Type-Options: nosniff prevents browsers from guessing the MIME type of a response and treating it differently from what the server declared. Without this header, a browser might execute a file uploaded as an image if it detects JavaScript inside it, a classic content injection attack vector.
X-Frame-Options: SAMEORIGIN tells browsers to refuse to render your page inside an <iframe> on any domain other than your own. This closes the door on clickjacking attacks, where a malicious site embeds your page invisibly and tricks users into clicking on hidden UI elements.
Referrer-Policy: strict-origin-when-cross-origin controls how much of the current URL is sent in the Referer header when a user clicks a link to another site. With this policy, the full URL is sent for same-origin navigation, but only the origin (domain without the path) is sent for cross-origin requests. This prevents sensitive URL parameters, like search terms, user IDs, or private page slugs, from leaking to third-party sites.
Permissions-Policy restricts which browser APIs your page can access. The example disables camera, microphone, and geolocation access entirely. If your site has no need for these features, explicitly denying them means a compromised third-party script injected into your page can’t silently request them either. You can extend this list to include payment=(), usb=(), fullscreen=(self), and others depending on your site’s needs.
X-Powered-By removal strips the header PHP adds by default that reveals your server technology stack. This doesn’t add security in the strict sense, but it’s part of reducing information leakage.
What About Content-Security-Policy?
A Content Security Policy header is the most powerful security header available, but it’s intentionally omitted from this snippet because it requires careful, site-specific configuration. An incorrectly set CSP will break your site, blocking inline scripts, third-party embeds, fonts, and analytics. It warrants its own dedicated implementation once you’ve audited all the resource origins your site depends on.
Server-Level vs WordPress-Level
If you have access to your server’s Nginx or Apache configuration, setting headers there is more efficient, they’re sent before PHP runs. However, the WordPress approach works on any host including shared hosting where you can’t modify server config directly, and is easier to manage alongside your other site code.
add_filter( 'wp_headers', function( $headers ) {
// Prevent MIME-type sniffing
$headers['X-Content-Type-Options'] = 'nosniff';
// Block your site from being embedded in iframes on other domains
$headers['X-Frame-Options'] = 'SAMEORIGIN';
// Control how much referrer info is sent with outbound links
$headers['Referrer-Policy'] = 'strict-origin-when-cross-origin';
// Restrict access to browser features
$headers['Permissions-Policy'] = 'camera=(), microphone=(), geolocation=()';
// Remove the X-Powered-By header if PHP sets it
header_remove( 'X-Powered-By' );
return $headers;
} );
