Joomla to WordPress Migration Without Losing Rankings
for a cardboard packaging manufacturer: three sites become two
The task
The client, a cardboard packaging manufacturer, ran three sites on Joomla 3.10 with a Gantry 5 template and the RSForm form component. The CMS was outdated with nothing to update to. Content could not be edited without a developer, and the layout fell apart on a phone.
The conditions of the move:
- content, page URLs and search rankings must not be lost;
- the client edits texts, photos, cards and contacts on their own;
- the site works on a phone as well as on a computer;
- the client has two legal entities, so two sites remain, and they must not overlap in links, contacts or admin panels.
The solution
A CMS migration is not «rebuilding the site»; it is moving content, URLs and SEO data into a new system while keeping what search engines already know. We parsed the dumps of three Joomla databases with Python scripts, cleaned the content and loaded it into WordPress.
What was done:
- Transfer of texts, nested categories, dates, page URLs and attachments; RSForm requests exported to an archive CSV.
- An idempotent import: a repeat run updates posts and creates no duplicates.
- A map of old URLs and permanent 301 redirects to the new ones.
- A catalogue of 325 FEFCO designs with die-line drawings, groups, similar designs and a call to action under every card.
- Site search: FEFCO code, everyday names through a synonym dictionary, dimensions such as «300x200x150», live suggestions.
- A child theme of Astra with the palette taken from the originals, a dark mode with a switch and a responsive layout.
- Quote-request and call-back forms on Contact Form 7 with spam protection that needs no captcha.
- A «Site» panel in the admin: more than 60 fields, separate texts, the FEFCO reference and a built-in guide.
- Login protection and one-command deployment on the server.
How it works
Moving from Joomla to WordPress from a database dump
The pipeline: SQL dumps are loaded into MySQL, Python scripts with BeautifulSoup pull out the posts and clean the HTML, the result is saved as JSON, and PHP scripts create the posts in WordPress through WP-CLI. The cleaning rules live in a single script that the export imports rather than copies. Joomla shortcodes are stripped, drafts arrive as drafts, and informational and legal posts become pages.
In Joomla, tables were used for layout as well as for price lists, including «picture and text» blocks. The parsing threshold was derived from the real data: a table is broken into blocks if it contains an image, has at most two rows and fewer than half of its cells are numeric. Of 98 tables across the three databases, 95 layouts were unpacked, and the one real 53-row price list stayed a table.
301 redirects when changing CMS
A 301 redirect is a permanent redirection: it tells a search engine that the page has moved and passes the old address's weight to the new one. Joomla has a quirk: with friendly URLs on, an article's address is built from the menu item. So the map was checked against the live original: an address built from the category returned 404, while the one built from the menu item opened. The finished rules are kept as .htaccess files and as a CSV for human review.
The FEFCO catalogue in WordPress
FEFCO is the European classification of corrugated-board packaging designs. The catalogue of 325 designs was restored with die-line drawings and sorted into nine groups. The entry point is a showcase of groups, then a section, then a card. A card has a block of similar designs and «Back» and «Next» links. The filter by design type and assembly method works as an ordinary link with no JavaScript: the result can be forwarded, and pagination keeps the chosen conditions.
Isolating two sites of one client
The sites run in separate Docker containers with separate databases, and share no links, contacts or admin panel. The absence of cross-links was verified on the assembled pages: zero in both directions.
Admin login protection
A protection module counts wrong passwords per address: after three errors sign-in is closed for 15 minutes, on a repeat within a day for an hour, then for a day. The login error is the same for a foreign username and a wrong password, so the response does not reveal a valid username. A second line sits on the front nginx: no more than 20 requests a minute to the login page, and WordPress service files are closed.
A settings panel: the client edits the site
All management is gathered in the «Site» menu: an overview with launch readiness, settings, texts, the FEFCO reference and a built-in guide. The rule is simple: an empty field means «do not use», so an unfilled Metrica counter does not load its script. The settings pages end with a change history and a «Restore as it was» button.
Results
- Three Joomla 3.10 sites are being replaced by two WordPress sites that the client manages on their own.
- The catalogue of 325 FEFCO designs is restored with drawings, search and a filter.
- Responsiveness was measured at 375 and 1280 pixels: no overflow, the menu expands and price tables turn into cards.
- Titles and descriptions are filled in for every post and category, with a sitemap, breadcrumbs, and organisation and FAQ markup.
- The migration is reproducible by script: containers, dumps, import, styling and redirects are deployed with one command.
- The sources contain no page-speed measurements, so we give no scores.
Technologies and why
- Python, sqlparse, BeautifulSoup, pymysql — parsing Joomla SQL dumps, cleaning HTML, exporting to JSON.
- WordPress 6.7 and PHP 8.2 — a new CMS with an admin the client already knows; the import, the theme and the «Site» panel are written in PHP.
- MySQL 8.0 — the sites' database and a staging database for dump parsing.
- Astra and a child theme — the base of the design, so updates to the parent theme do not break our changes.
- Rank Math SEO — titles, descriptions, sitemap, breadcrumbs, markup.
- Contact Form 7 and Flamingo — request forms and their archive in the admin.
- Docker Compose and nginx — the same environment locally and on the server, one container per site.
- Let's Encrypt with DNS-01 validation — certificates for a demo host that cannot answer HTTP checks because of server-side restrictions.
Status
Version 1.41.0 of 28 September 2026, on a demo environment at the client. The sites are shown to the client on a demo environment with working HTTPS; search indexing is deliberately off there and gets switched on at the move to the production domain. Still open: outgoing e-mail (waiting for mailbox access), analytics counters, photos for 23 posts without a cover image, a review of the FEFCO reference by the client's technologist, and scheduled backups. Other client details are not disclosed. How we build new sites from scratch is described in Websites.
Questions about this project
How do you migrate from Joomla to WordPress without losing search rankings?
Why do old URLs stop working after a move from Joomla?
What is migrated from Joomla and what stays in an archive?
Can you re-run the import without getting duplicates?
Can the client edit the site on their own after the move?
How do you secure WordPress after a migration?
More in this area
Online Shirt Store on Nuxt 4 with an Owner Admin Panel
a shirt shop in Cheboksary: the owner manages products, prices and orders alone
UNLOCK: Interactive Greeting Cards and Advent Calendars
our own studio service: a card behind a password link, managed from Telegram
DevUnit Lab: A Bilingual Studio Website on Nuxt 4
our own studio site, in production since 29 September 2026
Need something similar?
Tell us about the task — we'll show how we solved it and estimate the scope.