Lurox 1.0 is live β€” visual page building for Magento 2. Read the guide
Magento Multi-Store Setup Guide

Magento Multi-Store Setup Guide

One codebase, multiple brands β€” set up correctly, including the parts every other tutorial gets wrong.

What "multi-store" actually means

Magento can run several independent storefronts β€” different brands, domains, even catalogues β€” from a single codebase and database. The piece that trips people up is the hierarchy. Get these three levels straight and the rest is mechanical:

LevelRepresentsHas its own…
WebsiteA brand / domain boundaryDomain, customer base, pricing, payment scope
Store (group)A catalogue boundary within a websiteRoot category (product tree)
Store ViewA presentation of a storeLanguage, theme, base URLs
Separate brands vs. multiple languages Two brands on two domains = two websites. The same shop in English and French = one website, two store views. Most "multi-store" setups are really multi-website.

Why most tutorials are wrong: scopes live in the database

Older guides tell you to edit app/etc/config.php or env.php to add stores. On current Magento that's incorrect β€” websites, stores and store views are database records, created in the admin (or programmatically). config.php only records which modules are enabled. If a tutorial has you hand-editing config files to add a website, it predates 2.4 and will lead you astray.

2FA is mandatory since Magento 2.4.0 You cannot complete setup:install or log into the admin without two-factor authentication configured. Plan for it up front β€” it is not optional and cannot be skipped on a real install.

Step-by-step admin setup

All four steps live under Stores → Settings → All Stores.

1. Create the website

  • Create Website. Give it a Name and a Code β€” lowercase, no spaces, no leading digit (e.g. brand_a). Remember this code: it goes into your web-server config verbatim.

2. Create the store (group)

  • Create Store. Choose the website, give it a Name, a Code, and a Root Category.
The store-group code is now required β€” and the error doesn't tell you that Current Magento added a mandatory unique code to store groups. Leave it blank and the save fails with a baffling, field-less message:
Magento\Framework\Validator\Exception:
Invalid type given. String, integer or float expected
It names no field β€” but it's the empty store-group code failing validation. Older 2.2/2.3 tutorials never mention this column, which is why this one error wastes so many hours. Always set a code (e.g. brand_a_store).

3. Create the store view

  • Create Store View. Choose the store, give it a Name, a Code, set Status = Enabled. The view is what carries language, theme and URLs.

4. Set per-website base URLs

  • Go to Stores → Configuration, switch the scope selector (top-left) to the target website, then Web → Base URLs (and Base URLs (Secure)).
  • Untick "Use Default Value" and set the secure base URL to that brand's domain, e.g. https://brand-a.example/.
The #1 quiet mistake Forgetting to switch the scope selector away from "Default Config" β€” your new URLs then apply to the wrong scope and every domain shows the same store.

5. Assign a theme per store view

  • Content → Design → Configuration, edit the target store view's row, set its theme. Themes are assigned per store view, so each brand can look completely different.

Wiring your web server (nginx)

The web server has to tell Magento which website a request belongs to, using the MAGE_RUN_CODE variable. Map each hostname to its website code:

map $http_host $MAGE_RUN_CODE {
    default                  "";
    brand-a.example          "brand_a";
    www.brand-a.example      "brand_a";
    brand-b.example          "brand_b";
    www.brand-b.example      "brand_b";
}
map $http_host $MAGE_RUN_TYPE {
    default "website";
}

Then β€” and this is the part guides omit β€” the two variables must be forwarded to PHP in the location block that runs index.php / static.php:

location ~ ^/(index|get|static|errors/report|health_check)\.php$ {
    fastcgi_param MAGE_RUN_CODE $MAGE_RUN_CODE;   # required
    fastcgi_param MAGE_RUN_TYPE $MAGE_RUN_TYPE;   # required
    # ...your usual fastcgi_pass + params...
}
The single most common failure Omitting those two fastcgi_param lines. Magento silently falls back to the default website and every domain shows the same storefront. If all your brands look identical, check this first.

Validate and reload: nginx -t && nginx -s reload. The host on the left of the map must match the website code on the right exactly.

DNS, SSL & testing before cutover

  • Each brand domain needs its own TLS certificate. Issue one per domain (e.g. via Let's Encrypt) before going live.
  • Test a domain before you point its DNS, by resolving it to your server manually:
    curl -sk --resolve brand-a.example:443:YOUR.SERVER.IP \
         -o /dev/null -w "%{http_code}\n" https://brand-a.example/
    A 200 or 302 means the store resolves correctly. A 500 usually means a website-code mismatch β€” check the exception log.

Common failures & fixes

SymptomCause & fix
"The website with code X wasn't found"The nginx MAGE_RUN_CODE doesn't match the website code in admin. Make them identical.
All domains show the same storeThe two fastcgi_param lines are missing from the PHP location block.
Store view doesn't appear after creating itFlush the cache: bin/magento cache:flush.
"Invalid type given. String, integer or float expected"The store-group code is empty. Set a unique code.
New store view is unstyled / 404s on assetsDeploy static content for it: bin/magento setup:static-content:deploy <locales>.
Don't disable security to debug Keep your media/static PHP-execution blocks in place even while troubleshooting β€” multi-store issues are configuration, not security, and weakening hardening only adds risk.

Go-live checklist

  • Website, store group (with code!), and store view created for each brand.
  • Per-website secure base URLs set, with the scope selector switched correctly.
  • Theme assigned per store view.
  • nginx host → MAGE_RUN_CODE map complete (apex and www), and the two fastcgi_param lines present.
  • A TLS certificate issued per domain.
  • Each domain tested with --resolve and returning 200/302 before DNS cutover.
  • bin/magento cache:flush and indexer:reindex run; static content deployed for all locales.

Changelog

This guide is itself a Lurox page. Every revision is a published version in Lurox's built-in content history, so we can roll any guide back to an earlier version at any time. The public changelog below tracks what changed and when.

Version 1.0

30 June 2026

  • Initial publication. Covers the website/store/view hierarchy, the database-first model, the required store-group code, the nginx MAGE_RUN_CODE wiring, and a go-live checklist. Verified against Magento 2.4.8.

Explore the rest of the library

Browse every Cabbage Patch Studios user guide in one place.

All User Guides