Add HTTP security headers to WordPress via .htaccess

HTTP security headers are a first line of defence against common web attacks including cross-site scripting (XSS), clickjacking, and MIME-type sniffing. They are set by the web server and instruct browsers how to handle your site’s content, yet most WordPress installations ship with none of them configured. Adding these headers requires a single block in your .htaccess file and zero PHP code, making it one of the fastest security wins available. The X-Frame-Options: DENY header blocks your pages from being embedded in iframes on other domains, preventing clickjacking attacks. X-Content-Type-Options: nosniff stops browsers from guessing the MIME type of a response, which prevents certain content-injection exploits. Referrer-Policy: strict-origin-when-cross-origin controls how much referrer information is sent with navigation requests. The Permissions-Policy header restricts which browser APIs (camera, microphone, geolocation) pages are allowed to use. Strict-Transport-Security (HSTS) forces HTTPS for a configurable time period — only enable this after confirming your SSL certificate renews correctly. The Content-Security-Policy header is the most powerful and the most complex; it defines exactly which origins can load scripts, styles, images, and fonts. For a broader hardening approach, combine these headers with the file-level protections in .htaccess and disabling file editing in the dashboard. Always test header changes on staging using a tool like securityheaders.com before deploying to production.

Problem: Your WordPress site is missing HTTP security headers, leaving it exposed to clickjacking, MIME sniffing, and content injection attacks that a browser could otherwise block automatically.

Solution: Add the following block to your .htaccess file, above the WordPress rewrite rules:

<IfModule mod_headers.c>

    # Prevent clickjacking — block iframe embedding from other origins
    Header always set X-Frame-Options "SAMEORIGIN"

    # Prevent MIME-type sniffing
    Header always set X-Content-Type-Options "nosniff"

    # Control referrer information sent with requests
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # Disable dangerous browser features
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"

    # Force HTTPS for 1 year (only enable after SSL is confirmed stable)
    # Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # Basic Content Security Policy — adjust for your CDN / fonts / scripts
    Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:;"

</IfModule>

NOTE: The Content-Security-Policy directive above uses unsafe-inline for scripts and styles because WordPress core and most page builders require it. For maximum security, move your inline scripts to external files and remove unsafe-inline. HSTS is commented out intentionally — once set, a browser will refuse HTTP connections for the entire max-age duration, even if you need to temporarily disable SSL. Verify your changes at https://securityheaders.com after deploying.