Tuning the security of a Drupal site
Drupal gives you full control: you set repairs on the web server, through a module, or in the markup. Two headers, incidentally, core already sets itself. Below, per finding, where the fix lives, with code you can copy.
Response headers (HSTS, referrer, permissions)
Two of the headers the check expects are set by Drupal core already: X-Content-Type-Options in the shipped .htaccess, and X-Frame-Options: SAMEORIGIN on every response (since Drupal 8). So they may be green without any action, and you should not set them again (that gives a duplicate header). The rest (HSTS, Referrer-Policy, Permissions-Policy) you add yourself: in the .htaccess, in the nginx config, or through the contributed Security Kit module (seckit), which enables them from an admin screen. HSTS only takes effect over HTTPS.
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Header always set Permissions-Policy "geolocation=(), camera=(), microphone=()"
</IfModule>The Content-Security-Policy
A CSP is harder on Drupal because core and many modules inject inline scripts and styles. There is a contributed module, Content-Security-Policy (machine name csp), that generates the header and adds sources for your own libraries automatically; enabled, it provides a report-only header that logs violations without blocking. Security Kit can also set a CSP.
- Set the policy in report-only first (the Content-Security-Policy-Report-Only header), so it breaks nothing but still reports what it would block.
- Collect the reports with a tool like Sentry, Report URI or URIports, or read them in your browser console.
- Allow what is legitimately blocked, until no real violations come in.
- Rename the header to Content-Security-Policy to enforce the policy.
In the page (SRI, trackers after consent)
An integrity hash on an external script goes in the theme layer: in the Twig template that emits the script, or through a libraries.yml definition. Core has no UI for this; contributed modules such as External Script SRI manage it (an example, not a recommendation). Loading trackers only after consent is handled by a consent module, EU Cookie Compliance or Klaro say, which blocks third-party scripts until consent.
The connection (forcing HTTPS)
Drupal's default .htaccess contains a ready-made, commented-out HTTPS redirect block. Remove the # to send all HTTP traffic to HTTPS. On nginx you configure the redirect in the server config. Behind a CDN or load balancer this is often better done at that level.
# Uncomment the next two lines to redirect to HTTPS.
RewriteCond %{ENV:HTTPS} !on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]DNS records (SPF, DMARC, DNSSEC, IPv6)
These sit apart from your site: they are records at your domain. Set them with your DNS provider or domain registrar, not in your CMS or on your host. SPF and DMARC govern who may send mail as your domain, DNSSEC signs your DNS, and IPv6 is the one where the host has to offer an AAAA record and a connection before you can add it.