Migration

WooCommerce to Shopware 6: data, passwords and redirects

There is no official Shopware migration profile for WooCommerce. This guide covers what to move with which tool, how customers keep their WordPress passwords, and how your URLs survive the switch.

By Huzaifa Mustafa 6 min read October 31, 2026

The Quick Answer

Shopware's Migration Assistant has no WooCommerce profile, so you assemble the move yourself. The catalogue can come across with a Store extension or the two REST APIs. Customers and orders need a script. Passwords survive with a small legacy encoder that understands WordPress hashes, and old URLs need a 301 map in the web server, because Shopware core has no redirect manager.

Checked against: Shopware 6.7.15.1, Migration Assistant 19.0.0 and WordPress 6.8.0 source, October 2026.

Moving from Shopware 5 has an official, free tool. Moving from WooCommerce does not. Migration Assistant 19.0.0 ships profiles for Shopware 5.4 to 5.7 and Shopware 6, and Magento lives in a separate Shopware extension. WooCommerce and Shopify have neither.

So you decide per data type how it moves, and the three things that hurt when they go wrong are customer passwords, order history and URLs.

Step 1: Decide How Each Data Type Moves

Catalogue

Categories, products, variations, attributes, manufacturers and images. A Store extension or a script over both REST APIs.

Customers

Accounts and addresses through the API, password hashes from the database. A server-side import, see Step 3.

Orders

History for customer accounts and accounting. Always a script, and the part to scope first.

Content and URLs

Pages and posts are rebuilt in Shopping Experiences. Every old URL gets a 301 target.

For the catalogue, one Store extension, "WordPress/WooCommerce To Shopware 6", imports categories, products, variants, properties, manufacturers and images. Its listing does not mention customers, orders, passwords or SEO URLs, so plan those separately whichever way the catalogue moves.

For a script, the WooCommerce REST API (/wp-json/wc/v3/) exports products, variations, customers and orders, and Shopware's Admin API sync endpoint (/api/_action/sync) writes them in batches.

Step 2: Map Products and Variations

  • Variable products: a WooCommerce variable product becomes a Shopware parent product, and each variation becomes a variant.
  • Attributes: the attributes a variation is built from become property groups and options in the variant configurator. Attributes used only for display become plain properties.
  • Custom fields: data that plugins kept in post meta becomes Shopware custom fields. List which keys matter first, because a WordPress store collects meta from every plugin it ever had.
  • SKUs: carry them over unchanged. ERP, feeds and the redirect map all match on them.

Step 3: Keep Customer Passwords Working

Shopware 6 can check a password hash from another system. Each customer has a legacyEncoder and a legacyPassword field. On login, Shopware asks the encoder with that name whether the password matches the old hash, and if it does, replaces it with a native hash. After one login the customer is on Shopware's own hashing.

Core ships two encoders, Md5 and Sha256, for Shopware 5. WordPress needs its own, and since WordPress 6.8 it has to handle two formats: the older phpass hashes starting with $P$, and the bcrypt-based ones starting with $wp$2y$. WordPress only rehashes on login, so one export usually holds both.

This encoder mirrors wp_check_password() from WordPress 6.8.0 branch for branch. For the phpass branch it uses the PasswordHash class WordPress ships in wp-includes/class-phpass.php, copied into your plugin and loaded through its Composer classmap. The one branch it leaves out is WordPress's own $wp_hasher override, which a migration does not need.

<?php declare(strict_types=1);

namespace Acme\Migration\Password;

use Shopware\Core\Checkout\Customer\Password\LegacyEncoder\LegacyEncoderInterface;

class WordPressEncoder implements LegacyEncoderInterface
{
    public function getName(): string
    {
        return 'WordPress';
    }

    public function isPasswordValid(#[\SensitiveParameter] string $password, string $hash): bool
    {
        if (\strlen($hash) <= 32) {
            return hash_equals($hash, md5($password));
        }

        if (\strlen($password) > 4096) {
            return false;
        }

        if (str_starts_with($hash, '$wp')) {
            $prehash = base64_encode(hash_hmac('sha384', $password, 'wp-sha384', true));

            return password_verify($prehash, substr($hash, 3));
        }

        if (str_starts_with($hash, '$P$')) {
            return (new \PasswordHash(8, true))->CheckPassword($password, $hash);
        }

        return password_verify($password, $hash);
    }
}

Register it as a service with the tag shopware.legacy_encoder. On import, set legacyEncoder to WordPress and legacyPassword to the user_pass value from the wp_users table. The REST API never returns hashes, so they come from the database.

Important: both legacy fields drop the ApiAware flag in 6.7.15.1, so the Admin API does not expose them. Write customers from a console command inside the same plugin, through the customer repository. An import that runs only over HTTP cannot carry the hashes.

Step 4: Build the Redirect Map

On an English install, WooCommerce product URLs default to /product/slug/, categories to /product-category/slug/ and tags to /product-tag/slug/. Other languages translate these bases. Shop owners can change these bases, for example to /shop/slug/ or /shop/hoodies/slug/, and stores on plain permalinks use /?product=ID. Take the real structure from a crawl of the live shop.

Shopware core has no manager for arbitrary redirects. Its one redirect setting sends a 301 from Shopware's own superseded SEO URLs to the current one, and it knows nothing about WooCommerce paths. So the map goes into the web server or CDN as 301 rules, or into a redirect extension.

The quickest map pairs each old product URL with its new one through the SKU. Crawl the old shop, export the new SEO URLs per SKU from Shopware, and join the two lists. Then test the map against staging with the loop from the Shopware 5 to 6 guide, which works for any source platform.

Step 5: Plan the Order History

Order import is the part with no ready-made tool, and the part merchants underestimate. Decide what the history is for before scoping it:

  • Customer accounts: customers want to see past orders. That needs orders linked to the migrated customers, with line items and totals as they were.
  • Accounting and returns: this usually lives in the ERP already. If it does, the shop may only need recent orders.
  • Reorders and analytics: often an export file is enough, with no import at all.

Imported orders keep their old prices and taxes. Write them as finished orders, with their own number range, so they never collide with new ones.

Key Takeaways

  • There is no official WooCommerce migration profile. Choose a tool or script per data type.
  • A legacy encoder keeps WordPress passwords working. It must handle both $P$ and $wp$ hashes.
  • The hashes need a server-side import, because the Admin API does not expose the legacy fields.
  • Old URLs need a 301 map in the web server or CDN. Join old and new URLs on the SKU.

Written by

Share:

Planning the Move from WooCommerce?

Send us your store URL and a rough count of products, customers and orders. We'll tell you what moves with a tool and what needs a script.