Guides Create account 🇬🇧 🇩🇪
  • Guides
  • Create account
  • Sign in
  • 🇬🇧 🇩🇪
  • Server recipe

    Security headers in Apache

    A block you can paste into .htaccess or a virtual host, an explanation of each directive, and the difference between set and always that decides whether your error pages are protected.

    Check my headers All security headers

    01 Where it goes

    Virtual host if you can, .htaccess if you must

    On a server you control, the directives belong in the virtual host configuration: Apache reads it once at startup instead of checking every directory on every request. On shared hosting you rarely have that option, and .htaccess in your document root does the same job at a small cost in performance.

    Either way, mod_headers has to be enabled or the directives do nothing at all. Wrapping them in an IfModule block, as below, means a server without the module serves your site unprotected instead of returning a 500 error, which is the safer failure but also the quieter one: check that the module is actually loaded rather than assuming it.

    02 The recipe

    Paste this into .htaccess

    A safe starting point for a site that serves its own assets. The same block works unchanged inside a VirtualHost section, which is where it belongs if you have access to it.

    <IfModule mod_headers.c>     Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
        Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'"
        Header always set X-Frame-Options "DENY"
        Header always set X-Content-Type-Options "nosniff"
        Header always set Referrer-Policy "strict-origin-when-cross-origin"
        Header always set Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=()" </IfModule>

    Two adjustments before this is yours. The Content-Security-Policy allows nothing from third parties, so embedded maps, fonts or analytics will be blocked and it should go out in report-only mode first. And leave preload out of the HSTS header until you have read what it commits you to.

    03 Line by line

    What each directive does

    Every line from the block above on its own, so you can leave out what does not fit rather than pasting something you cannot explain.

    Strict-Transport-Security

    What it does. Commits browsers to reaching your site over HTTPS only, for two years. Do not add it until HTTPS works on every subdomain, because includeSubDomains covers hosts you may have forgotten.

    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains"
    Read the full guide

    Content-Security-Policy

    What it does. Restricts every resource to your own origin and forbids framing. This is the line most likely to break a real site, and the only one worth rolling out in report-only mode first.

    Header always set Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'"
    Header always set Content-Security-Policy-Report-Only "default-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; report-uri /csp-report"
    Read the full guide

    X-Frame-Options

    What it does. Stops your pages being embedded anywhere. Use SAMEORIGIN if part of your own site frames another part.

    Header always set X-Frame-Options "DENY"
    Read the full guide

    X-Content-Type-Options

    What it does. Tells the browser to trust your declared content types rather than guessing at them. There is no site where this value is wrong.

    Header always set X-Content-Type-Options "nosniff"

    Referrer-Policy

    What it does. Sends the full URL within your own site and only the bare domain to anyone else. The right value for almost every public website.

    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Read the full guide

    Permissions-Policy

    What it does. Switches off camera, microphone, geolocation, payment and USB access for your pages and anything they embed. Extend the list rather than shortening it: a feature you do not name stays available.

    Header always set Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=()"
    Read the full guide

    04 Verifying it

    Enable the module, then read the response

    Turning on mod_headers and testing the configuration only proves Apache will start. Whether a header reaches a browser is a separate question, and on shared hosting it is the only one you can answer, because you cannot see the server configuration above your .htaccess.

    a2enmod headers && apachectl configtest && systemctl reload apache2 curl -sI https://example.com | grep -i "^strict-transport\|^content-security\|^x-frame"

    Check an error page as well as the front page. Header set applies to successful responses only; the always condition used in the block above is what carries the headers onto 404 and 5xx responses.

    Check my headers with a free scan

    05 Questions

    What people ask about Apache headers

    I pasted the block and nothing changed. What now?

    Nine times out of ten mod_headers is not enabled, and because the directives sit inside an IfModule block Apache skips them silently rather than complaining. On your own server, a2enmod headers and a reload fixes it; on shared hosting it is a support ticket.

    What is the difference between Header set and Header always set?

    set applies to successful responses only. always applies to error responses too, and those are exactly the pages you least want served without protection. Use always unless you have a specific reason not to.

    Does .htaccess slow the site down?

    Measurably but usually not noticeably: Apache checks for the file in every directory along the path on every request. If you can put the directives in the virtual host instead and switch AllowOverride off, do that. For a typical site on shared hosting the difference is not worth losing sleep over.

    My application already sends some of these headers. Do they conflict?

    They can end up duplicated, and browsers do not treat duplicates consistently. Pick one layer, the server or the application, and switch the other off. Header always unset followed by Header always set is the way to force the server to win.

    06 In depth

    The headers in detail

    This page is the configuration. These guides are what each header actually does and how to choose its value.

    HTTP security headers

    What each response header does, which ones your site should send and how to configure them correctly.

    Read guide

    Content Security Policy

    The strongest defence against cross-site scripting: which directives to set, how to roll a policy out in report-only mode and how to reach an enforcing policy without breaking your site.

    Read guide

    HSTS

    Strict-Transport-Security explained: what max-age, includeSubDomains and preload actually do, and why the preload list is a decision you cannot quickly undo.

    Read guide

    Cookie security

    Secure, HttpOnly and SameSite: the cookie attributes that keep a session out of reach of scripts and cross-site requests, with the settings for a typical stack.

    Read guide

    Clickjacking and X-Frame-Options

    How an invisible overlay turns a visitor click into an action on your site, and the two headers that stop it: X-Frame-Options for older clients, frame-ancestors for everything else.

    Read guide

    Referrer-Policy

    Which part of your URLs travels to other sites when a visitor clicks away: the five policy values that matter, what each one gives up and the one to set by default.

    Read guide

    Permissions-Policy

    Switch off camera, microphone, geolocation and payment for your site and everything it embeds: the allow-list syntax, and why a feature you did not name is not a feature you turned off.

    Read guide

    SPF, DMARC and DNSSEC

    The records that stop someone sending mail in your name, and the one that keeps your DNS answers honest: what each does and the order to introduce them in.

    Read guide

    security.txt

    The file that tells a researcher where to report a vulnerability. Two mandatory fields, ten minutes of work, and the difference between a private report and a public one.

    Read guide

    Monitoring security headers

    A header that is right today can be gone after the next deploy. Three ways to notice: a cron job with curl, a check in your CI pipeline and a scheduled scan, with what each of them catches and misses.

    Read guide

    Detecting CSP changes

    How a policy changes quietly through deploys, plugins and CDN rules, and how to catch it: a header diff, violation reports through report-to, and what a scan comparison can and cannot show.

    Read guide

    Keep reading

    Every guide stands on its own, and together they cover what our scanner looks at. Pick the one closest to your next question.

    Is your website affected? BFSG check Accessibility statement BFSG for online shops BFSG for medical practices BFSG for hotels BFSG for tradespeople HTTP security headers Privacy signals Content Security Policy HSTS Cookie security Clickjacking and X-Frame-Options Referrer-Policy Permissions-Policy SPF, DMARC and DNSSEC security.txt Monitoring security headers Detecting CSP changes Security headers in nginx Security headers in WordPress Security headers in IIS Security headers in TYPO3 Security headers in Shopware 6 Security headers in Plesk All guides

    See which headers Apache is really sending

    Our free Quickscan reads the headers from your live response rather than from your configuration file, and explains every finding in plain language.

    Scan a website Latest scans
    Recent scans Guides Local leagues Pricing Methodology For hosters API Data protection Imprint Accessibility Terms Cancel contracts here © 2026 Erseni