Tuning the security of a Joomla site
Joomla has had a built-in plugin for security headers since version 4, so much of this you can switch on without code. Below, per finding, where the fix lives.
Response headers (HSTS, clickjacking, referrer, permissions)
Since version 4 Joomla ships a core plugin, "System - HTTP Headers". Go to System > Plugins, search "HTTP Headers", open it and set it to Enabled. It sets, among others, X-Frame-Options (default SAMEORIGIN), Referrer-Policy, Cross-Origin-Opener-Policy, HSTS (off by default) and Permissions-Policy. For each header you choose where it applies: the site, the admin, or both. HSTS has its own fields for max-age, subdomains and preload, and only works over HTTPS. X-Content-Type-Options: nosniff is no longer in the plugin but in the shipped htaccess.txt (see below).
To do it on the server instead, Joomla ships an htaccess.txt in the site root. Rename it to .htaccess and add your rules. Do not edit htaccess.txt itself, since an update can overwrite it; your .htaccess is not updated automatically on upgrade.
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
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=()"
</IfModule>The Content-Security-Policy
The same "System - HTTP Headers" plugin can set a Content-Security-Policy, including the report-only variant. With third-party extensions and templates a policy that is too strict breaks things quickly, so roll it out in steps:
- 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)
Joomla has no built-in switch to put an integrity hash on external scripts; you do that in the template layer (or a template override) where the script is loaded, with the integrity and crossorigin attributes. Note the limit of Joomla's privacy tools: the Privacy component and the "Privacy Consent" plugin ask users to agree to your privacy policy, but do not hold back third-party trackers until a visitor consents. Loading trackers only after consent is therefore handled in the template layer or with a separate consent solution.
The connection (forcing HTTPS)
You force HTTPS in System > Global Configuration on the Server tab with "Force HTTPS". The options are None, Administrator Only and Entire Site. Choose Entire Site so every page goes over https; this also sets the session cookies to secure. It requires a valid certificate on the server, which your host or CDN provides, not Joomla. Then optionally enable HSTS so browsers remember https.
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.