Guides Guide

How to Set Up PrestaShop Multistore: Complete Guide

Set up PrestaShop Multistore safely: shop groups, URLs, shared catalogs, module settings, SEO checks, performance risks and common fixes.

What multistore is — and what running it feels like

We run multistore on mypresta.rocks itself: six languages, six shops, one back office. Our client wetroomsdesign.co.uk runs five (UK, IT, DE, ES, NL) from one install. So when we describe multistore on this page, we're not quoting documentation — we're describing the thing we log into every morning.

Multistore lets one PrestaShop install host several storefronts that share the codebase, database and module set, but show different products, themes, prices and URLs. It's been in the core since 1.5 and is genuinely usable in 8.x and 9.x. It's also the single feature most likely to expose a sloppy module — the moment something forgets to filter by id_shop, one shop's settings start leaking into another.

When multistore is the right call

Multiple brands sharing a warehouse. Two storefronts, distinct themes, shared products and stock, one team in one admin.

Country variants of the same catalogue. The use case we see most. wetroomsdesign sells the same shower trays across five countries — same products, different prices, different VAT, different shipping, different category structure. Multistore lets each shop diverge where it needs to while sharing the spine.

B2B alongside B2C. Retail sees tax-included prices and standard shipping. Wholesale sees net prices, minimum quantities and negotiated carriers. One catalogue, two experiences.

Outlet or clearance shop. A second storefront with its own theme drawing discounted items from the same product database.

When it's the wrong call

  • Unrelated businesses. Pet supplies and industrial machinery share nothing. Two separate installs are simpler everywhere.
  • Different PrestaShop versions. All shops in one install run the same core. You cannot keep one on 1.7 while another moves to 9.
  • Incompatible modules. If two shops genuinely need conflicting modules, separate installs avoid a permanent fight.
  • Risk isolation. A bad migration or runaway query hits every shop at once. Independent installs contain the damage.
  • You only need multiple languages. A single PrestaShop shop handles multilingual content natively. Don't reach for multistore just for that.
Before you turn multistore on, write down what's actually shared (products? customers? stock? config?) and what must diverge per shop. If the "shared" list is short, two separate installs will save you years of pain.

Planning before clicking anything

Shop groups decide what's shared, and you can't easily change them

The hierarchy is Shop Group → Shop. The group controls two settings that are painful to reverse:

  • Share customers. Can someone who registered on the UK shop log into the Italian shop with the same credentials? Cross-border B2C usually wants this off; multi-brand within one company usually wants it on.
  • Share available quantities. Does a sale on one shop drop the displayed stock on another? Same warehouse yes, separate inventory no.

Get these right the first time. Flipping them after orders exist leaves you with customer accounts that exist in one shop's table but not the other, and stock records that diverge from reality.

Shared vs separate catalogues

A product can be associated with all shops or only specific ones. Even when shared, the price, name, short and full description, images and SEO metadata can all be per-shop. Categories work the same way. This is what makes multistore worth using — one product record, six storefronts, each with its own copy on the things that move SEO and conversion.

URL strategy

  • Separate domains (brand-a.com, brand-b.com) — what we use for genuinely separate brands. Each needs its own SSL cert or a multi-domain cert.
  • Subdomains (de.shop.com, fr.shop.com) — natural for country variants. A wildcard SSL covers them all. This is what mypresta.rocks uses for its six language shops.
  • Subdirectories (shop.com/wholesale/) — easiest setup, one SSL, but limits SEO because search engines treat them as one site.

One database, one backup, one blast radius

Multistore is a single database. Tables gain id_shop columns and PrestaShop filters by context. One backup covers every shop (convenient. One bad query takes every shop down) not.

Turning it on and adding the second shop

Step 1: Enable the feature

Advanced Parameters → General on PS 1.7.x, Advanced Parameters → Multistore on 8.x/9.x. A shop context selector appears in the header — from this point on, it's the most important UI element in the back office.

Enable the feature before planning URLs.

This real Back Office capture shows the switch that exposes the Multistore administration page. Enable it only after you have decided what must be shared and what must stay shop-specific.

PrestaShop Back Office Enable Multistore setting switched to Yes

Step 2: Create a shop group

Under Advanced Parameters → Multistore, Add a new shop group. Name it, pick the customer-sharing and stock-sharing settings. These are sticky.

Step 3: Add the shop

Add a new shop. Pick the name, group, root category, and optionally import the structure from an existing shop. The import saves real time when the new shop starts as a copy — for example, when wetroomsdesign added Spain after Italy, the IT shop was a useful starting point.

The tree is your source of truth.

After enabling multistore, verify the group, shop, and URL hierarchy here before changing products, modules, or themes. If the tree looks wrong, fix it before adding real catalog data.

PrestaShop Multistore tree showing the Default shop group and PrestaShop store

Step 4: Configure URLs

Open the shop and add a URL row:

# Separate domain
Domain: www.my-second-shop.com
Physical URI: /
Virtual URI: (empty)

# Subdomain
Domain: shop2.myshop.com
Physical URI: /
Virtual URI: (empty)

# Subdirectory
Domain: www.myshop.com
Physical URI: /
Virtual URI: wholesale/

Each shop needs a clean main URL.

The URL list is where you confirm the canonical domain, SSL domain, physical URI, virtual URI, main URL flag, and enabled state. Most redirect loops start with a mismatch on this screen.

PrestaShop Multistore shop URL list showing the main URL for one shop

Step 5: Make the server route

For separate domains and subdomains, every domain has to resolve to the same PrestaShop directory. On Apache, .htaccess handles it. On Nginx, point each server_name at the same docroot. For subdirectories, PrestaShop handles routing through .htaccess on its own.

After any URL change, clear the cache. URL routing is cached aggressively and stale entries are the single most common cause of redirect loops after adding a shop.

Living with the shop context selector

The single most important habit

The dropdown at the top of the back office decides which shop your changes apply to: All shops, a group, or a specific shop. The rule we drill into every team taking over a multistore install: work in the narrowest context the change should affect. Editing in "All shops" when you meant a single shop is the most common multistore mistake we clean up.

Configuration inheritance, and the checkbox that catches everyone

Values set in "All shops" become defaults. A specific shop can override any field, and a checkbox next to each field controls whether that shop uses the inherited value or its own. The catch: the checkbox state means different things in different PrestaShop versions. We've seen configuration "saved" successfully but silently fall back to the parent because the override wasn't ticked. Always reload and verify in the shop context, not just immediately after save.

Products and categories

  • Create in "All shops" → available everywhere.
  • Create in a specific shop → limited to that shop, you can associate it with others later.
  • Disabling in one shop doesn't disable in the others.
  • Deleting in "All shops" wipes it from every shop permanently. There's no recycle bin.
  • Stock behaviour depends on the group's "Share available quantities" setting.

Theme, language and currency per shop

Each shop runs its own theme, its own enabled languages and currencies. Switch to the shop context first, then make changes in Design → Theme & Logo and International → Localization. Change theme settings in "All shops" context and you overwrite every shop's theme configuration in one click.

How modules behave and misbehave

What a well-built module does

A module that respects multistore stores config per shop — Configuration::updateValue() (which honours the active context) not Configuration::updateGlobalValue() — and reads Shop::getContextShopID() in hooks before assuming a single shop. The ps_configuration table has both id_shop_group and id_shop columns, and a well-written module fills them.

The configuration table needs both id_shop_group and id_shop scope set, or you'll see other shops' settings bleeding through. A module that "kind of supports multistore" usually means it writes id_shop but leaves id_shop_group as NULL — then values vanish or duplicate depending on the active context.

The four ways modules break

  • Global-only config. Settings written without id_shop, so changing one shop changes all of them.
  • Hardcoded shop ID. Somewhere in the module's queries: id_shop = 1 as a literal. Shop 1 works, every other shop is empty or broken.
  • Missing shop filter in queries. Custom SQL joins ps_product without a shop filter — Shop 2's front office shows products that belong only to Shop 1.
  • Cache key collisions. Module caches a rendered block without the shop ID in the key. Shop A loads, caches, Shop B serves Shop A's block to its visitors.

How to vet a module before installing it

  • Does the description actually say "multistore compatible" — or does it dodge the question?
  • Is there documentation on per-shop configuration?
  • After installing, does the config page show inheritance checkboxes when you switch to a specific shop?
  • The real test: set different values on two shops, save, reload, confirm both front ends reflect the difference.
A module that ignores multistore won't necessarily crash — it just shares one set of settings across every shop. For a global Google Analytics ID, fine. For per-shop shipping, checkout text or carriers, that's a quiet disaster.

SEO across multiple shops

Canonical URLs

Every page on every shop must canonical to itself. A product on shop-a.com canonicals to shop-a.com, never to shop-b.com. If you're running near-identical content across shops, pick one primary source and implement cross-domain canonicals deliberately. Don't let Google guess.

Hreflang for multi-language shops

If your shops target different languages and regions, hreflang tags tell search engines which version to serve which audience:

<link rel="alternate" hreflang="de" href="https://de.myshop.com/produkt" />
<link rel="alternate" hreflang="fr" href="https://fr.myshop.com/produit" />
<link rel="alternate" hreflang="x-default" href="https://www.myshop.com/product" />

The gap PrestaShop doesn't fill: the core handles hreflang between languages within one shop, but not across separate shops. For multi-shop hreflang you'll need a module, theme modifications, or hand-written tags. The tags also have to be bidirectional — if Shop A references Shop B, Shop B has to reference Shop A back, or Google ignores the lot.

Sitemaps and duplicate content

Each shop needs its own sitemap.xml containing only its own URLs. Register every shop separately in Google Search Console. To avoid duplicate-content penalties when shops share products: write per-shop descriptions for at least your top sellers, set canonical tags deliberately, and rewrite category page intros so they aren't identical strings on five domains.

For multi-language shops on separate domains, hreflang isn't optional. Without it, Google routinely indexes the wrong language for each market and organic traffic drops with no obvious cause.

What multistore costs in performance

Three areas take a hit:

  • Configuration tables get bigger. N rows per setting (one per shop), not one. Every config lookup reads more data.
  • Every query gains a shop filter. Almost every SQL gets a WHERE id_shop = X clause. Without the right indexes, this gets slow as the database grows.
  • Cache footprint multiplies. Each shop has its own cache entries for templates, configs and listings. Memory scales with shop count.

On Redis or Memcached, double-check that cache key prefixes include the shop ID. We've inherited installs where they didn't, and Shop B's visitors saw Shop A's blocks for the lifetime of the cache entry.

# Check table sizes
SELECT table_name, ROUND(data_length/1024/1024, 2) AS size_mb
FROM information_schema.tables
WHERE table_schema = 'prestashop'
ORDER BY data_length DESC LIMIT 20;

# Find missing indexes on id_shop columns
SELECT t.table_name, c.column_name
FROM information_schema.columns c
JOIN information_schema.tables t
  ON c.table_schema = t.table_schema AND c.table_name = t.table_name
WHERE c.table_schema = 'prestashop' AND c.column_name = 'id_shop'
  AND c.column_name NOT IN (
    SELECT column_name FROM information_schema.statistics
    WHERE table_schema = 'prestashop' AND table_name = c.table_name
  );

The problems you'll actually hit

Product appears in the wrong shop

Cause: created in "All shops" context, or associated wrongly during a bulk edit. Fix: switch to the unwanted shop's context and remove the association. Verify in the database:

SELECT ps.id_product, ps.id_shop, ps.active
FROM ps_product_shop ps WHERE ps.id_product = 123;

A config change affects every shop

Either the selector was on "All shops" when you saved, or the module stores globally. Check:

SELECT id_shop, name, value FROM ps_configuration
WHERE name = 'MODULE_SETTING_NAME' ORDER BY id_shop;

If you only see one row with id_shop = 0 or NULL, the module stores globally regardless of context. That's a module bug — patch it to call Configuration::updateValue() with the shop ID, or insert per-shop rows manually.

Module won't save per-shop settings

Almost always Configuration::updateGlobalValue() where Configuration::updateValue() should be. Code fix in the module or a vendor update — no back-office workaround.

Cart bleeds across shops

Cookie domain set too broadly. If the cookie is scoped to .myshop.com, a cart on shop-a.myshop.com is visible to shop-b.myshop.com. Fix per shop in Shop Parameters → General: scope the cookie to the specific subdomain.

CSV import lands in the wrong shop

The import tool respects the active context. Switch to the target shop before you open the import page, not after. Then verify ps_product_shop has the rows you expect — fixing wrong associations on thousands of products by hand is a long evening.

Redirect loops after adding a shop

Either URL config is wrong or .htaccess is stale. Regenerate .htaccess from Advanced Parameters → Performance, then verify ps_shop_url matches reality:

SELECT s.name, su.domain, su.domain_ssl, su.physical_uri, su.virtual_uri
FROM ps_shop s
JOIN ps_shop_url su ON s.id_shop = su.id_shop AND su.main = 1;

SSL also has to be consistent — domain_ssl pointing at a non-SSL host while the customer hits https:// is a classic source of loops.

Maintenance routine

Weekly

  • Load every shop's storefront. No redirects, no errors, right theme.
  • Check recent orders are tagged with the correct shop ID.
  • Run a test checkout on each shop.

Monthly

  • Audit product associations — anything in a shop it shouldn't be in?
  • Cycle through each shop context and spot-check module configuration pages.
  • Read error logs per shop, not just globally.
  • Confirm sitemaps are current and serving 200s.
  • Send a test email from each shop and verify the sender domain.

After a module update

  • Open the module config page in every shop context and check it still saves independently.
  • Test front-end behaviour per shop, not just on Shop 1.
  • Clear caches for every shop.

After a PrestaShop core update

  • Test every shop immediately. Multistore regressions are common — the core team tests mostly on single-shop installs.
  • Recheck ps_shop_url.
  • Regenerate .htaccess.
  • Run a full checkout on every shop, every payment method.

Related reading

Loading...
Back to top