Live This site runs Joomla 6.1.2
JoomClub

News, security and craft for the Joomla ecosystem

SEO

Redirect Old Query-String URLs After Moving to Joomla

You move a catalogue site to Joomla and the old URLs look like /index.php?cid=2&tid=3&fid=345. Joomla's redirect manager sees the path — /index.php — and treats everything after the question mark as noise, so one rule would capture every…

First, work out the mapping

Before writing any rules, decide what each old URL should become. In a catalogue there are usually three cases, and they need different answers:

  • The product still exists → 301 to the new product page.
  • The product is gone but its category remains → 301 to the category. Better than a 404: the visitor arrived wanting a paving block and lands on paving blocks.
  • Neither exists → 404, with a page that explains and offers the catalogue. Do not redirect these to the front page. A redirect that lands nowhere useful is a worse experience than an honest 404, and search engines treat mass redirects to the home page as soft 404s anyway.

Export the old product table and generate the pairs mechanically — old ID to new path. On a catalogue of any size this is a script, not an afternoon of copying.

Option 1: rewrite rules

The query string is available to Apache as %{QUERY_STRING}, so the mapping can live in .htaccess above Joomla's own rules:

RewriteEngine On

# Category pages
RewriteCond %{QUERY_STRING} (^|&)tid=1(&|$)
RewriteRule ^index\.php$ /completed-projects? [R=301,L]

RewriteCond %{QUERY_STRING} (^|&)tid=8(&|$)
RewriteRule ^index\.php$ /bricks? [R=301,L]

# A specific product
RewriteCond %{QUERY_STRING} (^|&)fid=345(&|$)
RewriteRule ^index\.php$ /blocks/block-b01? [R=301,L]

The trailing ? in the target matters: without it Apache appends the original query string to the new URL, and you end up with /blocks/block-b01?cid=2&tid=3&fid=345.

This is fine for tens of rules. At hundreds it becomes slow — every request is matched against every rule — and at that point use a rewrite map or the plugin below.

On nginx the same logic goes in the server block with if ($args ~ "fid=345"), though a map block is the cleaner form there.

Option 2: a small system plugin

This is the version that survives updates, and it is not much code. A system plugin reading the input early in the request:

use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;

final class PlgSystemLegacyUrls extends CMSPlugin
{
    private const PRODUCTS = [
        345 => '/blocks/block-b01',
        372 => '/blocks/block-b02',
    ];

    private const CATEGORIES = [
        1 => '/completed-projects',
        8 => '/bricks',
        2 => '/blocks',
    ];

    public function onAfterInitialise(): void
    {
        $app = Factory::getApplication();

        if (!$app->isClient('site')) {
            return;
        }

        $input = $app->getInput();
        $fid   = $input->getInt('fid');
        $tid   = $input->getInt('tid');

        if ($fid && isset(self::PRODUCTS[$fid])) {
            $app->redirect(self::PRODUCTS[$fid], 301);
        }

        if ($tid && isset(self::CATEGORIES[$tid])) {
            $app->redirect(self::CATEGORIES[$tid], 301);
        }
    }
}

Note the order: a known product wins over its category, so a discontinued item falls through to the category rule rather than 404ing. For a large catalogue, keep the map in a database table instead of a constant and look it up.

What not to do

An approach that circulates for this problem is to rename Joomla's index.php to something else, put your own redirect script in its place, and repoint DirectoryIndex and the rewrite rules at the renamed file.

It works, and it breaks on the next update — Joomla restores its own index.php, and the site silently stops redirecting. Nothing in the admin interface tells you it happened. The plugin above does the same job through a supported extension point and is not touched by updates.

Afterwards

Keep the redirects. There is a persistent belief that once search engines have reindexed, the rules can be removed — but old links live in other people's bookmarks, emails and pages for years, and a 301 costs you nothing to keep.

Enable Joomla's redirect manager as well. It logs the 404s your site actually serves, which is how you find the old URLs your mapping missed — there are always some.