Skip to content
websitecontrole.nl
Advice

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.

.htaccess (Apache), the server route if you skip the plugin
<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:

  1. 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.
  2. Collect the reports with a tool like Sentry, Report URI or URIports, or read them in your browser console.
  3. Allow what is legitimately blocked, until no real violations come in.
  4. Rename the header to Content-Security-Policy to enforce the policy.

Cookies (Secure, HttpOnly, SameSite)

The Secure flag comes in via "Force HTTPS" in System > Global Configuration (Server tab): it sets the session cookies to secure once HTTPS is enforced. HttpOnly is on for the Joomla session cookie. SameSite has been configurable in Global Configuration since Joomla 4 (values such as Lax or None; None only works together with Force HTTPS).

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.

Go deeper

Advice for another platform