Category: Joomla

  • How to Remove MooTools from Joomla

    How to Remove MooTools from Joomla

    MooTools is a JavaScript framework Joomla used from 1.5 through 3.10 for SqueezeBox popups, image captions, and older effects. Joomla 4, Joomla 5, and Joomla 6 do not ship it. Core in those versions uses the Web Asset Manager, Bootstrap 5, and media/system/js/core.js. If a current site still requests mootools-core.js, a template or extension is loading its own copy.

    People remove it to cut unused JavaScript and to stop fights with jQuery over the $ symbol. On Joomla 3 that is safe only after you replace captions and modals. Deleting every script tag, which is what a lot of old snippets do, takes jQuery and the template down with it.

    What you will learn

    • What MooTools was for, in general and inside Joomla
    • Which versions still include it, and which do not
    • Why caption.js keeps showing up on ordinary article pages
    • Why speed plugins and developers want it gone
    • How to remove only those files on Joomla 3, and what to do when Joomla 4+ still loads them

    What MooTools is

    MooTools is an open-source JavaScript library, not a Joomla extension. It gave developers a class system, DOM helpers, events, animation (Fx), and Ajax (Request). The copy Joomla 3 shipped is MooTools Core 1.4.5, plus MooTools More 1.4.0.1 when something asked for the extra effects. Official inventory: Joomla 3 JavaScript frameworks.

    The MooTools project itself stalled. Its last release line is 1.6.0 from 2016, and Joomla never moved the CMS copy up to that. You are looking at an unmaintained library frozen inside Joomla 3.

    It is not jQuery. Joomla 3 shipped both. jQuery is a separate, optional asset even on Joomla 4 and 5 (jquery in the Web Asset Manager). Removing MooTools does not remove jQuery, and you should not treat them as the same cleanup.

    What Joomla used it for

    Joomla did not load MooTools on every page by itself. A layout, template, or extension called a behavior, and that behavior added the files. These are the calls that mattered:

    Call Files it pulled What you saw on the site
    JHtml::_('behavior.framework') mootools-core.js. Pass true and it also loads mootools-more.js Nothing visible. Later scripts then use $, $$(), and window.addEvent('domready')
    JHtml::_('behavior.caption') Core plus caption.js Images with class caption get a text line under them, read from the title
    JHtml::_('behavior.modal') Core plus modal.js and modal.css SqueezeBox. Links with class modal open in a lightbox
    Template or extension addScript Whatever path they hardcoded Sliders, accordions, menus, galleries written against MooTools

    Two details explain the script tags people paste into forum posts.

    • caption.js is why MooTools appears on plain articles. Joomla 3 article and blog layouts call behavior.caption. That loads MooTools even when the page has no popup and no slideshow. Since Joomla 3.2.2 the editor inserts real figure and figcaption tags, but the old script stayed so articles written before that still got captions.
    • The file is not named mootools.js on Joomla 3. Core ships /media/system/js/mootools-core.js and /media/system/js/mootools-more.js. The single mootools.js name is the Joomla 1.5 file, or a template that copied it. Search for mootools, not one exact filename.

    Older Joomla also used MooTools for tooltips and form checks. By 3.10 most of those had moved to Bootstrap and jQuery. Modals and captions did not. SqueezeBox was still the modal implementation in 3.10. There is a long core discussion of that leftover: MooTools is still required by modals.

    Which versions include it

    Joomla MooTools in core? What to do
    1.5 Yes. It was the JavaScript framework Upgrade. Do not spend time micro-optimizing 1.5
    1.6, 1.7, 2.5 Yes Same. These releases are long out of support
    3.0 to 3.10 Yes, deprecated. Core 1.4.5 and More 1.4.0.1. Loaded only when a behavior or extension asks Remove the caller on the frontend after you replace captions and modals. Leave the files on disk
    4.0 and later No. Removed from core, along with caption.js and SqueezeBox If the file still loads, it is third-party. Update or replace that extension
    5.x and 6.x No Same as Joomla 4. Core scripts are core.js and Bootstrap, not MooTools

    Joomla 4’s own break list says caption.js is gone and captions must be figure and figcaption: Potential backward compatibility issues in Joomla 4. Joomla 5 and 6 inherited that. They did not put MooTools back.

    A compatibility plugin does not restore it either. Behaviour – Backward Compatibility covers selected PHP APIs. It does not inject mootools-core.js.

    Why people want it removed

    • Unused weight. On a Joomla 3 article page the caption behavior adds render-blocking scripts in the head. If you have no img.caption and no SqueezeBox link, the browser still downloads and parses them.
    • The $ clash. MooTools claims $. Joomla loads jQuery and then jquery-noconflict.js, which gives $ back. Whichever library runs last owns $. A template script that expects jQuery throws “is not a function”. A MooTools slider dies the other way. Removing the library you do not need is one fix. Wrapping jQuery as jQuery(function ($) { ... }) is the other, and it is safer when something still needs MooTools.
    • No upstream fixes. A 2016-era library inside a CMS that stopped at 3.10 will never get a modern browser pass or a security release of its own.
    • PageSpeed. Lighthouse flags it as unused JavaScript. The win is real only when the page still works afterward. A broken menu costs more than a few kilobytes.
    • Stack conflicts. Bootstrap, Vue, Alpine, and current templates were not written against MooTools 1.4. Old effects CSS also collides with SqueezeBox’s modal.css.
    • Migration. Joomla 4 deletes the files. A Joomla 3 site that still needs them is carrying a blocker into the upgrade. Fix the dependency before the hop, not during a failed update. Path and breakage notes: Joomla upgrade issues and Joomla 3 to 4, 5, and 6.

    What breaks if you strip it blindly

    • Captions on old articles that use <img class="caption"> instead of figcaption
    • Any class="modal" link that expected SqueezeBox
    • Template features written with Fx.Slide, Fx.Accordion, or Tips
    • Extensions that still call behavior.framework: older galleries, some menu systems, some editors’ lightbox buttons
    • The administrator, if your snippet runs there too. Joomla 3 admin modals used SqueezeBox. Never strip MooTools from /administrator
    • Inline scripts that still say SqueezeBox.initialize. The file is gone, the inline code remains, and the console fills with errors

    Do not delete media/system/js/mootools-core.js from disk on Joomla 3. Core and extensions still print that URL. You get a 404, not a faster site. Stop the request. Leave the file.

    Step 1: Confirm the Joomla version

    1. Open System → System Information, or look at the footer of the administrator.
    2. If the version is 4, 5, or 6, skip the Joomla 3 unset snippet. Core is already clean. Go to Step 3 and find the third-party file.
    3. If the version is 3.10 or older, keep reading. Take a backup before you edit the template. Method: how to backup a Joomla website.

    Step 2: See the real script URL

    1. Turn off any “combine JS” or cache plugin first. Combiners hide mootools-core.js inside one bundled file, so view-source lies.
    2. Open a frontend article, view source, and search for mootools and caption.js.
    3. In the browser Network panel, filter by mootools. The Initiator column names the document or script that requested it.
    4. Note the path.
      • /media/system/js/mootools-core.js means Joomla 3 core, pulled in by a behavior.
      • /templates/yourtemplate/js/... or /media/com_something/ means that template or extension bundles its own copy. Common on Joomla 4+ leftovers.

    Step 3: Find the caller

    Search the site files (template, then components, modules, plugins, templates) for:

    • behavior.framework
    • behavior.mootools
    • behavior.caption
    • behavior.modal
    • mootools
    • SqueezeBox

    On Joomla 3, a hit inside components/com_content is core calling captions. That one is expected. Hits inside your template and third-party extensions are the ones you can turn off. On Joomla 4+, any hit is third-party. Update that extension, disable it, or replace it. There is no core switch labeled “MooTools”.

    Step 4: Replace captions and modals first

    1. For images, use figure and figcaption. The Joomla editor has done this since 3.2.2. Re-save old articles, or run a content replace from img.caption to that markup. After the move, caption.js has nothing to do.
    2. For popups, stop using class="modal" plus a rel attribute. That syntax is SqueezeBox. Use a Bootstrap modal, or on Joomla 4+ the core dialog pattern. behavior.modal does not exist on Joomla 4.
    3. Click every slider, tab, and gallery on a staging copy. If one of them dies with $ is not a function or Fx is not defined, that feature still needs the library. Update the extension before you block the file.

    Step 5: Unset only MooTools on the Joomla 3 frontend

    The reliable hook is onBeforeCompileHead in a system plugin. It runs after extensions have added scripts and before the head is printed. Code at the top of index.php runs too early, and the usual copy-paste deletes every script, not just MooTools. That broken pattern is called out in the next section.

    This version touches MooTools and caption.js only, and it leaves the administrator alone:

    <?php
    public function onBeforeCompileHead()
    {
        $app = JFactory::getApplication();
        if (!$app->isSite()) {
            return;
        }
    
        $doc = JFactory::getDocument();
        $head = $doc->getHeadData();
        if (empty($head['scripts']) || !is_array($head['scripts'])) {
            return;
        }
    
        foreach ($head['scripts'] as $url => $attrs) {
            $path = strtolower($url);
            if (strpos($path, 'mootools') !== false || strpos($path, 'caption.js') !== false) {
                unset($head['scripts'][$url]);
            }
        }
    
        $doc->setHeadData($head);
    }
    ?>

    Clear Joomla cache and the combiner cache, then view source again. mootools-core.js and caption.js should be gone. jQuery, your template file, and analytics should still be there.

    If the console says SqueezeBox is not defined, a layout is still calling behavior.modal. Remove that call. Unsetting the file does not remove the inline startup script.

    Step 6: Test the pages that used to need it

    • An article with an image caption
    • A page that used a lightbox or class="modal"
    • The menu, the slider, and the contact form
    • One login
    • The administrator home screen, media manager, and a modal field such as the article image picker

    Check the administrator even if the plugin returns early on admin. A template edit often runs on both sides.

    Step 7: On Joomla 4, 5, or 6, update the extension

    There is no core file to unset. Use the path from Step 2.

    • Template path: switch to Cassiopeia. If MooTools disappears, the template was loading it. Update the template or drop that script include.
    • Extension path: update it to a build made for your major, or disable it. Packing MooTools in 2026 means the vendor has not moved that feature to Joomla.request, Bootstrap, or plain JavaScript.
    • Do not add the Joomla 3 unset plugin to Joomla 4 and expect it to see Web Asset Manager scripts the same way. Assets registered with $wa->useScript() are not always sitting in the old scripts array. Disable the asset or remove the include at the source.

    The snippet that removes every script

    This is the code still circulating, including on older copies of this article. Do not use it:

    foreach ($headerstuff['scripts'] as $key => $value) {
        unset($headerstuff['scripts'][$key]);
    }

    The loop has no filter. It drops jQuery, template JavaScript, captchas, and statistics, then the site feels “lighter” because half of it no longer runs. Filter on mootools and caption.js, or do not ship the change.

    What to use instead

    Old MooTools piece Replacement
    behavior.caption and caption.js figure and figcaption
    behavior.modal and SqueezeBox Bootstrap modal, or the Joomla 4+ dialog field
    window.addEvent('domready', ...) document.addEventListener('DOMContentLoaded', ...) or a defer script
    $$('.item') document.querySelectorAll('.item')
    Request.HTML fetch, or Joomla.request() from core.js on Joomla 4+
    Fx.Slide / accordion CSS, or Bootstrap collapse
    $ as MooTools Keep jQuery behind jQuery(function ($) { }) if a script still needs jQuery

    Web Asset Manager is the loader on current Joomla, not JHtml::_('behavior.framework'). A script that only needs Joomla’s helpers depends on core. Add jquery only when that file truly uses jQuery. Docs: Web Asset Manager.

    When removing it is the wrong project

    If the template, the shop, and two galleries all fatal without MooTools, you do not have a script-tag problem. You have a Joomla 3 stack. Stripping the library will not make that stack current. Plan the upgrade, replace the extensions on staging, then the file disappears because Joomla 4 does not ship it.

    Infyways runs those jumps from $149, with a compatibility audit within 12 hours. Start at Joomla Upgrade Services.

    Key takeaways

    1. MooTools powered SqueezeBox, caption.js, and older effects. It is not jQuery.
    2. Joomla 3.10 still ships Core 1.4.5. Joomla 4, 5, and 6 do not ship it at all.
    3. Article layouts load caption.js, which is why the file shows up on pages that look static.
    4. Unset only URLs that contain mootools or caption.js, on the frontend, after captions and modals have a replacement.
    5. Never delete the core file on Joomla 3, and never unset every script in the head.
    6. On a current site, update the template or extension that is still bundling the library.

    Frequently asked questions

    What is MooTools used for in Joomla?

    On Joomla 3 it opens SqueezeBox modals and builds captions for images that use the caption class. Older templates also use it for sliders and accordions. Joomla 4 and newer core do not use it.

    Does the latest Joomla still use MooTools?

    No. Joomla 4 removed it, and Joomla 5 and 6 did not bring it back. A request for mootools-core.js on those versions comes from a template or extension.

    Why do people remove MooTools?

    The usual reasons are unused JavaScript on article pages, conflicts with jQuery over $, and the fact that the library has had no real release since 2016. PageSpeed scores are a side effect, not the only reason.

    Why are mootools-core.js and caption.js on every article?

    Joomla 3 content layouts call JHtml::_('behavior.caption'). That behavior loads both files so old img.caption markup still gets a caption.

    Will my site break if I remove it?

    It breaks wherever a template or extension still calls SqueezeBox, caption.js, or MooTools effects. Test those pages. The administrator on Joomla 3 should keep the library.

    Should I delete mootools-core.js over FTP?

    No. On Joomla 3 that only creates a 404, because core still prints the URL. Stop the behavior or unset that URL. On Joomla 4+ the file is not part of core.

    Is MooTools the same as jQuery?

    No. Joomla 3 loaded both. jQuery remains an optional Web Asset Manager asset on current Joomla. MooTools does not.

    What replaces caption.js and SqueezeBox?

    Use figure and figcaption for captions. Use a Bootstrap modal, or the Joomla 4 dialog, instead of class="modal".

    Can someone remove it without breaking the template?

    Yes. That means finding the caller, replacing captions and lightboxes, then unsetting only those scripts. Request a Joomla upgrade audit if the template still depends on it.

  • Fix “Class JRequest Not Found” in Joomla 4, 5 and 6.2 (Tested)

    Fix “Class JRequest Not Found” in Joomla 4, 5 and 6.2 (Tested)

    Updated 15 October 2026 · By Abhilash Sahoo, Founder, Infyways

    Quick answer: Class "JRequest" not found means some extension, template or override still calls JRequest, a Joomla 3-era class that Joomla 4, 5 and 6 don’t include. To fix it:

    1. Turn on Debug System to see the file and line.
    2. Search the site for JRequest::.
    3. Replace each call with Factory::getApplication()->getInput(), for example JRequest::getInt('id') → $input->getInt('id', 0).
    4. If it’s a third-party extension, update it first. The Backward Compatibility plugin will not bring JRequest back.

    This applies unchanged to Joomla 6.2, the latest version.

    I reproduced this error on a real Joomla 5.4.10 site (PHP 8.2, XAMPP) and tested every replacement below on it. I also checked the Joomla 6.2.0 source package. Images marked “real output” show what the site actually returned. Main reference: Joomla’s official Input documentation.

    What does “Class JRequest not found” mean?

    PHP reached a line such as JRequest::getCmd('view') and couldn’t find a class called JRequest. Joomla 1.5 to 3 used it to read $_GET, $_POST and $_FILES. It was deprecated during the Joomla 3 series in favour of the Input API. Joomla 4 doesn’t include it, and neither do Joomla 5 or 6.

    Because the class is missing, it’s a fatal error and the page stops at that line. With debug off, my test site showed only “Error: 0” and HTTP 500. A search of the Joomla 5.4.10 libraries/ and plugins/ folders found no references to JRequest, so the call always comes from your own code or a third-party extension.

    Which file is causing the error?

    Go to System → Global Configuration → System, set Debug System to Yes, and reload the broken page. The error page now includes a call stack. Row 1 is the file and line that called JRequest:

    Joomla debug call stack for Class "JRequest" not found pointing to the file and line

    Turn debug off again when you’re done. If you can’t reach the admin, set public $debug = true; in configuration.php. You can also check the PHP error log, which records the same file and line.

    If the stack shows nothing useful, your template may not show system messages. Temporarily switch to Cassiopeia. Then disable third-party plugins one at a time until the error goes away.

    How do I find every JRequest call on the site?

    Fixing one file is rarely enough. Old extensions usually call JRequest in several places, and template overrides in templates/*/html/ are easy to miss. From the site root:

    rg -n "JRequest::" .          # ripgrep, fastest
    grep -rn "JRequest::" --include=*.php .   # works on any host
    Searching a Joomla site for JRequest calls with ripgrep, including template overrides

    If you have no shell access, download the site over FTP and search the folder in your code editor. You can also run the extension through JED Checker.

    What replaces JRequest in Joomla 4, 5 and 6?

    Use the application’s Input object:

    use Joomla\CMS\Factory;
    
    $input = Factory::getApplication()->getInput();
    
    $id   = $input->getInt('id', 0);
    $view = $input->getCmd('view', '');
    $q    = $input->getString('q', '');

    Put the use line at the top of the file, after defined('_JEXEC') or die;. Always pass a default value, because the Input methods return it when the parameter is missing.

    Cheat sheet mapping JRequest methods to the Joomla Input API
    Old JRequest call Joomla 4 / 5 / 6 replacement
    JRequest::getInt('id') $input->getInt('id', 0)
    JRequest::getCmd('task') $input->getCmd('task', '')
    JRequest::getString('q') $input->getString('q', '')
    JRequest::getWord('layout') $input->getWord('layout', '')
    JRequest::getVar('x', '', 'post') $input->post->get('x', '', 'string')
    JRequest::getVar('x', '', 'get') $input->get->get('x', '', 'string')
    JRequest::getVar('file', null, 'files') $input->files->get('file')
    JRequest::get('post') $input->post->getArray()
    JRequest::getVar('jform', [], 'post', 'array') $input->post->get('jform', [], 'array')
    JRequest::setVar('a', 1) $input->set('a', 1)
    JRequest::checkToken() Session::checkToken() (use Joomla\CMS\Session\Session;)
    JRequest::getVar('HTTP_REFERER', '', 'server') $input->server->getString('HTTP_REFERER', '')

    Which filter should I use?

    The old JRequest::getVar() was often used with no filter at all. Choose a filter on purpose. The official docs say get() uses CMD when you don’t pass a filter, so $input->get('q') strips spaces and punctuation. Here’s what my test site returned for ?id=7abc&q=<b>hi</b>:

    Tested Joomla Input filter results for getInt, getString, raw and getCmd
    Data Use Tested result
    IDs, counts getInt() / 'uint' 7abc → int(7)
    option, view, task getCmd() missing → your default
    Search text, names getString() <b>hi</b> → "hi"
    Editor HTML 'html', or 'raw' plus your own filtering 'raw' kept <b>hi</b> unchanged
    Form arrays post->get('jform', [], 'array'), then validate in the model No filtering, as with core MVC

    Only use raw when you clean the value afterwards. Raw input that ends up in SQL or HTML is a security hole. See our Joomla security guide.

    How do I fix JRequest inside a controller, model or module?

    • Controllers that extend BaseController already have the input as $this->input, so JRequest::getCmd('task') becomes $this->input->getCmd('task').
    • Models, views, helpers, plugins and template overrides: use Factory::getApplication()->getInput(). In a plugin that has $this->getApplication(), call $this->getApplication()->getInput().
    • Modern modules with a Dispatcher get Input passed into the constructor. The official manual includes a full mod_input example.

    Does the Backward Compatibility plugin fix JRequest?

    No. I tested this directly. With the Behaviour – Backward Compatibility plugins enabled, old aliases such as JFactory and JInput worked. With the plugins disabled, they failed. JRequest didn’t exist in either case:

    Behaviour Backward Compatibility plugin test showing JRequest is never restored

    In Joomla 6.2, the compat6 plugin’s class map has 450 aliases. It still includes JFactory, JInput and JText, but not JRequest. So don’t replace JRequest with JFactory::getApplication()->input, because that still depends on the compatibility layer. Use the namespaced form.

    Is $app->input still OK in Joomla 6.2?

    It works, but it’s deprecated. In the Joomla 6.2.0 source, reading ->input goes through a magic __get() proxy. That proxy triggers a deprecation notice and is marked “will be removed in 8.0”. New code should call getInput().

    Joomla 6.2 source showing the app input property is deprecated in favour of getInput

    What if the error comes from a third-party extension?

    1. Check the developer’s site or the JED for a Joomla 5/6 version. Updating is safer than patching, because code that uses JRequest usually has other Joomla 3-only code (JError, old MVC, Bootstrap 2).
    2. If it’s abandoned, find a maintained replacement.
    3. If you have to patch it, keep a copy of your changes, because the next update will overwrite them.

    Before upgrading, run our Joomla 6 compatibility check to catch these problems first. For the full migration path, see the Joomla 3 to 6 upgrade guide.

    Troubleshooting

    Symptom Cause Fix
    White page or “Error: 0”, HTTP 500 Debug off, fatal error Turn on Debug System and read row 1 of the stack
    Fixed one file, error moved to another Several calls Search the whole tree, including overrides
    Class "Factory" not found Missing use Add use Joomla\CMS\Factory; or write \Joomla\CMS\Factory
    Call to a member function getCmd() on null Code ran before the app was ready, or $this->input outside a controller Use Factory::getApplication()->getInput()
    Search box loses spaces get() defaults to CMD Use getString()
    HTML from an editor gets stripped STRING filter Use 'html', or 'raw' and filter it yourself
    Works on Joomla 5, breaks on 6 Old J* aliases with the compat plugin off Switch to namespaced classes
    Error only in admin Admin module or plugin Disable it via the database, or rename its folder over FTP

    FAQs

    Was JRequest removed in Joomla 4?

    Yes. It was deprecated in the Joomla 3 series. Joomla 4, 5 and 6 don’t include it, so any call causes a fatal error.

    Can I add a JRequest class myself to make the error go away?

    You could write a small shim, but it only hides the problem. Code like that usually breaks on the next Joomla 3 API it uses. Replacing the calls is quicker and safer.

    What is the Joomla 4 equivalent of JRequest::getVar()?

    Factory::getApplication()->getInput()->get('name', $default, 'filter'). Use ->post, ->get or ->files to read from a specific source.

    Is JInput the same as JRequest?

    No. JInput is the old alias for Joomla\Input\Input, which is the replacement. Use the namespaced Input rather than JInput.

    Why does my site show only “Error: 0”?

    Debug mode is off, so Joomla hides the details. Turn on Debug System, or check the PHP error log for the file and line.

    Does this error affect Joomla 6.2?

    Yes, in the same way. Joomla 6.2 is the latest version, and it has no JRequest either.

    Will turning off the Backward Compatibility plugin cause more errors?

    It can. In my test, JFactory and JInput stopped working when it was off. Fix those aliases too before you rely on the site without it.

    How do I read JSON sent by AJAX?

    Use $input->json->get('key'). This only works when the whole request body is JSON.

    Can I fix this without coding?

    Update or replace the extension that causes it. If it’s custom code, a developer will need to change it. We fix these as part of our Joomla upgrade service.

  • Fix Joomla 500 Error on mod_rewrite

    Fix Joomla 500 Error on mod_rewrite

    A Joomla 500 error on mod_rewrite is almost always Apache rejecting a directive in your root .htaccess file right after you enable Search Engine Friendly URLs or “Use URL rewriting.” The browser only shows Internal Server Error. The Apache error log names the real cause: usually Options +FollowSymLinks, a missing AllowOverride, disabled mod_rewrite, or a bad RewriteBase.

    This guide is the fix path we use on Joomla 4, 5, and 6 sites (and older 3.x still on Apache). It complements our deeper Joomla .htaccess guide and the Joomla SEF URL setup.

    What you will learn

    • How to prove the 500 is caused by .htaccess / mod_rewrite in under two minutes
    • The exact Apache log messages that map to each fix
    • How to repair FollowSymLinks, AllowOverride, mod_rewrite, and RewriteBase
    • How to rebuild a clean Joomla .htaccess from htaccess.txt
    • What to check in Global Configuration and conflicting extensions

    Why Joomla shows 500 after enabling URL rewriting

    Joomla SEF without rewriting still works with index.php in the URL. Turning on Use URL rewriting tells Joomla to drop index.php and rely on Apache mod_rewrite plus the rules in .htaccess (renamed from the shipped htaccess.txt).

    If Apache cannot apply those rules, every front-end request can return HTTP 500. The administrator may still load if you open it with a direct /administrator/ path, which is a useful clue.

    Symptom / log clue Most likely cause First fix
    Site dies only after enabling URL rewriting .htaccess active and rejected Rename .htaccess to restore the site, then fix the directive
    Options not allowed here or FollowSymLinks error Host forbids Options in .htaccess Comment out Options +FollowSymLinks
    .htaccess ignored, URLs 404 AllowOverride None Set AllowOverride All (or FileInfo Options) in the vhost
    Invalid command RewriteEngine mod_rewrite not loaded Enable the module and restart Apache
    Works in root, breaks in subdirectory Wrong RewriteBase Set RewriteBase /subfolder/
    Works until a security extension ships custom rules Broken rewrite block from a plugin Disable the extension, restore core SEF section

    Step 1: Prove it is the .htaccess file

    1. Via FTP or file manager, rename root .htaccess to .htaccess.bak.
    2. Reload the homepage.
    3. If the 500 disappears (even if SEF URLs look ugly again), the rewrite file is the culprit.

    Do not leave rewriting enabled in Global Configuration while .htaccess is missing, or you will get 404s on pretty URLs. Temporarily turn Use URL rewriting off, then continue.

    Step 2: Read the Apache error log

    Guessing wastes time. Open today’s error log (cPanel “Errors”, Plesk logs, /var/log/apache2/error.log, or your host’s equivalent) and reload the site once.

    Look for lines that mention .htaccess, Options, RewriteEngine, or AllowOverride. Match them to the table above before editing Joomla again.

    Step 3: Fix Options +FollowSymLinks (the most common 500)

    Joomla’s shipped htaccess.txt includes Options +FollowSymLinks because mod_rewrite historically needed it. Many shared hosts already set symlink policy in the virtual host and forbid changing Options from .htaccess. Apache then returns 500 with “Options not allowed here.”

    In your .htaccess, comment the line:

    # Options +FollowSymLinks

    Some hosts prefer:

    Options +SymLinksIfOwnerMatch

    Official Joomla docs note that if commenting FollowSymLinks restores the site and SEF still works, your administrator already set the option server-side and you should leave it commented. See Preconfigured htaccess and Enabling SEF URLs.

    Step 4: Confirm AllowOverride permits .htaccess

    If you control the server (VPS, dedicated, local XAMPP/WAMP), open the virtual host or httpd.conf <Directory> block for the site document root and ensure overrides are allowed:

    <Directory "/var/www/html">
        AllowOverride All
        Require all granted
    </Directory>

    On shared hosting you usually cannot edit this. If AllowOverride is None, ask support to enable .htaccess overrides for Options and FileInfo, or to enable Joomla URL rewriting for your account.

    Restart Apache after config changes.

    Step 5: Enable mod_rewrite

    On Debian/Ubuntu:

    sudo a2enmod rewrite
    sudo systemctl restart apache2

    On RHEL/Alma/CloudLinux, ensure this line is uncommented in the Apache config and restart:

    LoadModule rewrite_module modules/mod_rewrite.so

    Quick PHP check on a throwaway file (delete after testing):

    <?php
    print_r(apache_get_modules());

    If mod_rewrite is missing from the list (or apache_get_modules is unavailable under PHP-FPM), confirm with your host. Without the module, leave Joomla URL rewriting off. SEF can still run with index.php in the path.

    Step 6: Rebuild .htaccess from htaccess.txt

    Corrupt or hand-edited rewrite blocks are a frequent source of 500s. Joomla ships a known-good template as htaccess.txt in the site root.

    1. Download a fresh copy from your Joomla version package if the root file is missing.
    2. Copy htaccess.txt to .htaccess (merge carefully if you already have custom redirects).
    3. Comment Options +FollowSymLinks if Step 3 applied.
    4. Keep core SEF rules inside the <IfModule mod_rewrite.c> block.

    Core SEF section (simplified shape; prefer the full file from your Joomla version):

    <IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteBase /
    
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} !^/index\.php
    RewriteCond %{REQUEST_URI} /component/|(/[^.]*|\.(php|html?|feed|pdf|vcf|raw))$ [NC]
    RewriteRule .* index.php [L]
    </IfModule>

    Never paste truncated snippets that smash the title or other text into the middle of a RewriteCond line. That alone will 500 the site.

    Step 7: Set RewriteBase for subdirectory installs

    If Joomla lives at https://example.com/shop/, set:

    RewriteBase /shop/

    Root installs usually use RewriteBase /. Wrong base values produce 500s or broken asset paths after rewriting is enabled.

    Step 8: Align Global Configuration

    In System → Global Configuration → Site (wording varies slightly by Joomla 4/5/6):

    • Search Engine Friendly URLs: Yes
    • Use URL rewriting: Yes only after .htaccess works
    • Adds Suffix to URL: optional
    • Unicode Aliases: as needed for non-ASCII

    Save, clear Joomla cache, then test a menu item URL. If enabling rewriting brings the 500 back, return to Steps 2 and 3. Keep SEF on and rewriting off until Apache is clean.

    Step 9: PHP-FPM Authorization line and host extras

    Some hosts need the HTTP Authorization pass-through for API or extension auth:

    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    That line belongs in the core SEF section of a current Joomla .htaccess. Removing it rarely causes a 500, but a broken duplicate of it can. Also watch for host-injected blocks (LiteSpeed, Imunify, custom php_flag lines) that are invalid on your PHP handler. Comment suspect lines one at a time while watching the error log.

    Step 10: Rule out extensions and security plugins

    After the core file works, re-enable custom redirects and security rules carefully.

    • Disable recently installed SEF, security, or “firewall” extensions that write rewrite rules.
    • Check for a second .htaccess in /administrator or a subdomain docroot.
    • Restore from backup if a malware cleanup left garbage rewrite conditions.

    If the site was compromised, fix rewriting only after a clean restore. See our guide on repairing a hacked Joomla website.

    Nginx and IIS note

    mod_rewrite is an Apache module. Nginx uses try_files toward index.php. IIS uses web.config. A “mod_rewrite 500” on those stacks usually means you are reading Apache advice on the wrong server. Ask the host which web server fronts PHP before editing .htaccess.

    Key takeaways

    1. A Joomla 500 right after enabling URL rewriting is an Apache .htaccess problem until the error log proves otherwise.
    2. Commenting Options +FollowSymLinks fixes the majority of shared-hosting cases.
    3. Rename .htaccess to restore the site, then rebuild from htaccess.txt.
    4. Enable mod_rewrite and correct AllowOverride / RewriteBase before turning rewriting back on in Joomla.
    5. Keep Global Configuration rewriting off until a homepage request returns 200 with the fixed file in place.

    Related Joomla guides

    Need hands-on help? See our Joomla support and maintenance.

    Frequently asked questions

    Why do I get a Joomla 500 error when I enable mod_rewrite?

    Apache is rejecting a directive in .htaccess, most often Options +FollowSymLinks, or mod_rewrite is not allowed. Check the error log, then comment FollowSymLinks and confirm AllowOverride.

    Is the administrator also down during a rewrite 500?

    Not always. /administrator/ can still load while the public site 500s. That pattern strongly points at front-end rewrite rules rather than a total PHP crash.

    Should I delete .htaccess permanently?

    No. Use a rename only to diagnose. Joomla needs a valid .htaccess for clean SEF URLs and basic exploit blocking from the core template.

    Does this apply to Joomla 5 and Joomla 6?

    Yes. The SEF and htaccess.txt flow is the same family on Joomla 4, 5, and 6. Always copy htaccess.txt from your exact version package.

    What if I am on Nginx?

    Do not chase mod_rewrite. Configure Nginx try_files $uri $uri/ /index.php?$args; (or your host’s Joomla snippet) instead of Apache .htaccess.

    Can a plugin cause a rewrite 500?

    Yes. Security and SEF extensions sometimes append invalid rules. Disable recent extensions, restore the core SEF block, then re-add custom rules one by one.

    Where can I get a clean Joomla htaccess file?

    From the htaccess.txt in your Joomla root or install package. Our htaccess for Joomla article covers version-specific notes and hardening.

  • How to Back Up a Joomla Website (Joomla 4, 5 & 6)

    How to Back Up a Joomla Website (Joomla 4, 5 & 6)

    Short answer: The most reliable way to back up a Joomla 4, 5 or 6 site is Akeeba Backup: it saves files and database in one archive that you can restore anywhere with Kickstart. Keep at least three copies on two kinds of storage with one off-site (the 3-2-1 rule), automate the schedule, and test a restore regularly. Your host’s backups are a useful extra, not a replacement.

    Backing up a Joomla website means capturing both the site files and the database in a form you can restore on the same host or a new one. A usable backup is not a folder zip of images/ alone, and it is not a host snapshot you have never tested. For most Joomla 4, 5, and 6 sites the standard tool is Akeeba Backup (free Core or paid Professional), plus an off-site copy and a restore drill.

    This is the complete Infyways runbook: strategy, what to include, Akeeba install and configuration, Backup Now, off-site storage, Kickstart restore, automation, and the mistakes that turn “we have a backup” into downtime.

    What you will learn

    • What belongs in a real Joomla backup (and what you can safely exclude)
    • How Core and Professional Akeeba Backup differ for solo sites vs agencies
    • Step-by-step install, configure, Backup Now, download, and Kickstart restore
    • How to schedule backups, apply quotas, and keep archives off the public web root
    • When host backups, manual SQL dumps, and CLI jobs are better (or worse) than Akeeba

    What a complete Joomla backup must include

    Joomla stores content and settings in MySQL/MariaDB (or another supported DB). Templates, media, extensions, and configuration.php live on disk. Restore either piece alone and the site breaks.

    PieceWhy it mattersTypical location
    DatabaseArticles, users, menus, extension settings, SEF dataMySQL/MariaDB (prefix from configuration.php)
    Site filesCore, templates, extensions, media, overridesDocument root (and sometimes private folders outside it)
    configuration.phpDB credentials, paths, secret, mail, cachingSite root (rewritten during restore)
    Restoration scriptLets you install the archive without a blank Joomla packageEmbedded by Akeeba inside the archive

    Optional but valuable: a written note of PHP version, Joomla version, and critical extensions. That context speeds recovery after a failed major upgrade.

    Backup strategy before you click Backup Now

    Tooling fails when strategy is missing. Use a simple policy every production site can follow.

    1. 3-2-1 rule: at least three copies, on two different media/systems, with one off-site (S3, Drive, another server, or downloaded to a machine that is not the web host).
    2. Before every risky change: Joomla update, PHP bump, template change, migration, or security incident response.
    3. On a schedule: daily or weekly depending on how often content changes. Ecommerce and membership sites usually need daily.
    4. Test restore quarterly: a backup you have never restored is a hope, not a plan.
    5. Retention: keep several generations (for example 7 daily + 4 weekly) so a bad backup does not overwrite the last good one.
    MethodBest forLimits
    Akeeba Backup Core (free)Most sites: one-click full backup, Kickstart restore/migrateNo cloud push, limited automation, no encrypted JPS
    Akeeba Backup ProfessionalAgencies, shops, scheduled jobs, S3/Drive/SFTP, CLIPaid subscription
    Host panel backup / snapshotEmergency host-level recoveryOften opaque; hard to move hosts; may exclude remote DBs
    Manual files + phpMyAdmin dumpTiny sites or one-off cloningEasy to miss files; painful restore; no integrated installer
    Server CLI (mysqldump + rsync/tar)DevOps-managed VPSYou own scripting, encryption, and restore docs

    Infyways recommendation for typical Joomla sites: Akeeba as the primary portable backup, host snapshots as a secondary safety net, and at least one copy downloaded or pushed off the origin server.

    Joomla backup options compared

    MethodBacks upRestoreBest forWatch out for
    Akeeba Backup (Core, free)Files + database in one .jpa/.zipKickstart + restoration wizard, on any serverMost sites; moving serversStore copies off the server; Core has no remote upload
    Akeeba Backup ProSame, plus remote storage and more automationSameSites that need off-site copies automaticallyPaid subscription
    Host backups (cPanel, Plesk, managed hosts)Account or site snapshotsFrom the host panelQuick rollback on the same hostRetention and restore access depend on the host
    Manual (files by SFTP + database export)Whatever you copyRe-upload, import, edit configuration.phpSmall sites, emergenciesEasy to miss files or tables
    Server/VPS snapshotsWhole server diskRoll back the serverVPS/dedicated ownersNot portable to another host

    The 3-2-1 backup policy for Joomla

    • 3 copies of the site: the live site plus two backups.
    • 2 storage types, for example the server and cloud storage (S3, Backblaze, Google Drive, Dropbox).
    • 1 copy off-site, outside your hosting account, so a hacked or failed server does not take the backups with it.
    • Keep daily backups for a week and weekly backups for a month as a starting point, plus a manual backup before every update.

    Step 1: Install Akeeba Backup

    1. Download the current Akeeba Backup Core (or Professional) package from the official Akeeba product page. Match the package to your Joomla major version.
    2. In the administrator go to System → Install → Extensions (wording varies slightly on Joomla 4/5/6).
    3. Upload the package and confirm a clean install with no PHP fatal.
    4. Open Components → Akeeba Backup. On first run, accept the configuration wizard / automatic configuration so Akeeba tunes itself to your server.

    If installation fails on PHP version or memory, fix the server requirements first. Do not force an ancient Akeeba build onto a new Joomla release.

    Step 2: Configure output directory and security

    Open Akeeba Backup Configuration (profile settings).

    • Output directory: prefer a folder outside the public web root when the host allows it. If you must use a web-accessible path, use a random folder name and block HTTP access with .htaccess / server rules. Never leave dated .jpa / .zip files downloadable by the world.
    • Temporary directory: a writable temp path Akeeba can use during archival (often derived from site tmp). Confirm the folder is writable by PHP.
    • Archiver: JPA is the Akeeba-native format and usually the best default. ZIP is fine when you need wide tooling support. JPS (AES-encrypted) is a Professional feature for sensitive sites.
    • Database dump options: keep extended INSERTs enabled when your host supports them; include procedures/triggers if your extensions use them.

    Save the profile. Create a second profile later if you need “files only” or “database only” jobs (Professional), or a lighter profile that excludes bulky cache folders.

    Step 3: Set filters (exclude junk, keep what restores)

    Default full-site backup is correct for disaster recovery. Trim noise so jobs finish and archives stay manageable:

    • Exclude cache directories that regenerate (cache/, template cache, image-cache plugins) unless you have a reason to keep them.
    • Exclude old backup archives inside the output folder so backups do not nest forever.
    • Exclude giant log directories and unused tmp dumps.
    • Do not exclude images/, template overrides, or extension folders you still use.

    Professional adds regex filters, off-site directory includes, and extra database includes for multi-DB setups.

    Step 4: Run Backup Now

    1. Go to Components → Akeeba Backup → Backup Now.
    2. Add a short description (for example pre-j5-upgrade-2026-09-17).
    3. Start the backup and leave the browser tab open until it reports success. Do not navigate away on shared hosts with aggressive session limits; if the UI stalls, check Akeeba’s log and ALICE analyser rather than starting five parallel jobs.
    4. Open Manage Backups / Administer Backup Files and confirm the archive size looks sane (not a few kilobytes).

    If the job fails with timeout, memory, or “AJAX” errors, lower the partitioning size in Configuration, increase PHP max_execution_time / memory where allowed, or run during low traffic. Large media sites often need tuned chunk sizes more than they need a different product.

    Step 5: Download and store a copy off the server

    A backup that exists only on the same disk as the live site dies with the server.

    1. Download the archive from Manage Backups, or pull it over SFTP.
    2. Store it on encrypted disk, object storage, or another host.
    3. With Professional, configure post-processing to Amazon S3, Google Drive, OneDrive, Dropbox, SFTP, or other supported remotes, then enable remote quotas so old cloud copies rotate.
    4. Apply local quota rules so the web disk does not fill with archives.

    Step 6: Restore with Kickstart (the correct way)

    Do not treat an Akeeba archive like a random zip of a fresh Joomla package. Akeeba embeds a restoration script. The standard portable restore path:

    1. Provision an empty directory (or clean target) on the destination server with a working PHP and a MySQL/MariaDB database ready.
    2. Upload the backup archive (.jpa, .jps, or .zip) and the current Kickstart PHP file from Akeeba into that directory.
    3. Browse to kickstart.php, extract the archive, then continue into the restoration (Angie) wizard.
    4. Enter the new database credentials, site URL, and confirm configuration.php is written correctly.
    5. Delete Kickstart and leftover installation / restoration files after a successful login.
    6. Clear caches, recheck HTTPS, and test key pages (home, login, checkout, admin).

    Professional can also restore some full-site archives from inside the component on the same site. Site Transfer Wizard helps move archives over FTP. For unattended restores, Akeeba UNiTE is the CLI path.

    If you only have a host “files + SQL” dump without Akeeba’s restoration script, restore files, import SQL, then hand-edit configuration.php. That works, but it is slower and easier to get wrong than Kickstart.

    Step 7: Automate (cron, Scheduled Tasks, CLI)

    Manual Backup Now is perfect before upgrades. Production sites also need automation.

    • Professional: native CLI backup script, Joomla CLI integration (cli/joomla.php), Joomla Scheduled Tasks, front-end / remote backup API, and Remote CLI / UNiTE ecosystems.
    • Core: schedule is limited; many small sites download manually or rely on host jobs plus periodic Akeeba runs.
    • Always verify the first automated job actually produced a remote or downloaded file, not only a green UI message.

    Step 8: Prove the backup with a restore drill

    Quarterly (or after any major profile change):

    1. Restore the latest archive to a staging subdomain or local environment.
    2. Log into administrator, open a few articles, submit a contact form, and place a test order if you run ecommerce.
    3. Note restore time. That number is your real RTO (recovery time objective).
    4. Fix profile filters if something critical was missing.

    Special cases

    Before a Joomla or PHP upgrade

    Take a named full backup, download it off-site, then upgrade. If the site white-screens, restore first and debug on staging. Pair this with our upgrade and security checklists when you move majors.

    After a hack

    Do not blindly restore an infected archive over a cleaned host. Prefer a known-clean backup from before the compromise, then patch and harden. See How to repair a hacked Joomla website.

    Multisite / multiple databases

    Core backs up the main Joomla DB. Professional can include extra MySQL databases and off-site directories when your architecture needs them.

    Very large media libraries

    Exclude regenerated caches, use JPA, tune part size, and consider storing bulky static media in object storage with a separate sync if archives become multi-gigabyte every night.

    Security checklist for backup archives

    • Block web access to the output directory
    • Rotate and delete old archives with quotas
    • Prefer off-site copies for ransomware and host failure
    • Use JPS encryption (Professional) for sensitive data at rest when appropriate
    • Limit who can run Akeeba via Joomla ACL
    • Never commit backup archives to public git repositories

    Related guides and services

    Key takeaways

    1. A Joomla backup is files + database + a tested restore path, not a random zip of the template.
    2. Akeeba Backup Core covers one-click backup and Kickstart migration; Professional adds cloud, scheduling, CLI, and encryption.
    3. Store at least one copy off the production server and enforce quotas.
    4. Restore with Kickstart (or Pro in-component restore), then delete installer tools.
    5. Run a restore drill. Untested backups fail when you need them.

    Frequently asked questions

    What is the best way to backup a Joomla website?

    Use Akeeba Backup for a portable full-site archive, keep a copy off-site, and test restore with Kickstart. Add host snapshots as a secondary layer.

    Is Akeeba Backup free?

    Akeeba Backup Core is free for standard backup and Kickstart restore. Professional is paid and adds cloud storage, automation, CLI, encrypted JPS, and more.

    Does Akeeba work on Joomla 5 and Joomla 6?

    Yes, when you install a current Akeeba package built for your Joomla major. Always download from Akeeba’s official site, not random mirrors.

    Where should I store Joomla backup files?

    Outside the public web root when possible, plus an off-site copy (S3, Drive, SFTP, or local encrypted disk). Never leave archives openly downloadable.

    How often should I backup Joomla?

    Before every upgrade or risky change, and on a schedule that matches content change rate. Daily is common for shops and membership sites; weekly may suffice for static brochure sites.

    Can I restore an Akeeba backup like a new Joomla install zip?

    No. Use Kickstart (or Professional’s restore tools) so the embedded restoration script can rewrite configuration and import the database correctly.

    Are host cPanel backups enough?

    They help in a host-side disaster but are a poor primary plan for moving hosts or granular recovery. Keep a portable Akeeba archive as well.

    What if Backup Now fails halfway?

    Read the Akeeba log, reduce part size / memory pressure, exclude huge cache folders, and retry. Do not assume a partial archive is restorable.

    Can Infyways set this up for us?

    Yes. We configure Akeeba profiles, off-site storage, and restore drills for Joomla sites. Contact Infyways for backup and maintenance help.

  • How to Show Heureka Reviews on Your Website

    How to Show Heureka Reviews on Your Website

    Heureka reviews on your website come from the Verified by Customers program (Ověřeno zákazníky). Heureka does not give you a public reviews API. You display shop and product ratings with secret-key XML exports, plus official certificate icons and widgets from Heureka administration. The practical path for most stores is a custom Joomla module, WordPress plugin, or CMS integration that syncs those feeds safely and matches your theme.

    Infyways builds that integration. We already shipped a production Joomla module for estilofina.sk. Packages start at USD 149. This guide covers DIY XML setup and when to hire us for Joomla, WordPress, Magento, OpenCart, Shopify, or custom PHP.

    What you will learn

    • How Verified by Customers shop and product XML exports work (CZ and SK)
    • How official badges and widgets differ from parsed review lists
    • The legal notice Heureka expects when you republish verified reviews
    • Why caching and cron sync beat live XML on every page view
    • What Infyways delivers in a paid Heureka reviews plugin or module from USD 149

    Why put Heureka reviews on your own site

    Heureka is the trust layer for many Czech and Slovak shops. Showing those same verified ratings on your product pages, homepage, and checkout reduces tab-switching and keeps social proof where buyers decide.

    You get three building blocks from Heureka:

    1. Shop review XML (last about 500 store reviews)
    2. Product review XML (product reviews from roughly the last 6 months)
    3. Verified by Customers icons and widgets (auto-updating certificate UI)

    Exports refresh about every 6 hours. Icons and widgets follow your current certificate status (blue or gold). If you lose the certificate, Heureka stops showing those embeds automatically.

    Official references:

    DIY vs custom plugin: which path fits

    Approach Best for Limits
    Paste Heureka icon / widget code Quick trust badge on footer or sidebar No custom layout, no product-page review list, no theme match
    WooCommerce / third-party importer WordPress shops that already use that stack Rare outside WooCommerce; limited design and caching control
    Custom Joomla module / WP plugin / CMS module Stores that want shop + product reviews, cron sync, schema, and brand styling Needs a developer once; then you own the control panel
    Infyways Heureka integration (from USD 149) Joomla, WordPress, Magento, OpenCart, Shopify, custom PHP Scoped build with support and handoff

    If you only need the blue or gold badge, use Heureka’s SVG icon codes. If you want testimonials, product ratings, and a design that matches your store, you need a parser and a front-end component. That is the paid build.

    Step 1: Join Verified by Customers and get your export key

    You must already send orders through the Verified by Customers script and earn enough feedback for a certificate. Heureka awards blue or gold automatically from recommendation rates over the tracked windows (blue around 90% recommend over 180 days; gold around 97% over 90 days, with higher review volume). Exact thresholds can change. Confirm current rules in Heureka administration.

    In Heureka admin, open the Verified by Customers section and copy:

    • Shop review export URL
    • Product review export URL
    • Icon and widget embed codes

    Typical shapes:

    https://www.heureka.cz/direct/dotaznik/export-review.php?key=YOUR_SECRET_KEY
    https://www.heureka.sk/direct/dotaznik/export-review.php?key=YOUR_SECRET_KEY
    https://www.heureka.cz/direct/dotaznik/export-product-review.php?key=YOUR_SECRET_KEY

    Treat the key like a password. Do not commit it to public repos. Prefer server-side fetch and cache over exposing the full URL in browser JavaScript.

    Step 2: Understand the shop review XML

    Shop exports wrap reviews under a root reviews element. Each review commonly includes fields such as:

    • rating_id
    • order_id
    • unix_timestamp
    • delivery_time, transport_quality, communication, web_usability (scores often 0.5 to 5)
    • recommends / recommendation signal
    • pros, cons, summary
    • reaction (store reply, when present)

    Example structure:

    <reviews>
      <review>
        <rating_id>XXXXXX</rating_id>
        <unix_timestamp>1453155494</unix_timestamp>
        <delivery_time>5</delivery_time>
        <transport_quality>5</transport_quality>
        <communication>5</communication>
        <pros><![CDATA[Fast delivery]]></pros>
        <cons><![CDATA[]]></cons>
        <summary><![CDATA[Great shop]]></summary>
        <order_id>123</order_id>
      </review>
    </reviews>

    Field names can vary slightly by market and export generation. Always inspect your live feed before hard-coding a schema.

    Step 3: Understand the product review XML

    Product exports use a products root. Each product carries identifiers (name, URL, price, EAN, and related IDs) plus nested reviews. Each product review typically includes:

    • rating_id and rating_id_type (offer or product)
    • unix_timestamp
    • rating (0 to 5 in half-star steps)
    • pros, cons, summary
    • recommends

    Optional filter on the export URL:

    &from=2026-01-01 00:00:00

    That shortens the default ~6 month window to reviews created from that timestamp onward.

    For product reviews to accumulate, send a stable ITEM_ID in both your Heureka product feed and the Verified by Customers order script. Do not recycle ITEM_ID values across different products.

    Step 4: Add official badges and widgets

    Certificate icons and side widgets are separate from XML parsing. Heureka generates HTML/SVG snippets that reflect your live rating and recommendation percentage. Prefer SVG so mobile layouts stay sharp.

    Blue vs gold is certificate status, not a different XML format. When eligibility drops, Heureka hides those embeds. Your custom review list from XML can still show historical text reviews unless you choose to gate it behind certificate status yourself.

    Step 5: Add the required authenticity notice

    If you republish consumer reviews, EU consumer rules expect transparent origin wording. Heureka recommends language similar to this (adapt for SK/CZ legal counsel):

    Verified reviews come from customers who have purchased goods or services from us. They are collected via completed customer satisfaction questionnaires within the “Verified by Customers” program provided by Heureka Group a.s. Satisfaction questionnaires are sent only to customers who purchased from us.

    Put that near the review block. Infyways includes a configurable notice in every integration.

    Step 6: Cache, cron, and performance (do not hit XML on every request)

    Feeds update about every 6 hours. Fetching Heureka on each page view is slow, brittle, and unnecessary.

    Production pattern:

    1. Cron or scheduled task pulls shop and product XML every few hours
    2. Parse and store reviews in your CMS (database table, cache, or custom fields)
    3. Front end reads local data only
    4. Fail soft if Heureka is unreachable (keep last good cache)

    That is how our estilofina.sk Joomla module is designed to behave in production: admin URL + display options, then reliable front-end output.

    What Infyways builds for you

    Heureka Reviews Integration starts at USD 149. Scope scales with CMS and features.

    Included capabilities

    • Shop reviews from Heureka XML export (CZ and/or SK)
    • Product reviews on product detail pages
    • Official Verified by Customers badge / widget placement
    • Caching + cron sync (not live XML on every view)
    • Schema / rich-snippet friendly markup where appropriate
    • Multilingual support for Czech and Slovak feeds
    • Design match to your store theme (colors, typography, carousel or grid)
    • Admin settings: feed URL/key, count, minimum rating, layout, legal notice text

    Platforms we cover

    • Joomla module (proven on estilofina.sk)
    • WordPress plugin (WooCommerce or classic themes)
    • Magento, OpenCart, Shopify, and custom PHP
    Joomla module settings for Heureka XML URL, badge, and review layout
    Front-end Heureka review testimonials styled to match the store

    Proof: estilofina.sk

    We delivered a custom Joomla module for estilofina.sk that shows the Heureka badge plus latest reviews and ratings from the XML feed, with backend parameters for feed URL, testimonial height, and badge options. The same architecture ports to WordPress and other CMS stacks.

    Pricing

    Package Starting price Typical deliverable
    Starter badge + shop reviews USD 149 Module/plugin, cron cache, theme-styled shop reviews, legal notice
    Shop + product reviews Custom quote Product-page mapping, ITEM_ID alignment, schema
    Multi-market CZ + SK Custom quote Dual feeds, language-aware display
    Full design + carousel + ongoing support Custom quote Pixel-matched UI and maintenance

    Want Heureka reviews live on your site without building the parser yourself? Contact Infyways and request a Heureka Reviews Integration. Mention your CMS and whether you need shop reviews, product reviews, or both.

    Key takeaways

    1. Heureka reviews on your site come from XML exports and certificate widgets, not a public API.
    2. Shop feeds cover recent store reviews; product feeds cover roughly six months of product ratings.
    3. Always cache and sync on a schedule. Do not call the export URL on every page view.
    4. Add clear verified-origin wording when you republish reviews.
    5. Infyways ships Joomla, WordPress, and any-CMS integrations from USD 149, with a live Joomla reference on estilofina.sk.

    Frequently asked questions

    Is there a public Heureka reviews API?

    No. Merchants use secret-key XML exports from Verified by Customers plus official icon and widget codes from Heureka administration.

    How often do Heureka review exports update?

    About every 6 hours. Design your sync around that cadence.

    Can I show Heureka reviews on Joomla?

    Yes. Infyways already built a production Joomla module for estilofina.sk. We can deliver the same pattern for your template.

    Do you support WordPress and other CMS platforms?

    Yes. WordPress plugins, Magento, OpenCart, Shopify, and custom PHP are all in scope. Starting packages begin at USD 149.

    What is the difference between the badge and the XML list?

    The badge/widget is Heureka’s embed for certificate status and summary rating. The XML list is your own UI for individual shop or product reviews.

    Do I need both heureka.cz and heureka.sk feeds?

    Only if you sell into both markets with separate Verified by Customers setups. We can wire one or both feeds.

    Will this work without a Verified by Customers certificate?

    XML exports require the service and your key. Official icons and widgets only display while you hold a valid certificate.

    How do I get a quote?

    Send your CMS, Heureka market (CZ/SK), and whether you need shop reviews, product reviews, or badges via the contact form. Packages start at USD 149.

  • Joomla Backward Compatibility Plugin: The Definitive Guide

    Joomla Backward Compatibility Plugin: The Definitive Guide

    The Joomla Behaviour – Backward Compatibility plugin is a temporary bridge that restores selected legacy classes and assets so older extensions can keep running after a major Joomla upgrade. It is not a performance plugin, a security plugin, or proof that an extension is fully compatible. On Joomla 5 it bridges Joomla 4-era code. On Joomla 6, the separate Behaviour – Backward Compatibility 6 plugin bridges selected Joomla 5-era code. You should keep the appropriate bridge enabled while it is needed, then update or replace dependent extensions and prove the site works with it disabled.

    The most important detail is easy to miss: Joomla 5.4 has two compatibility plugins, and they have opposite roles during an upgrade to Joomla 6. The unnumbered Joomla 5 plugin must be disabled. The Joomla 6 plugin must be enabled. Joomla's pre-update checker enforces this configuration before Live Update can continue.

    This guide explains the history, purpose, options, overhead, risks, safe disable test, recovery procedure, developer migration work, and exact settings for Joomla 4.4, 5.x, and 6.x. It is based on the official Joomla compatibility plugin documentation, Joomla 5.4 to 6.x migration documentation, backward compatibility policy, and current Joomla source.

    What you will learn

    • Why Joomla created compatibility plugins
    • When the first plugin appeared and how the bridge model evolved
    • Which plugin belongs to Joomla 5 and which belongs to Joomla 6
    • What every option actually loads
    • Whether leaving the plugin enabled affects speed, security, or upgrades
    • When you can safely disable it
    • How to find the extension that still depends on it
    • How to recover if disabling it breaks the frontend or Administrator
    • What extension developers must change for native compatibility

    For hands-on upgrade help, see Joomla upgrade services or hire Joomla developers.

    The short answer for every Joomla version

    Your site Plugin state What it means
    Joomla 4.4 preparing for Joomla 5 Behaviour – Backward Compatibility is staged for the Joomla 5 transition The bridge becomes active for the major upgrade
    Joomla 5.0 to 5.3 Unnumbered plugin often enabled Older Joomla 4 extensions may still depend on class aliases or asset shims
    Joomla 5.4 staying on Joomla 5 Unnumbered plugin may remain enabled temporarily Disable it on staging to discover technical debt
    Joomla 5.4 preparing for Joomla 6 Disable unnumbered plugin; enable Backward Compatibility 6 Required by the pre-update checker
    Joomla 6 upgraded from 5.4 Backward Compatibility 6 is normally enabled It protects the first boot while extensions transition
    Fresh Joomla 6 installation Backward Compatibility 6 is installed but disabled by default New extensions should not need the bridge
    Joomla 6 after every extension passes native testing Backward Compatibility 6 can be disabled This is the desired end state

    Current context as of September 2026: Joomla 6.1.3 and 5.4.8 are the latest stable maintenance releases. Joomla 5.4 remains the bridge line to Joomla 6. Check the Joomla roadmap before planning production work.

    What backward compatibility means in Joomla

    Backward compatibility means newer Joomla code continues to accept supported older APIs, data structures, or behavior for a defined period. Joomla guarantees compatibility within a major series, subject to exceptional security fixes. A new major series is where deprecated technical debt can be removed.

    Joomla's policy says deprecated code can be moved into a compatibility plugin. That lets the core become cleaner without forcing every third-party extension to be rewritten on the exact day a major version ships.

    An extension is only fully compatible with a Joomla major version when it works with that version's compatibility plugin disabled. A directory badge that says "works on Joomla 6 with compatibility plugin" is useful, but it is not the same as native Joomla 6 compatibility.

    Why Joomla introduced the plugin

    Joomla had accumulated years of deprecated APIs while moving from global class names toward PHP namespaces, replacing old JavaScript assets, and modernizing its framework.

    For example, older extensions used global classes such as JFactory, JTable, JPlugin, and JText. Namespaced replacements started arriving during the Joomla 3 era. Removing every alias at once in Joomla 5 would have broken many otherwise usable extensions.

    The compatibility plugin solved four product problems:

    1. Safer first boot after a major update. System and behaviour plugins may execute before an administrator can log in and fix them.
    2. Time for extension developers. Vendors could support the new major while removing deprecated dependencies in stages.
    3. A measurable migration path. Administrators can disable individual shims or the entire plugin to discover what remains.
    4. Cleaner core. Deprecated code can leave the main core without disappearing immediately from upgraded sites.

    It was implemented in the special behaviour plugin group because it must load before other plugins that might call a legacy class during initialization. Joomla explicitly warns third-party developers not to create their own behaviour plugins because this group exists for an internal early-loading purpose and may change in the future.

    Timeline: when it started and how it evolved

    Date / release What happened Why it matters
    Joomla 3.x era Namespaced replacements gradually superseded many global J* classes Extensions had a long deprecation runway
    Joomla 4.4 Joomla 5 transition code was staged before the major update The bridge could be ready before Joomla 5 files replaced the site
    Joomla 5.0, October 2023 Behaviour – Backward Compatibility became the Joomla 4 to 5 bridge It supplied class aliases and web asset shims
    Joomla 5.4, October 2025 Behaviour – Backward Compatibility 6 was installed and enabled as a no-op bridge seed It would already be active when Joomla 6 code arrived
    Joomla 6.0, October 2025 The old Joomla 5 bridge was replaced by Backward Compatibility 6 Selected Joomla 5-era APIs moved behind the new bridge
    Joomla 6.x today Upgraded sites can use compat6 temporarily; fresh installs keep it disabled Native compatibility remains the target

    The official Joomla 5 plugin manifest identifies version 5.0.0, while its PHP class is marked @since 4.4.0. That reflects the staging strategy: place the bridge during the final minor of the old major so it is available at the first boot of the new major.

    The two plugins people confuse

    Behaviour – Backward Compatibility

    This is the Joomla 5 compatibility plugin. Its extension element is compat, in the behaviour folder.

    It bridges selected Joomla 4-era dependencies while running Joomla 5. Before upgrading from Joomla 5.4 to Joomla 6, it must be disabled. If disabling it breaks the site, one or more extensions are not ready for Joomla 6.

    Behaviour – Backward Compatibility 6

    This is the Joomla 6 compatibility plugin. Its extension element is compat6, also in the behaviour folder.

    It is installed and enabled on Joomla 5.4 but deliberately does no compatibility work while Joomla 5 code is still running. It is waiting for the Joomla 6 codebase. It must be enabled before the Joomla 5.4 to 6 upgrade.

    After the site runs successfully on Joomla 6 and every extension is native, this plugin can be disabled.

    What the Joomla 5 plugin options do

    The Joomla 5 plugin has three separately configurable options.

    Option What it supplies What dependence looks like
    Classes Aliases Aliases for classes renamed or moved into namespaces Errors such as Class "JTable" not found after disabling
    ES5 Assets Empty compatibility entries for removed .es5 web assets WebAssetManager throws an asset-not-found exception
    Removed Assets Empty registry entries for Joomla 4 assets removed in Joomla 5 An extension requests an old CSS/JS asset by name

    The ES5 and removed-asset shims are usually empty. They prevent exceptions; they do not recreate obsolete browser code or restore an old interface. If an extension relied on the actual removed asset behavior, an empty registry entry may stop the crash but cannot guarantee that the feature still works.

    Source: Joomla 5 compatibility plugin implementation.

    What the Joomla 6 plugin options do

    Joomla 6's compat6 plugin has a different set of bridges.

    Option What it supplies Typical dependency
    Classes Aliases Aliases for renamed or moved classes Old global or moved class references
    Include Deprecated Classes Loads selected Joomla CMS classes moved into plugins/behaviour/compat6/classes Code using packages removed from Joomla 6 core
    Removed Assets Empty registry entries for assets removed from Joomla 5 to 6 Old WebAssetManager dependencies

    Concrete examples documented for Joomla 6 include the old Joomla\CMS\Input namespace and the deprecated Joomla\CMS\Filesystem package. Native code should move to Joomla\Input and Joomla\Filesystem. Joomla's removed and backward-incompatible list is the authoritative developer checklist.

    Source: compat6 implementation.

    What the plugin does not do

    The plugin is intentionally limited. It does not:

    • Make every Joomla 3 or Joomla 4 extension work on Joomla 5 or 6
    • Fix PHP version incompatibility
    • Rewrite an obsolete extension manifest
    • Repair database schemas or failed update SQL
    • Restore removed third-party libraries
    • Convert legacy event handling automatically
    • Fix template overrides after core markup changes
    • Make an abandoned extension secure
    • Replace extension updates from the vendor
    • Downgrade Joomla or reverse an upgrade

    If an extension claims support only because the compatibility plugin hides one missing class, test its complete workflow. Loading without an exception is not the same as functioning correctly.

    Benefits of keeping it enabled during transition

    Safer major upgrades

    The first request after an update may load third-party system plugins before you can reach Administrator. Early class aliases reduce the chance of an immediate fatal error.

    Controlled extension migration

    You can upgrade the site platform first, then replace or refactor extensions on staging in a managed sequence.

    Faster diagnosis

    Turning options off one at a time can reveal whether an extension depends on class aliases, deprecated classes, or asset shims.

    Longer useful life for maintained extensions

    A vendor can ship a release that works on two Joomla majors while completing native migration work.

    Does leaving it enabled hamper the website

    Usually not in a noticeable way. The plugin loads early, may include a class map, registers a namespace for legacy classes, and may add small web asset registry files. On a normal site, that overhead is generally minor compared with database queries, page builders, images, and network latency.

    However, "small runtime overhead" is not the same as "leave it forever." The larger costs are operational:

    • It can hide extensions that are not truly ready for the next major version.
    • It keeps legacy code paths available.
    • It delays vendor accountability and cleanup.
    • It can make a future major upgrade fail when that bridge is removed.
    • Troubleshooting becomes harder because the site behaves differently with and without shims.

    There is no honest universal millisecond figure. Impact depends on enabled options, opcode cache, extension code, and request type. Benchmark your site before and after on staging if performance is a concern.

    Security implications

    Enabling the official core plugin is not inherently a vulnerability. It is maintained as part of Joomla and exists for supported upgrade paths.

    The risk is dependency, not the switch itself. An old extension that requires legacy compatibility may also contain outdated coding patterns or unpatched vulnerabilities. The plugin does not audit, patch, sandbox, or secure that extension.

    Use this rule:

    Compatibility is a bridge to updated code, not permission to keep abandoned code.

    Keep Joomla core and extensions patched. Remove extensions you no longer use. Check compatibility and security notices in the Joomla Extensions Directory.

    When you can safely turn it off

    Turn off the active compatibility plugin when all these statements are true:

    1. Every installed component, module, plugin, template, and package explicitly supports your current Joomla major.
    2. The extension vendor says native support, not only "works with compatibility plugin."
    3. You tested frontend, Administrator, forms, login, search, checkout, scheduled tasks, CLI, API, and email on staging.
    4. Debug logging shows no missing legacy classes or assets, and a Joomla 6 compatibility check shows no extension still relying on compat6 aliases.
    5. You have a tested backup and rollback point.
    6. The site remains stable through at least one representative business workflow.

    For a fresh Joomla 6 site, leave compat6 disabled unless a known extension requires it. For an upgraded Joomla 6 site, do not race to disable it on production immediately after the core update. First prove the site, update extensions, then run the disable test on staging.

    When you should not turn it off

    Do not disable it directly on production when:

    • You have no recent backup
    • You do not have staging
    • An extension vendor explicitly requires it
    • The site uses old custom code nobody has audited
    • A Joomla 5 site still throws JFactory, JTable, JPlugin, or similar legacy-class errors
    • A Joomla 6 site still uses packages documented as moved into compat6
    • A release or checkout is in progress

    Keeping it enabled temporarily is safer than causing an outage. The correct follow-up is to identify the dependency, not pretend the plugin must stay forever.

    Safe disable procedure

    Step 1: Clone production to staging

    Use the same PHP version, database engine, web server, and extensions. A different environment can hide or invent compatibility errors.

    Step 2: Update everything first

    Update Joomla within the current major. Update every extension and template. Remove unused extensions instead of merely disabling them.

    Step 3: Back up and test the restore

    A backup you have never restored is only a hopeful archive.

    Step 4: Enable maximum diagnostics on staging

    In Global Configuration:

    • Debug System: Yes
    • Error Reporting: Maximum
    • Log Deprecated API: enable if available in your Joomla version and logging configuration

    Do not leave verbose errors exposed on production.

    Step 5: Disable options one at a time

    Start with removed assets, then ES5 assets or deprecated classes, then class aliases. Clear Joomla cache after every change. This isolates the type of dependency.

    Step 6: Test more than the homepage

    Use this test matrix:

    Area Tests
    Frontend Home, article, category blog, search, contact form, multilingual switcher
    Administrator Login, article edit/save, media, menus, modules, users, configuration
    Extensions Checkout, form submission, subscriptions, imports, exports, backups
    Background Scheduled tasks, cron, queue jobs, CLI commands
    Integration REST API, webhooks, SMTP, payment callbacks
    Template Menu, modal, tabs, tooltips, JavaScript widgets, overrides

    Step 7: Disable the whole plugin

    If all individual options pass, disable the plugin and repeat the matrix. Keep logs open for fatal errors and deprecation messages.

    Step 8: Fix the dependency, then retest

    Update, replace, or refactor the responsible extension. Do not just re-enable the bridge and close the ticket.

    Joomla 5.4 to Joomla 6: exact plugin states

    Before upgrading:

    Plugin Required state on Joomla 5.4
    Behaviour – Backward Compatibility Disabled
    Behaviour – Backward Compatibility 6 Enabled

    The pre-update checker and CLI core:update verify both conditions. If either check fails, Joomla blocks the major update. This is deliberate. It prevents an incompatible Joomla 4-era extension from crashing the site halfway through a Joomla 6 update.

    Official procedure: Joomla 5 to 6 planning and upgrade.

    The correct Joomla 5 to 6 workflow

    1. Update the current site to Joomla 5.4.x.
    2. Update every extension and template.
    3. Confirm Backward Compatibility 6 is installed and enabled.
    4. Disable the unnumbered Backward Compatibility plugin.
    5. Clear cache and test the complete site while still on Joomla 5.4.
    6. If anything breaks, re-enable the old plugin and fix the extension on Joomla 5.4.
    7. Repeat until Joomla 5.4 runs without the old bridge.
    8. Back up and verify restore.
    9. Run Joomla's pre-update check.
    10. Upgrade to Joomla 6.
    11. Test with compat6 enabled.
    12. Migrate remaining Joomla 5-era dependencies and later test compat6 disabled.

    The important safety feature is step 5. Finding the fatal error before the major update is safer than finding it during a partially completed update.

    How to identify the extension that depends on it

    Read the first useful stack frame

    A fatal error often names JTable, JFactory, JPlugin, JText, an old namespace, or a removed asset. The first stack frame inside /components, /plugins, /modules, /templates, or /administrator/components usually identifies the owner.

    Search custom and third-party code

    Examples worth searching for on a Joomla 5 site:

    JFactory
    JTable
    JPlugin
    JText
    JLoader
    JFormField
    

    Do not assume every search match is active or wrong. Vendors sometimes provide their own aliases. Use the stack trace to connect a match to a failing request.

    For Joomla 6, also search imports from removed CMS packages listed in the migration guide, such as:

    use Joomla\CMS\Input\Input;
    use Joomla\CMS\Filesystem\File;
    use Joomla\CMS\Filesystem\Folder;
    

    Replace them using the official Joomla 6 migration notes rather than blind search-and-replace.

    Disable extensions in batches on staging

    When the stack trace is unclear, disable non-core system plugins in small batches. System plugins can execute on every request and are frequent causes of an Administrator lockout.

    Check WebAssetManager exceptions

    An error naming an asset instead of a class points to ES5 or removed-asset compatibility. Update the extension's asset declaration and calls.

    Recovery when disabling the plugin breaks the site

    Fast recovery through Administrator

    If Administrator still works, open System → Manage → Plugins, search Backward Compatibility, and enable the plugin again. Clear cache.

    Database recovery when Administrator is down

    Open the #__extensions table (replace #__ with your table prefix). Find:

    • folder = behaviour, element = compat for the Joomla 5 bridge
    • folder = behaviour, element = compat6 for the Joomla 6 bridge

    Set enabled = 1 for the plugin you need to restore. Do not enable both blindly during a Joomla 5.4 to 6 upgrade attempt. Restore the state appropriate for the Joomla version, then fix the underlying extension on staging.

    Restore instead of downgrading

    Joomla does not support downgrading a core upgrade. If the update itself failed or data changed, restore the pre-upgrade backup. Do not copy old core files over a newer database and call it a rollback.

    Can you uninstall the compatibility plugin

    Do not uninstall or delete this core plugin. Disable it when it is no longer needed.

    Keeping the files installed lets Joomla updates maintain them and gives you a supported recovery path. Manually deleting core plugin files can create database/file mismatches and complicate later updates.

    Can you disable only some options

    Yes, and that is the best diagnostic method.

    On Joomla 5, an extension may need class aliases but not ES5 or removed assets. On Joomla 6, it may need deprecated classes but not removed asset placeholders. Turn options off individually on staging, clear cache, and run the test matrix.

    Partial disablement reduces the compatibility surface while showing the vendor exactly what remains.

    Extension developer migration checklist

    An extension developer should:

    1. Replace global J* class names with supported namespaced classes.
    2. Replace removed CMS packages with current framework packages where Joomla documents a replacement.
    3. Stop requesting removed ES5 or WebAssetManager entries.
    4. Use supported concrete event classes and subscriber patterns where applicable.
    5. Test installation, update, uninstall, and schema changes.
    6. Test frontend, Administrator, CLI, API, and scheduled-task contexts.
    7. Run the extension with all compatibility options disabled.
    8. Declare compatibility accurately in the update server and Joomla Extensions Directory.

    The Joomla backward compatibility policy explains what core promises and what it does not. Internal, private, final, and third-party APIs are not automatically protected.

    Common errors after disabling

    Error or symptom Likely dependency Correct response
    Class "JTable" not found Joomla 4-era global class alias Update/refactor the extension
    Class "JPlugin" not found Very old plugin base class Use current CMSPlugin and service registration
    Web asset not found Removed or ES5 asset name Update asset JSON/calls; do not rely on empty shim
    Blank frontend but Admin works Site plugin/module/template dependency Enable debug on staging and inspect stack trace
    Both frontend and Admin fail Early-loading system/behaviour plugin Re-enable via database, then isolate extension
    Joomla 6 pre-update check blocks Wrong states for compat and compat6 Disable old plugin; enable compat6
    Site works enabled but fails disabled Extension is bridge-compatible, not native Update, replace, or refactor

    Myths and facts

    Myth Fact
    The plugin converts Joomla 4 extensions to Joomla 5 It supplies selected aliases and shims; it does not rewrite extension code
    Enabled means the site is insecure The core plugin is supported; the concern is outdated dependent extensions
    Disabled always makes Joomla faster Runtime savings are normally small; clean architecture is the bigger benefit
    Joomla 5.4 should have both plugins disabled before Joomla 6 Wrong. Old compat disabled; compat6 enabled
    A Joomla 6 badge means no bridge is needed Check whether the vendor means native compatibility or compatibility with compat6
    If the site loads, the extension is compatible You must test saves, jobs, forms, API, email, and other workflows
    Delete the plugin when done Disable it; do not delete core plugin files

    Recommended operating policy

    Use a simple policy across client sites:

    1. Record the active compatibility plugin and option states in the maintenance runbook.
    2. Review the state after every extension release cycle.
    3. Run a disabled test on staging at least quarterly.
    4. Reject new extensions that require a bridge without a vendor migration plan.
    5. Make "runs with compatibility plugin disabled" an acceptance criterion for custom extension work.
    6. Remove abandoned extensions before the next Joomla major.

    This turns the plugin from permanent mystery infrastructure into measurable migration debt.

    Key takeaways

    1. Joomla introduced compatibility plugins to make major upgrades safer while removing deprecated code from core.
    2. The Joomla 5 plugin bridges selected Joomla 4 dependencies; compat6 bridges selected Joomla 5 dependencies.
    3. On Joomla 5.4 before Joomla 6, disable the unnumbered plugin and enable Backward Compatibility 6.
    4. Leaving the correct plugin enabled usually has minor runtime overhead, but it can hide extension debt.
    5. The goal is native compatibility, proven on staging with the plugin disabled.
    6. Never delete the core plugin. Disable it and keep a tested recovery path.
    7. A successful homepage is not enough; test Admin, forms, jobs, API, email, and extension workflows.

    Frequently asked questions

    What is the Joomla Behaviour – Backward Compatibility plugin?

    It is a core transition plugin that restores selected legacy classes and asset registrations after a major Joomla upgrade so older extensions have time to become native.

    When was the plugin introduced?

    The Joomla 5 bridge was staged in Joomla 4.4 and became the compatibility layer for Joomla 5.0 in October 2023. The Joomla 6 bridge was staged in Joomla 5.4 and became active with Joomla 6.0 in October 2025.

    Should I keep the plugin enabled?

    Keep it enabled while a maintained extension genuinely needs it. Update or replace that dependency and test disabling the plugin on staging.

    Does the compatibility plugin slow down Joomla?

    Usually not noticeably. It loads aliases, optional legacy classes, and small asset registries. The bigger cost is hidden technical debt rather than page-load time.

    Is it a security risk?

    The official plugin itself is supported Joomla core code. The risk is that it may keep an old, potentially vulnerable third-party extension running. It does not patch extensions.

    Can I turn it off on Joomla 5?

    Yes, after testing. In fact, the unnumbered Joomla 5 plugin must be disabled before upgrading from Joomla 5.4 to Joomla 6.

    Which plugin must be enabled before Joomla 6?

    Behaviour – Backward Compatibility 6 (compat6) must be installed and enabled on Joomla 5.4. The unnumbered Joomla 5 plugin (compat) must be disabled.

    Can I turn off Backward Compatibility 6 after upgrading?

    Yes, once every extension and custom integration works natively on Joomla 6. Test it on staging first.

    Why did my site break when I disabled it?

    An extension, template, or custom plugin still calls a class or asset supplied by the bridge. Re-enable it, inspect the stack trace, and update or refactor the responsible extension.

    How do I re-enable it if Administrator is broken?

    In #__extensions, find the behaviour plugin with element compat or compat6 and set enabled to 1. Then clear cache and investigate the failing extension.

    Can I uninstall the plugin?

    No. Treat it as a core plugin and disable it when unused. Deleting it can create file/database mismatches and complicate updates.

    Does the plugin make a Joomla 3 extension work on Joomla 6?

    No. It only supplies selected compatibility cases from the previous major. A Joomla 3 extension may also depend on removed PHP APIs, manifests, events, libraries, and database behavior.

    How do I know an extension is natively compatible?

    It installs, updates, and passes its full workflow on the current Joomla major with the relevant compatibility plugin disabled.

    Where can Infyways help?

    Infyways provides Joomla upgrades, extension development, and support and maintenance for compatibility audits and major-version transitions.

  • Joomla Template Override Disappeared After an Update

    Joomla Template Override Disappeared After an Update

    A Joomla template override disappeared after an update because you edited the parent template (usually Cassiopeia html/), and the package put the stock files back. Child html/ is not part of that package. It stays. This is a file that vanished from disk (or was restored to vendor markup). It is not “the override never loaded.” If the PHP is still there and the public page ignores it, use Joomla template override not working instead.

    Joomla and club-template updates are allowed to replace parent files. That is how security fixes arrive. Your job is to stop storing custom PHP on a path the installer owns. Move the copy into a child template html/ folder, assign that style, and leave Cassiopeia stock. Same rule as CSS and JS: customize without editing core.

    Joomla update restoring parent html while the child html folder still holds the copy

    The update owns the parent. It does not own the child. If you customized Cassiopeia in place, the next package looks like your override “disappeared.”

    What you will learn

    • How to tell a wiped parent file from an override that never loaded
    • Why Cassiopeia html/ is not a safe home, even when Create Overrides offered it
    • How to recover markup from backup before you recreate anything
    • How to move surviving files into child html/ (including plugin plg_ copies)
    • What the template Overrides list means after the next update (stale vs gone)
    • How to keep the parent inheritable so you are not maintaining a fork

    Official references: Layout Overrides in Joomla, Child Templates, Template Overrides.

    What happened This article The other article
    FTP: html/com_content/article/default.php is gone or matches core again Yes. Parent was replaced. No
    FTP: your probe class is still in the file, public HTML is stock No Override not working
    You switched the default style to a new child and Home went stock Maybe both: files still on parent, style now child Assign and copy into the child
    Club zip reinstalled the template you renamed as “Cassiopeia custom” Yes, if that folder is what the zip overwrites Path/filename issues if the file remains
    Core layout changed and your copy still exists but looks wrong Stale override. Diff it. File did not disappear. Only if the path was always wrong

    Step 1: Confirm the file is gone (or stock) on disk

    Do not trust memory of “it used to look different.”

    1. Note the view you customized (article, blog, mod_menu, plugin Prev/Next).
    2. On the server, open parent templates/cassiopeia/html/ (or the club parent you actually edited).
    3. Compare with a stock copy: same Joomla version, unmodified Cassiopeia, or the extension’s tmpl in components/ / modules/ / plugins/.

    Outcomes:

    • Missing file: the update deleted a file that is not in the package (some hosts sync to the zip). Or you never committed it and a deploy overwrote the tree. Restore from backup first (Step 2).
    • File exists, content is vendor: the installer replaced your PHP. This is the usual Cassiopeia / extension update.
    • File exists, your markup is still there: this is not a disappearance. Go to override not working (wrong style, _default.php, alternative layout, plugin HTML).

    Also look at the child, if you have one: templates/{child}/html/. If the custom PHP is only on the parent, assigning the child after the update will look like a wipe even when the parent file was restored. The child never had a copy. That is still this article’s fix: copy into the child, do not re-edit the parent.

    Administrator overrides live under administrator/templates/atum/html/ (or an Atum child). A Joomla update can restore Atum the same way. Site and admin are separate trees.

    Step 2: Recover the last good copy from backup

    Do not recreate a 400-line article layout from a screenshot if last night’s backup still has it.

    1. Take a new backup before you copy anything around (so a bad paste has a rollback).
    2. Restore only the override files you need from the pre-update backup into a staging folder on your desk, not straight onto production parent html/.
    3. Diff against current core tmpl for this Joomla version. Core may have changed. Pasting an old override onto new Joomla can fatal. Official advice is to compare overrides after updates. The template UI Overrides (Updated Files) list is for copies that still exist and drifted from core. A replaced parent file will not show your old diff because your old file is gone.

    If you have no backup, you rebuild from Create Overrides on the child (Step 3) and re-apply the HTML you remember. That is slower. It is still safer than patching Cassiopeia again.

    Step 3: Create or open a child and put html/ there

    On Joomla 4.1, 5, and 6:

    1. System → Site Templates → Cassiopeia (inheritable parent) → Create Child Template if you do not already have one. Clicks: How to set up a Joomla child template.
    2. Open the child → Create Overrides for each component or module you had customized. That copies current core layouts into templates/{child}/html/.
    3. Merge your recovered markup into those child files. Keep new core PHP (variables, escaping) unless you know why you are dropping it.
    4. Plugin layouts: Create Overrides usually will not list them. Recreate html/plg_{group}_{element}/ by hand and paste the recovered tmpl. Guide: Joomla plugin override.

    A child does not magically inherit every parent html/ file. Same-named files in the child win. Everything you never copied still comes from the parent package. After an update, parent html/ is stock again. If you needed a customization, it must exist in the child folder.

    Do not zip-clone the whole Cassiopeia tree and call it a child. That is a fork. The next Cassiopeia security fix will not reach it, and the next vendor zip may overwrite it anyway.

    Joomla 6 Cassiopeia Extended is already a child. Put overrides on Extended (or on your own child), not on Cassiopeia parent.

    Step 4: Assign the child style, then stop editing the parent

    Creating the child does not change the public site.

    1. System → Site Template Styles. Make the child’s style default, or set it on the menu items that need the markup.
    2. Clear Cache. Test the URL as a guest if Page Cache is on.
    3. Confirm View Source has your markup (probe class).
    4. Leave templates/cassiopeia/html/ empty of custom files. If Create Overrides on the parent still looks convenient, close it. Use the child only.

    A template style duplicate is not a child. Styles store params and assignment. They still point at the same parent files. Duplicating “Cassiopeia – Default” and editing PHP in Cassiopeia will disappear again on the next update.

    Language strings do not belong in restored PHP. Use language overrides. CSS that died with the parent user.css is the CSS checklist: Joomla CSS changes not showing. Parent user.css is also wiped or orphaned when you switch to a child. Copy it into the child’s media folder.

    Step 5: After the next update, use Overrides as a stale check, not as a time machine

    When Joomla updates a layout you overrode in the child, the file does not vanish. It can become wrong relative to core (missing a new variable, old markup).

    1. Open the child in Template Manager.
    2. Check Overrides / updated-files style notices (wording varies by version).
    3. Diff your copy against the new core tmpl. Merge.

    That list does not watch user.css or user.js. Review those yourself. It also will not bring back a parent file the installer already replaced. Prevention is the child, not the notice.

    Club templates: read the vendor’s child or “custom” folder rules. If their zip replaces templates/yourname/html/, you are still on a parent-shaped path. Ask for an inheritable parent or keep a true Joomla child.

    Five checks: file gone on disk, parent was edited, backup, move to child html, assign child style

    Disk first. If the file is still there, you are on the not-working article. If it is stock or missing, backup, child html, assign the style, never edit the parent again.

    Key takeaways

    1. Disappeared means the custom PHP is gone or stock on disk after an update. Not-working means the file is still there and unused.
    2. Parent Cassiopeia html/ is owned by the installer. Child html/ is yours.
    3. Create Overrides on the parent is how this incident starts. Run it on the child.
    4. Restore from backup into the child, then diff against current core. Do not paste an old override onto new Joomla blindly.
    5. Assign the child style. Creating a child does not publish it.
    6. Plugin copies in parent html/plg_… disappear the same way. Recreate them on the child: plugin override.
    7. After future updates, diff stale child overrides. That is not a disappearance.
    8. Keep the stack in customize without editing core.

    Frequently asked questions

    Why did my Joomla template override disappear after an update?

    You stored it in the parent template. The Joomla or club package replaced those files. Move the override into a child html/ folder and assign that style.

    Is this the same as a template override not working?

    No. Not working: the file is on disk and Joomla never includes it (folder, style, _default.php, alternative layout, plugin). Disappeared: the file was restored or deleted by an update. Different checklists.

    Will a child template survive a Joomla update?

    The child folder is not in the Cassiopeia package, so your html/ copies stay. You still diff them if core layouts change. Official description: Child Templates.

    Can I copy the whole Cassiopeia folder instead of a child?

    You can. You then own a fork. Security fixes in Cassiopeia will not apply until you merge by hand. Use Create Child Template on Joomla 4.1+.

    Where do I put plugin overrides so they do not vanish?

    templates/{child}/html/plg_{group}_{element}/, same as other overrides. Manual copy: Joomla plugin override.

    The Overrides tab still lists my file. Did it disappear?

    No. That list is for overrides that exist and may be out of date versus core. A true disappearance is a missing or stock file in parent html/ after the package ran.

    Should I edit Cassiopeia html again “just this once”?

    No. That is how this article starts the next time you click Update. Use the child. Setup: Joomla child template.

    Conclusion

    When a Joomla template override disappeared after an update, believe the disk. Parent html/ comes back from the zip. Child html/ does not. Restore the last good PHP on staging, merge it into a child created with Create Child Template, assign that style, and leave Cassiopeia alone. If the file is still sitting on disk and the site ignores it, you opened the wrong guide: override not working.

    If production was customized for years inside the parent, Joomla support and maintenance can move the tree to a child without another surprise on the next security release. Layout you would rather not own: Joomla design services.

  • Joomla Template Override Not Working: Causes and Fixes

    Joomla Template Override Not Working: Causes and Fixes

    A Joomla template override is not working when Joomla never loads the PHP file you edited. The usual causes are the wrong html/ folder, a template style that is not the one assigned to the page, a filename of _default.php (a sublayout, not the layout), an alternative layout selected on the menu item or module, plugin HTML that does not live in the component override, or cache still serving the old markup. This is a file that never loaded. If the file used to work and vanished after an update, that is a different article: template override disappeared after an update.

    Create Overrides is the safe copy for components and modules. Plugin tmpl is usually copied by hand into html/plg_…. Official map: Layout Overrides in Joomla. User-manual walkthrough: Template Overrides. Put the copy on a child template, not in parent Cassiopeia.

    _default.php rejected versus default.php in html/com_content/article

    The layout Joomla looks for is default.php. A leading underscore is a sublayout. The assigned template style must be the folder that contains the file.

    What you will learn

    • How to prove the override file is the one PHP included
    • Why Create Overrides for com_ and mod_ is safer than a hand-made path
    • Why plugin HTML is usually html/plg_…, not the article default.php
    • Why _default.php never replaces default.php
    • What an alternative layout on the menu item actually selects
    • Why cache and the wrong template style look like a “dead” override
    What you see Likely cause Wrong rabbit hole
    Stock markup, your PHP comment missing Wrong folder, wrong template style, or _default.php Editing components/
    Override works on Home, not on a landing page That menu item uses another template style Recreating the override
    Article body changed, Prev/Next did not Page navigation is a plugin layout More com_content PHP
    Blog looks stock, single article changed You overrode article/default.php. The blog uses articles/… (or blog.php) Cache only
    File on disk, still stock Alternative layout selected, or cache Reinstalling the component
    Child looks stock Override still sits in parent html/ “Overrides are broken”

    Step 1: Prove which template is assigned, then prove the file is included

    Overrides are files on a template. They are not a Global Configuration switch.

    1. Open the public URL that should use the override.
    2. System → Site Template Styles. Note the default (star).
    3. Open that menu item. Template Style may be a different style (child vs parent, or a club template).

    If the page uses cassiopeia_brand and you edited templates/cassiopeia/html/…, Joomla will not load your copy. Child html/ wins for that style. Parent Cassiopeia html/ is unused when the child is assigned. The reverse is also true: assign the parent, and the child’s override is ignored.

    Prove inclusion:

    1. On the file you believe is active, add a unique class on a wrapper you already have, for example js-override-probe. Do not use a core-edit in components/.
    2. System → Maintenance → Clear Cache. Test logged out if Page Cache is on.
    3. View Source. Search for js-override-probe.

    If the class is missing, Joomla did not include that file. Keep going. If the class is present and the page still “looks stock,” you changed the wrong markup, or CSS is hiding it: Joomla CSS changes not showing.

    Step 2: Use Create Overrides for components and modules

    Hand-built folders are how silent misses happen.

    1. System → Templates → Site Templates → open the active template (the child).
    2. Open Create Overrides.
    3. Pick the component view (com_content → article, category, …) or the module (mod_login, mod_menu, …).
    4. Joomla copies into templates/{template}/html/com_… or html/mod_….
    5. Edit that copy only.

    Typical paths:

    • Article: templates/{child}/html/com_content/article/default.php
    • Category blog: templates/{child}/html/com_content/category/blog.php (and often blog_item.php as a sublayout)
    • Module: templates/{child}/html/mod_login/default.php

    Wrong folders that fail silently:

    • html/com_content/articles/ when the view is article (singular) or the reverse
    • html/modules/mod_login/ (extra modules segment)
    • html/com_content/article/tmpl/default.php (no extra tmpl under html)
    • An override on Atum while you are looking at the site

    Create Overrides exists so the path matches what Joomla searches. Official folder rules: Layout Overrides in Joomla. Joomla 4 layout notes: Template Layouts.

    JLayouts (Read more, images, fields chrome) are not always the view default.php. They live under html/layouts/… after you override them from the Layouts list. If you changed article/default.php and the intro image is still stock, you needed html/layouts/joomla/content/…. The user manual shows the LayoutHelper::render mapping: Template Overrides.

    Step 3: Name the file default.php, not _default.php

    The layout file is default.php. A leading underscore marks a sublayout. Joomla will not use _default.php as the main view.

    Create Overrides copies default.php. Some views also copy default_logout.php, blog_item.php, and similar. Those extra files are included from the main layout ($this->loadTemplate('item') looks for {layout}_item.php). They do not replace default.php.

    If you renamed the copy to _default.php because a blog said “underscore means override,” you hid the file from the layout resolver. Rename it back to default.php.

    If you only edited blog_item.php while the menu item still uses the default layout (not blog), that sublayout is never included. Match the layout name Joomla is actually rendering (next step).

    Step 4: Check Alternative Layout on the menu item, module, or article

    Joomla can select a file that is not default.php.

    On the menu item: Options (or the Blog / Article tab) → Layout / Alternative Layout. Values such as blog, a template-specific layout, or a custom mylayout.php mean your default.php override is idle for that view.

    On a module: Advanced → Alternative Layout. A module can use default.php on one instance and a custom layout on another. You overrode the file the instance is not using.

    On an article: some views let the article pick a layout. If that is set, it wins for that article.

    Fix:

    • Set Alternative Layout back to default (or inherited), or
    • Create or copy the override with the same basename as the selected layout (blog.php, mylayout.php), still without a leading underscore.

    A custom alternative layout is a second file in html/com_…/. It is not a child-template feature by itself. The child only decides which template’s html/ is searched.

    Step 5: If the HTML comes from a plugin, do not expect a component override

    Content plugins inject markup after (or beside) the component layout. Prev/Next, vote, some field types, and many extra buttons are plugin tmpl files.

    Overriding html/com_content/article/default.php will not restyle page navigation. You need html/plg_content_pagenavigation/default.php (and a plugin that actually calls getLayoutPath). Create Overrides usually does not list plugins. You copy tmpl by hand.

    Full path, tmpl test, and what cannot be overridden: Joomla plugin override.

    If the string is only a label, use a language override instead of PHP.

    Step 6: Clear cache, then decide if the file was never going to survive an update

    After a path fix:

    1. Clear Cache (and Page Cache for guests).
    2. Hard refresh.
    3. Search View Source for your probe class again.

    If the override used to appear and the PHP file is gone from disk after a Joomla or template update, stop this article. You edited the parent. Read template override disappeared after an update.

    If the file is still there and still unused, you are still on folder, style, filename, alternative layout, or plugin output.

    Keep new overrides on the child so the next Cassiopeia package cannot replace them. Policy: customize without editing core. Visual markup you do not want to maintain: Joomla design services.

    Five checks: folder, template style, default.php, alternative layout, plugin output

    Folder path, then the assigned style, then default.php versus a sublayout, then Alternative Layout, then plugin HTML. Cache last.

    Key takeaways

    1. An override that “does nothing” did not load. Prove it with a probe class in View Source after Clear Cache.
    2. Create Overrides for com_ and mod_ on the active child. Hand-typed folders fail silently.
    3. Plugin output is html/plg_{group}_{element}/, usually manual. Guide: plugin override.
    4. The main file is default.php. _default.php is a sublayout. blog_item.php does not replace blog.php.
    5. Alternative Layout on the menu item or module selects a different basename. Override that file, or set layout back to default.
    6. Child html/ wins only when that child’s style is assigned. Parent and child are not a merge of every PHP file.
    7. JLayouts live under html/layouts/, not always in the view default.php.
    8. A file that vanished from disk after an update is not this diagnosis. Use the disappeared checklist.

    Frequently asked questions

    Why is my Joomla template override not working?

    Joomla is not including that PHP file. The folder does not match the view, the assigned template style is a different template, the file is named _default.php, Alternative Layout points at another basename, or the HTML comes from a plugin. Prove it with a probe class in View Source.

    Should the override file be named _default.php?

    No. Name it default.php (or blog.php if that is the layout in use). A leading underscore is a sublayout. Create Overrides already uses the correct names.

    Why did Create Overrides copy default.php and default_logout.php?

    default_logout.php is a sublayout of the login module’s default layout. Edit the file that prints the markup you care about. Deleting default.php when you only wanted logout is correct if you do not want to override the login form. The user manual does exactly that: Template Overrides.

    Can I override a plugin from the article layout?

    No. Plugin HTML that uses tmpl is overridden under html/plg_…. See Joomla plugin override.

    Does a child template automatically use the parent html folder?

    No. Same-named files in the child win. Files you never copied stay on the parent. An override that exists only on the parent is unused while the child style is assigned.

    I updated Joomla and the override vanished. Is that this article?

    No. That is the parent getting restored. Use Joomla template override disappeared after an update.

    Where are the official override rules?

    Layout Overrides in Joomla and Understanding Output Overrides. Child placement: Child Templates.

    Conclusion

    When a Joomla template override is not working, stop editing components/ and stop renaming files with a leading underscore. Assign the child style, put default.php (or the alternative layout’s real name) in the matching html/com_ or html/mod_ folder, and treat plugin markup as html/plg_. A probe class in View Source tells you in one reload whether PHP included the file. If the file used to exist and the disk is stock after an update, switch to the disappeared guide and move the work into a child.

    Need the markup rebuilt without another silent path? Joomla design services or Joomla support and maintenance.

  • Joomla JavaScript Not Working

    Joomla JavaScript Not Working

    Joomla JavaScript is not working when the browser never executes the file you think you added, or it executes and throws before your code runs. Open the console first. Then confirm user.js is requested from media/templates/site/{template}/js/user.js on the active template (a child does not load the parent’s user.js). Typical failures after that are $ is not defined (jQuery is not on the page), an ES module in a classic deferred script, script order, Content-Security-Policy, or another extension overwriting the same global.

    This article is why it fails. The how-to for adding a file the supported way is Add custom JavaScript to Joomla. Keep custom JS on a child template, not in core index.php. Same overlay rule as CSS: customize without editing core.

    Console error, missing user.js, and jQuery dollar undefined

    Read the console before you add another script tag. $ is not shipped on Cassiopeia by default. CSP can block a file that 200s in Network.

    What you will learn

    • How to separate a 404 path from a runtime exception
    • Where Cassiopeia user.js lives (and why the child ignores the parent file)
    • Why $ and jQuery fail on Joomla 4, 5, and 6 front ends
    • What defer and ES modules do to order
    • How the HTTP Headers plugin (CSP) blocks inline or third-party scripts
    • How to spot a conflict without disabling the whole site in public

    Official references: Web Asset Manager, Adding JavaScript and CSS to the page, Cassiopeia Template Customisation, HTTP Headers plugin.

    What you see Likely cause Wrong rabbit hole
    Console Failed to load resource / 404 user.js on the parent, old templates/…/js/ path, or user.js.js Rewriting the script
    $ is not defined / jQuery is not defined Cassiopeia does not load jQuery unless an extension called it Copy-pasting a Joomla 3 snippet
    Cannot use import statement outside a module ES import inside Cassiopeia’s classic user.js (deferred, not type="module") More defer
    Script in Network 200, nothing happens Error earlier in the same file, wrong selector, or you tested a page that does not include the node Cache only
    Console CSP Refused to execute HTTP Headers plugin or server Content-Security-Policy Disabling SEF
    Works in admin, fails on the site Site template user.js vs Atum. Two templates, two files “Joomla JS is broken”
    Works logged in, fails as guest Page Cache serving HTML without your new <script>, or a guest-only extension Access on a module

    Step 1: Read the browser console before you add another file

    Do not stack a second snippet on top of a SyntaxError.

    1. Open the public page that should run the script. Use a private window so extensions and your admin session are not in the way.
    2. DevTools → Console. Reload.
    3. DevTools → Network → filter JS. Reload again.

    Write down, in this order:

    • The first red error (file name and line). Later errors are often fallout.
    • Whether user.js (or the extension file you care about) appears, and its HTTP status.
    • Whether the URL is /media/templates/site/{active-template}/js/user.js.

    A 404 is a path problem (Step 2). A 200 plus a TypeError is a runtime problem (Steps 3 to 6). No request at all means the template never registered the asset, or you pasted the script in a Custom module that is unpublished: Joomla module not showing.

    If the console is clean and the feature still “does nothing,” the script ran against markup that is not on this view. Inspect the DOM. CSS hiding the node is CSS changes not showing, not a JS failure.

    Step 2: Put user.js on the media path of the active template

    Cassiopeia loads user.js the same way it loads user.css: from the assigned template’s media folder.

    On Joomla 4.1, 5, and 6:

    media/templates/site/{template}/js/user.js

    A child named cassiopeia_brand uses media/templates/site/cassiopeia_brand/js/user.js. A child does not load the parent’s user.js. If you added the file on Cassiopeia and then assigned the child, the public page will not request the parent script.

    In Template Manager:

    1. System → Templates → Site Templates → the active template (child if you have one).
    2. New File. Select the js folder.
    3. Filename: user with no suffix. File type: .js.
    4. Create, paste, Save.

    The Joomla 4.0-era path templates/cassiopeia/js/user.js is the same class of mistake as the old CSS folder. Current Cassiopeia looks in media/. Filename user.js.js 404s for the same reason as user.css.css.

    Confirm the menu item’s Template Style. Home can use the child while the page you tested still uses the parent. Two styles, two user.js files.

    Cassiopeia typically registers user.js through the Web Asset Manager as a deferred classic script, not as type="module". That matters in Step 4.

    How to add JS through WAM, a Custom module, or an extension asset JSON: Add custom JavaScript to Joomla. This checklist stops when the file is in the right folder and the Network tab shows 200.

    Step 3: Separate jQuery snippets from vanilla and from ES modules

    Joomla 4, 5, and 6 front ends are not Joomla 3.

    Cassiopeia uses Bootstrap 5. It does not enqueue jQuery for every page. $ and jQuery exist only if some extension called the jQuery asset (jquery / jquery-noconflict in the Web Asset Manager). A blog you copied that starts with jQuery(document).ready(...) will throw $ is not defined on a stock site.

    Fixes that are honest:

    • Rewrite the snippet in vanilla JS (document.addEventListener('DOMContentLoaded', …)).
    • If you truly need jQuery, load it as a dependency of your asset, not by pasting a second copy of jQuery 1.12 into user.js. Duplicate jQuery is a classic conflict (Step 6).

    ES modules: import / export are a SyntaxError in a non-module script. Cassiopeia’s user.js is a normal deferred file. Do not put import { … } from '…' in it unless you register that file as a module asset (type="module") in joomla.asset.json. Mixing module syntax into user.js is why “I followed a Vite example and Joomla exploded.”

    document.write and inline onclick= in article HTML are separate from user.js. They fail for the same CSP reasons in Step 5, and they are harder to cache-bust. Prefer one file in the child.

    Step 4: Account for defer, order, and DOM timing

    Web Asset Manager can output defer (Cassiopeia’s user.js usually has it). Deferred scripts run in order after the document is parsed. They do not run before a script without defer that sits earlier in the HTML.

    Typical races:

    • Your user.js assumes a calendar plugin’s global already exists. That plugin’s file is deferred later, or only loads on one menu item.
    • An inline script in a Custom module runs immediately, then user.js runs later and overwrites it (or the reverse).
    • You query #mod-finder-searchword on a page that has no finder module. querySelector returns null. The next line throws. The rest of user.js never runs. Put a guard around the node, or split files.

    Do not “fix” order by pasting <script> into core index.php. Register dependencies in joomla.asset.json or load from the child the way the custom JavaScript guide describes. Official model: Web Asset Manager.

    DOMContentLoaded in a deferred user.js may have already fired. If your snippet never runs, listen for DOMContentLoaded only when document.readyState === 'loading'; otherwise run immediately.

    Step 5: Check Content-Security-Policy and mixed content

    A file can 200 in Network and still never execute.

    System → Plugins → System – HTTP Headers. If Content-Security-Policy is on, script-src may allow 'self' and block:

    • Inline <script> in a Custom module or article
    • eval / new Function (some older sliders)
    • A CDN copy of jQuery or analytics you added in user.js via a remote URL
    • unsafe-inline missing when you still have inline handlers

    The console message is explicit: Refused to execute inline script or Refused to load the script 'https://…'. That is CSP, not a Joomla path bug. Loosen the policy on staging, or move the code into user.js on the same origin so 'self' allows it. Do not turn CSP off on production to “make a snippet work.”

    HTTPS pages that still request http:// scripts are mixed content. The browser blocks them. Same class of failure as images not loading on HTTPS.

    Server-level headers (Cloudflare, nginx add_header) override or duplicate the plugin. If the plugin is off and CSP still appears in Response Headers, fix the host, not Joomla.

    Step 6: Isolate a conflict without guessing

    Two scripts can both “work” and still cancel the feature.

    Signs:

    • The console shows your console.log at the top of user.js, then a third-party file throws, then your click handler is missing.
    • Bootstrap’s data-bs-toggle stops after you load a second Bootstrap JS.
    • A Mootools-era extension and a modern module both bind window.onload.

    On staging:

    1. Backup.
    2. System → Manage → Plugins. Batch-disable recently installed system plugins (not authentication). Retest.
    3. Switch the page to stock Cassiopeia (parent style) with an empty user.js. If the extension feature returns, your script or the child’s JS is the conflict.
    4. Re-enable plugins one at a time. Ordering on the same event is Joomla extension conflict.

    Do not debug this on production by disabling the Language Filter or Page Cache in public. Staging first.

    Clear Joomla cache after JS changes. Page Cache will keep old HTML that does not include your new <script src>. Guests then look like “JS does nothing” while you, logged in, skip Page Cache and see it work. Same guest-vs-login split as cache showing old content.

    Five checks: console, jQuery vs module, defer, CSP, user.js path

    Console first. Then jQuery versus module syntax. Then defer and order. Then CSP. Then the user.js path on the active template.

    Key takeaways

    1. Joomla JavaScript not working is a console diagnosis, not a reason to paste a second <script> into core.
    2. Cassiopeia user.js is media/templates/site/{template}/js/user.js on the active template. A child does not load the parent file.
    3. New File: name user, type .js. The old templates/…/js/ path is Joomla 4.0.
    4. $ is not defined on stock Cassiopeia. Rewrite vanilla or declare jQuery as a Web Asset dependency. Do not ship two jQuery copies.
    5. import belongs in a module asset, not in default deferred user.js.
    6. Deferred order and a missing DOM node throw and abort the rest of the file.
    7. CSP from HTTP Headers or the server will refuse inline and remote scripts even when Network is 200.
    8. Add files using Add custom JavaScript to Joomla. Keep them on a child.

    Frequently asked questions

    Why is my Joomla JavaScript not working?

    The console has a 404, a $ is not defined, a module SyntaxError, a CSP refusal, or an earlier exception that stopped the file. Read the first red line. Then confirm user.js is on the assigned template’s media path.

    Where do I put user.js in Joomla 5?

    media/templates/site/{template}/js/user.js, same pattern as user.css. Create it with filename user and type .js in the template’s js folder. Official customisation notes: Cassiopeia Template Customisation.

    Does Joomla 5 still include jQuery on every page?

    No. Cassiopeia does not load jQuery unless an extension (or your asset JSON) asks for it. Joomla 3 snippets that start with $ will fail on a stock 4, 5, or 6 site.

    Can Content-Security-Policy block user.js?

    'self' allows a same-origin user.js. It still blocks inline script in articles and many CDN URLs. Check the console for Refused to execute and the HTTP Headers plugin. Docs: HTTP Headers plugin.

    Why does my script work in the administrator but not on the site?

    Administrator uses Atum (or an Atum child). The site uses Cassiopeia (or a site child). user.js is per template. Put site scripts in the site child. Put admin scripts in an administrator child.

    Should I add JavaScript in this article or in the how-to?

    Use this page to find the failure. Use Add custom JavaScript to Joomla to register the file. Do not paste into core index.php.

    I cleared cache and guests still run old JS. Why?

    System – Page Cache stores full HTML. Logged-in users skip it. Clear cache, purge the CDN, test a private window. See cache showing old content.

    Conclusion

    When Joomla JavaScript is not working, the console already knows. 404 means the user.js path (child vs parent, media/ vs the old templates/ folder). $ is not defined means a Joomla 3 habit on a Cassiopeia page. import means a module in a classic deferred file. CSP means the headers, not SEF. Conflicts mean a second Bootstrap or a system plugin on staging, not a core hack.

    Add the file the supported way, on a child, using Add custom JavaScript to Joomla. If production is a pile of inline snippets and HTTP Headers, Joomla support and maintenance is cheaper than one more script tag in index.php.