Joomla Security Hardening : 13 Steps for a Production Site [2026]
Thirteen hardening steps ordered by what actually prevents compromise, starting with the one that matters more than all the others.
Most Joomla sites that get compromised are not broken into by anything clever. They are running a version with a published, patched vulnerability, and an automated scanner found them weeks after the fix shipped. That is the shape of the problem, and it means the hardening steps below are not equally valuable — they are ordered here by how much risk each one actually removes, which is not the order you will find them in most checklists.
This runbook assumes Joomla 5.x on a production site you are responsible for. Joomla 4 is superseded and Joomla 3 has been end-of-life since August 2023; if you are on either, the single highest-value security action available to you is a migration, and nothing below substitutes for it.
The order matters more than the list
Every Joomla hardening article contains roughly the same thirteen items. What they rarely say is that the first two are worth more than the remaining eleven combined. If you have one afternoon, spend it on patching discipline and administrator authentication. Doing the fiddly bits — permissions, database prefixes, session tuning — while running a version three point releases behind is organising the deckchairs.
1. Patching: the only step that is not optional
An unpatched Joomla site on the public internet is compromised eventually. Not probably — eventually. The Joomla project publishes security advisories on its developer site, and within hours the details are in scanner signature sets. Attackers do not find your site; they scan address space and version fingerprints continuously and hit whatever answers. The discipline that works is boring:
Subscribe to the source, not to a newsletter
Subscribe to the Joomla project’s security announcements feed directly. Second-hand coverage lags by days, and days are the whole window. Configure System → Update → Update Sites so the core update channel is enabled and reachable — a site whose update check silently fails has no patching discipline regardless of your intentions.
Enable update notification email
Joomla ships a Quick Icon – Joomla! Update Notification plugin. Enable it, set a monitored address, and confirm the site can actually send mail — a notification plugin on a site with broken SMTP is worse than none, because it creates false confidence.
Define an SLA and hold to it
Write it down: core security releases applied within 24 hours; third-party extension security releases within 72 hours; non-security point releases within two weeks. If you cannot meet 24 hours on a given site, that site needs either a staging environment that makes updates cheap, or a maintenance contract with someone who can.
Joomla 5’s update process is genuinely reliable for point releases. The common excuse for delay is fear of breakage, and the honest answer to that fear is a staging copy plus a tested backup, not deferral.
The gap between a public advisory and mass exploitation is routinely measured in hours, not weeks. Core advisories in recent years have covered classes such as unauthorised information disclosure via the web services API, cross-site scripting in administrator views, and access-control bypasses in core components. In each case the fix was a point release available on the day of disclosure. The sites that fell over were the ones that applied it a month later.
2. Administrator accounts and multi-factor authentication
Credential compromise is the second route in, and unlike vulnerability exploitation it leaves almost no trace in your logs beyond a successful login. Joomla 5 ships multi-factor authentication in core — no extension needed — supporting TOTP authenticator apps, WebAuthn hardware keys, email codes and backup codes.
Enable MFA on every Super User
Go to Users → Manage, open each account with Super User or Administrator privileges, and set up Multi-factor Authentication. Prefer WebAuthn where the user has a hardware key; TOTP otherwise. Email codes are the weakest option and should be a fallback, not the primary method — an attacker with the mailbox has both factors.
Make it mandatory, not optional
Under Users → Manage → Options → Multi-factor Authentication, set which user groups are forced into MFA. Set it for Super Users and Administrators at minimum. Optional MFA on a team of five means two people have it.
Audit the Super User list quarterly
Filter Users → Manage by the Super Users group. Every account on that list should map to a named person who still needs it. The developer who built the site three years ago does not. Neither does the agency you left. Disable rather than delete if the account authored content, so authorship attribution survives.
Set a password policy while you are there: Users → Manage → Options lets you require a minimum length. Twelve characters is a reasonable floor — better still, tell your editors to use a password manager and stop caring about composition rules.
3. Remove the default admin username
Automated login attempts against Joomla try admin first, every single time, before anything else. Removing that username does not stop brute-forcing but it does force the attacker to guess two unknowns instead of one, and it removes your site from the cheapest tier of automated attack.
If your site was installed with admin, change it: open the account in Users → Manage and edit the Login Name. It is a live change and existing sessions survive. Do not create a decoy account called admin — you now have a real account with a guessable name, which is the thing you were avoiding.
Do not use the same account for authoring and administration. Create a separate Author or Publisher account for day-to-day content work and reserve the Super User account for administrative tasks. If the authoring account is phished, the blast radius is content, not the whole installation.
4. File and directory permissions, done properly
Permissions get cargo-culted more than any other item on this list. The rules are short:
| Target | Mode | Rationale |
|---|---|---|
| Directories | 755 | Web server traverses and reads; only the owner writes |
| Files | 644 | Web server reads; only the owner writes |
configuration.php |
444 (or 400) | Contains the database password; nothing should write it at runtime |
| Anything at all | Never 777 | Any compromised script anywhere on a shared server can rewrite your site |
With shell access, fix an entire tree in two commands:
find /path/to/site -type d -exec chmod 755 {} \;
find /path/to/site -type f -exec chmod 644 {} \;
chmod 444 /path/to/site/configuration.php
The important diagnostic point: when an upload or extension install fails at 755, the cause is almost never the mode. It is ownership — files uploaded over FTP as one user while PHP runs as another. Loosening to 777 makes the symptom disappear and converts a support ticket into a security exposure. Ask your host to correct ownership instead. Under Global Configuration → Server, leave Force Directory Permissions and Force File Permissions blank unless you have a specific reason.
5. Protect configuration.php
configuration.php holds the database credentials, the site secret and your log and tmp paths. It is the single most valuable file on the installation. Three protections:
Make it read-only
chmod 444 configuration.php. Joomla only writes it when you save Global Configuration, so you will need to loosen it briefly for that and tighten it afterwards. That inconvenience is a feature: an attacker who has achieved file write also cannot rewrite your config to point at their database.
Block direct access at the web server
Joomla’s stock .htaccess already prevents PHP files from being served as text, but an explicit deny costs nothing. In Apache:
<Files "configuration.php">
Require all denied
</Files>
On nginx:
location ~* /configuration\.php$ {
deny all;
return 404;
}
Move log and tmp paths outside the web root
In Global Configuration → Server, set log_path and tmp_path to directories above public_html. A tmp directory inside the web root is a favourite drop point for uploaded webshells, because anything written there is directly requestable.
6. Securing the administrator directory
The /administrator path is the front door, at a location nobody has to guess. Three options, and they stack.
| Method | Strength | Breaks what | Right for |
|---|---|---|---|
| IP allow-list | Very strong | Remote/mobile admin, dynamic IPs | Sites administered from a fixed office or VPN |
| HTTP basic auth | Strong | Nothing, mildly annoying | Almost everyone |
| Administrator URL secret | Weak but free | Bookmarks, some extensions | Reducing log noise |
An IP allow-list in Apache, placed in /administrator/.htaccess:
Require ip 203.0.113.0/24
Require ip 198.51.100.7
HTTP basic auth adds a challenge before Joomla’s PHP runs at all, so brute-force attempts never touch your database. Generate a password file with htpasswd -c /home/account/.htpasswd adminuser, store it outside the web root, and reference it from /administrator/.htaccess:
AuthType Basic
AuthName "Restricted"
AuthUserFile /home/account/.htpasswd
Require valid-user
The administrator URL secret — provided in Joomla 5 through Admin Tools or a comparable extension — appends a required query parameter to the admin URL. It is security through obscurity and I will not pretend otherwise: it reduces automated log noise substantially and stops nothing determined. Use it in addition to, never instead of, one of the first two.
Do not rename the /administrator directory on disk. Joomla’s core and a great many extensions reference that path, and the site will break in ways that are difficult to diagnose. Every legitimate method of hiding the admin login works at the web server or extension layer, never by moving the folder.
7. Remove unused extensions, templates and users
The most-skipped item on this list, and genuinely high value. Every installed extension is code on your server whether or not it is enabled — and disabled is not uninstalled. A disabled component’s files are still present and, depending on the vulnerability class, still reachable.
Inventory what is installed
Go to System → Manage → Extensions. Filter by type. For each third-party item, answer two questions: is it doing anything on this site, and has the developer shipped an update in the past 18 months? A no to either is a candidate for removal.
Uninstall, do not disable
Disabling removes it from the request path but leaves the files. Uninstall properly through System → Manage → Extensions → Uninstall so the files and database tables go with it. Take a backup first — uninstalling a component drops its tables, and if it held content you wanted, that is a one-way trip.
Remove abandoned templates
Old templates bundle their own copies of JavaScript libraries and, in the worst cases, file-upload or image-resize handlers. Delete every template you are not using, including ones shipped with a framework you have since replaced.
The unmaintained-extension question deserves care. An extension untouched for two years is not automatically dangerous — a simple module that outputs static markup may be genuinely finished. What matters is whether it processes input: anything handling uploads, form submissions, database queries with user-supplied parameters, or rendered user content is in the risk category and needs an active maintainer.
8. The database table prefix
Joomla’s installer generates a random three-to-five character prefix, and there is no reason to override it. A predictable prefix such as jos_ makes blind SQL injection meaningfully easier to exploit, because the attacker does not have to discover the table names before extracting the user table.
Changing the prefix on a live site is possible but not free — every table is renamed, and several core tables store the prefix inside serialised configuration values, so a careless job breaks the site. If yours is already random, do nothing. If it is jos_ because it came from Joomla 3, treat it as a low-priority migration item, not an emergency.
9. Session and cookie behaviour
Session settings sit under System → Global Configuration → System, and the defaults are reasonable but not optimal for a site with real administrative privilege.
| Setting | Default | Production recommendation |
|---|---|---|
| Session Lifetime | 15 minutes | 30 for admin comfort; 15 if handling sensitive data |
| Session Handler | Database | Database on shared hosting; Redis/Memcached if available |
| Shared Sessions | No | No — keep site and admin sessions separate |
| Force HTTPS | None | Entire Site |
| Cookie Domain / Path | Blank | Leave blank unless running a multi-site setup |
Shared Sessions deserves the emphasis. Setting it to Yes means a front-end login and an administrator login share one session record. It exists for specific integration scenarios and it widens session-fixation and session-hijacking impact considerably. Leave it off.
Behind a reverse proxy or CDN, confirm Joomla sees real client addresses rather than the proxy’s. Otherwise every login appears to come from one IP, rate limiting becomes useless, and your IP allow-list either blocks everyone or nobody.
10. HTTPS and HSTS
Certificates are free and universal, so there is no remaining argument for plaintext. Set Force HTTPS to Entire Site in Global Configuration → Server — Administrator Only is a half-measure that leaves front-end logins in the clear. In configuration.php that is public $force_ssl = '2';. Then add HSTS at the web server so browsers refuse plaintext without asking:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Once a browser has seen that header it will refuse plaintext HTTP to your domain for the full max-age, and there is no way to reach out and cancel it. Test with a short max-age=300 first, confirm every subdomain has a valid certificate before adding includeSubDomains, and only then raise it to a year. Do not add preload unless you understand that removal from the preload list takes months.
The cheap wins alongside HSTS are X-Content-Type-Options: nosniff, X-Frame-Options: SAMEORIGIN and a Referrer-Policy. A full Content-Security-Policy is worth more than all three together and considerably harder to deploy on Joomla, because templates and extensions inject inline scripts liberally — start with Content-Security-Policy-Report-Only and expect to spend real time on it.
11. Security extensions: what they genuinely add
The best known in the Joomla space is Admin Tools from Akeeba: a web application firewall, administrator access controls, a .htaccess maker, file-change scanning and a batch of hardening toggles. Actively maintained, and it earns its place on sites where you cannot configure the web server directly.
What they add: request filtering before Joomla dispatches, catching generic injection and traversal patterns; rate limiting and lockout on repeated failed logins, which core does not provide; the ability to generate a correct .htaccess without hand-writing it; and file integrity scanning, which is genuinely useful for detection.
They do not patch vulnerabilities. A WAF running in front of a vulnerable component may block a known exploit signature; it will not block a variant. Sites are routinely compromised while running a well-configured security extension because the underlying software was out of date. Treat this step as defence in depth, worth perhaps €40–90/year for a single site, and never as a reason to relax step 1.
12. Logging and monitoring
Prevention fails. What separates a bad day from a catastrophe is how quickly you notice. Joomla gives you more here than most people use.
Turn on the action log
Enable the User Actions Log plugins and review Users → User Actions Log. It records logins, content changes, extension installs and configuration saves with user and IP. An extension installed at 03:00 by an account that never installs extensions is exactly the signal you want. Keep log_path outside the web root and read the PHP error log periodically too — repeated fatals from one component often accompany exploitation attempts.
Baseline your file tree
Record a hash manifest of the installation so you can diff against it later:
find /path/to/site -type f -name "*.php" -exec sha256sum {} \; \
| sort -k2 > /home/account/baseline-$(date +%F).txt
Regenerate after every update and diff after anything suspicious. This turns “I think something changed” into a definite list of modified files.
Add external uptime and content monitoring on top. A service that fetches your homepage every few minutes and alerts on unexpected changes catches defacement and injected redirects long before a customer emails you.
13. Backups as a security control
Backup belongs in a security runbook because it determines your worst case. With a tested, offsite, recent backup a compromise costs you an afternoon; without one it can cost you the site. Three requirements, all commonly missed:
- Offsite. A backup stored in
/backupsinside the web root is available to whoever compromised the site — and in several cases I have investigated, was downloaded by them. - Retained long enough. Compromises are frequently discovered weeks later, so seven-day retention means every surviving backup is already infected. Keep daily for a fortnight, weekly for three months, monthly for a year.
- Tested. An untested backup is a hypothesis. Restore to a staging subdomain at least quarterly and confirm the site actually comes up.
Restoring from backup after a compromise does not fix the vulnerability that was exploited. If you restore to yesterday’s snapshot and change nothing else, you have restored a site with the same hole, and you will be back within days. Restore and patch, in that order, with the site offline in between.
What this runbook deliberately leaves out
A few items appear on other checklists that I would not spend your time on. Hiding the Joomla version by stripping the generator tag is harmless but does not hide you — fingerprinting from asset paths and core file hashes is a solved problem and every scanner does it. Blanket blocking of countries or user agents is attractive and largely ineffective, because attack traffic uses ordinary user agent strings and arrives from residential proxy networks everywhere. Disabling XML-RPC is WordPress advice that has migrated across by mistake; Joomla has no equivalent. Disabling exec, shell_exec and system in PHP is genuinely worth doing if nothing legitimate needs them, but it is a marginal gain that people reach for while their core is three releases behind.
A realistic maintenance rhythm
| Cadence | Task |
|---|---|
| On advisory | Apply core security release within 24 hours |
| Weekly | Check for extension updates; scan the action log |
| Monthly | Verify backups ran; check disk usage and error log |
| Quarterly | Restore a backup to staging; audit Super Users; regenerate the file hash baseline |
| Annually | Review every installed extension for maintenance status; review PHP version support horizon |
None of this is exotic. It is a couple of hours a month, and it is the difference between a Joomla installation that runs quietly for years and one that ends up in an incident response engagement.
Frequently asked questions
Do I still need a security extension if I patch promptly?
It is optional rather than essential. Prompt patching, MFA and tested backups get you most of the available risk reduction. A security extension mainly adds login rate limiting, file-change detection and easy web server configuration — real value on shared hosting where you cannot edit the server config, less so on a VPS where you can do the same things natively.
Should I set files to 777 to fix a failing extension install?
No, under any circumstances. A failing install at 755 is nearly always an ownership mismatch between your FTP user and the PHP user, and your host can fix that in minutes. Setting 777 means any compromised script anywhere on that server can rewrite your files, and people routinely forget to change it back.
How do I know whether my Joomla version is affected by an advisory?
Check the affected-version range in the advisory against the exact version in System → System Information, and check whether the vulnerable component or feature is actually enabled on your site. An advisory affecting the web services API does not apply if you have never enabled it — but verify that rather than assuming, because some features are enabled by default.
Is it safe to leave a Joomla 3 site running if it is behind a WAF?
No. Joomla 3 stopped receiving security fixes in August 2023, so newly discovered vulnerabilities in it will never be patched. A WAF blocks known signatures and is regularly bypassed by variants. Behind a WAF you have bought time to migrate, which is a reasonable thing to buy — it is not a place to stay.
How often should I actually test my backups?
Quarterly at minimum, and after any significant change to the hosting environment. The test is not “the backup file exists” — it is a full restore to a staging subdomain, log in to the administrator, and load a few front-end pages. Most backup failures are discovered only during a restore attempt, which is precisely the wrong moment.
My host says they handle security. Is that enough?
Your host secures the server: the operating system, the web server, network filtering, and usually a malware scanner. They do not patch your Joomla installation, manage your administrator accounts, or vet your extensions. Application-layer security is yours regardless of what the hosting marketing page implies.