Magento 2 NGINX Multi Website: Steps to Configure Multiple Websites

Magento 2 NGINX Multi Website: Steps to Configure Multiple Websites

[Updated: February 25, 2026]

Running three Magento stores on three servers triples your maintenance. One NGINX installation handles all of them.

This guide covers the 4-step setup, production env.php config, and Varnish cache pitfalls other guides miss.

Key Takeaways

  • Configure multiple Magento 2 websites from a single NGINX installation with shared backend
  • Use virtual host map directives or separate server blocks per domain
  • Lock base URLs in env.php for production (no manual Admin changes needed)
  • Avoid the Varnish X-Magento-Vary cache conflict that serves wrong store content
  • Automate SSL certificates for multiple domains with Let's Encrypt
  • Troubleshoot redirect loops, MAGE_RUN_CODE mismatches, and Redis session conflicts

TL;DR

Magento 2 NGINX Multi Website = one Magento installation serving multiple websites, each with its own domain, design, and product catalog. NGINX handles request routing through virtual hosts and MAGE_RUN_CODE variables.

Perfect for: Multi-brand stores, regional storefronts, B2B and B2C on one backend, agencies managing client stores

Not ideal for: Single-store setups, stores needing separate database isolation, Adobe Commerce Cloud (uses routes.yaml instead)

What is Magento 2 NGINX Multi Website?

Magento 2 NGINX multi website manages multiple websites from one Magento 2 installation. Each website gets its own store view, base URL, and domain. All websites share one Magento admin panel, one codebase, and one database.

NGINX routes incoming requests to the correct store using two variables:

  • MAGE_RUN_CODE: The unique code for each website or store view
  • MAGE_RUN_TYPE: Set to website or store depending on your routing strategy

This setup works for businesses running regional stores (US, EU, APAC), multi-brand portfolios, or separate B2B and B2C storefronts. One admin panel manages all of them.

For a head-to-head comparison, see NGINX vs Apache for Magento.

Why Use NGINX for Magento Multi-Site?

High Performance and Low Memory

NGINX uses an event-driven architecture. It handles thousands of connections in a single process. Apache spawns a new process per request. That difference matters when traffic hits multiple storefronts at once.

Reverse Proxy and Load Balancing

NGINX reverse proxy for Magento multi-website

NGINX distributes traffic across backend servers. It routes each request to the correct PHP-FPM pool based on the domain. One reverse proxy serves all your storefronts.

Built-in Caching

NGINX caches static assets (CSS, JS, images) at the server level. Combined with Varnish cache configuration, cached pages load in under 200ms.

SSL/TLS Termination

NGINX handles SSL decryption before passing requests to PHP-FPM. Each domain gets its own certificate. The application server never touches cryptographic operations.

4 Steps to Configure NGINX Multi Website

Step 1: Create Websites, Stores, and Store Views in Admin

  1. Go to Stores > Settings > All Stores
  2. Click Create Website. Enter a name and a unique website code (this becomes your MAGE_RUN_CODE). Save.
  3. Click Create Store. Select the website you created and enter store details. Save.
  4. Click Create Store View. Select the store and enter the view details. Save.

Repeat for each website. Keep the website codes short and lowercase (e.g., us, eu, b2b).

Warning: You lose Admin access until virtual hosts are configured for the new domains. Plan your DNS and NGINX config before creating websites in the admin.

Step 2: Create NGINX Virtual Hosts

You have two approaches: one virtual host file with a map directive, or separate files per domain.

Option A: Single file with map directive (recommended)

map $http_host $MAGE_RUN_CODE {
    default   base;
    us.example.com   us;
    eu.example.com   eu;
    b2b.example.com  b2b;
}

server {
    listen 80;
    listen 443 ssl http2;
    server_name us.example.com eu.example.com b2b.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    set $MAGE_ROOT /var/www/html/magento2;
    set $MAGE_MODE production;
    set $MAGE_RUN_TYPE website;

    include /var/www/html/magento2/nginx.conf;
}

Set default base; to your primary website code. An empty default can cause Magento to load the wrong store or throw errors when no hostname matches.

Option B: Separate files per domain

Create /etc/nginx/sites-available/eu.example.com:

server {
    listen 80;
    listen 443 ssl http2;
    server_name eu.example.com;

    ssl_certificate     /etc/letsencrypt/live/eu.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/eu.example.com/privkey.pem;

    set $MAGE_ROOT /var/www/html/magento2;
    set $MAGE_MODE production;
    set $MAGE_RUN_TYPE website;
    set $MAGE_RUN_CODE eu;

    include /var/www/html/magento2/nginx.conf;
}

Enable with symlinks:

cd /etc/nginx/sites-enabled
ln -s /etc/nginx/sites-available/eu.example.com eu.example.com

Validate before reloading:

nginx -t
sudo systemctl reload nginx

Step 3: Pass MAGE Variables to PHP-FPM

Open your Magento nginx configuration file (copy of nginx.conf.sample). Find the PHP entry point block and add the MAGE parameters:

location ~ (index|get|static|report|404|503|health_check)\.php$ {
    try_files $uri =404;
    fastcgi_pass fastcgi_backend;
    fastcgi_buffers 16 16k;
    fastcgi_buffer_size 32k;
    fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off";
    fastcgi_param PHP_VALUE "memory_limit=1G \n max_execution_time=18000";
    fastcgi_read_timeout 600s;
    fastcgi_connect_timeout 600s;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    # Multisite configuration
    fastcgi_param MAGE_RUN_TYPE $MAGE_RUN_TYPE;
    fastcgi_param MAGE_RUN_CODE $MAGE_RUN_CODE;

    include fastcgi_params;
}

Do not edit the original nginx.conf.sample. Copy it first, then modify the copy.

Step 4: Set Base URLs and Clean Cache

  1. Log into Magento Admin. Go to Stores > Configuration > General > Web.
  2. Switch the configuration scope to each website.
  3. Set Base URL and Secure Base URL for each domain:
Website Base URL Secure Base URL
US Store https://us.example.com/ https://us.example.com/
EU Store https://eu.example.com/ https://eu.example.com/
B2B Store https://b2b.example.com/ https://b2b.example.com/

Set both Base URL and Secure Base URL to https://. There is no reason to use http:// as a base URL in 2026.

Clean the cache:

bin/magento cache:clean config full_page

Verify each domain loads the correct store view on the storefront.

Production Config with env.php

The Admin panel approach from Step 4 works for development. For production, lock base URLs in app/etc/env.php instead. This eliminates manual Admin changes and works with CI/CD pipelines.

For website-scope routing (MAGE_RUN_TYPE = website):

'system' => [
    'websites' => [
        'us' => [
            'web' => [
                'unsecure' => ['base_url' => 'https://us.example.com/'],
                'secure'   => ['base_url' => 'https://us.example.com/'],
            ]
        ],
        'eu' => [
            'web' => [
                'unsecure' => ['base_url' => 'https://eu.example.com/'],
                'secure'   => ['base_url' => 'https://eu.example.com/'],
            ]
        ]
    ]
]

For store-scope routing (MAGE_RUN_TYPE = store):

'system' => [
    'stores' => [
        'us_en' => [
            'web' => [
                'unsecure' => ['base_url' => 'https://us.example.com/'],
                'secure'   => ['base_url' => 'https://us.example.com/'],
            ]
        ],
        'eu_de' => [
            'web' => [
                'unsecure' => ['base_url' => 'https://eu.example.com/de/'],
                'secure'   => ['base_url' => 'https://eu.example.com/de/'],
            ]
        ]
    ]
]

Use 'websites' when routing by website code, 'stores' when routing by store view code. Match the key to your MAGE_RUN_TYPE.

Export existing configuration with:

bin/magento app:config:dump system

This writes all store-scoped settings to env.php. Commit the file to version control and deploy across environments without touching the Admin panel.

See our Magento hosting requirements for server specs that support multi-site setups.

SSL Certificates for Multiple Domains

Each domain in your multi-site setup needs an SSL certificate. Let's Encrypt with Certbot handles this for free.

Webroot method (recommended for multi-domain setups):

certbot certonly --webroot -w /var/www/html/certbot \
    -d us.example.com -d eu.example.com -d b2b.example.com

The --webroot method does not touch your NGINX config. The --nginx plugin often rewrites only one server block in multi-domain setups, leaving other domains without certificates.

Wildcard certificate for subdomain stores:

certbot certonly --dns-cloudflare -d "*.example.com" -d "example.com"

Fix the ACME challenge conflict: Magento's catch-all location block returns 404 for .well-known/acme-challenge/ paths. Add this block BEFORE the Magento include in your virtual host:

location ^~ /.well-known/acme-challenge/ {
    allow all;
    root /var/www/html/certbot;
}

SSL setup for Magento NGINX websites

Set up auto-renewal:

# Test renewal
certbot renew --dry-run

# Add to crontab
0 3 * * * certbot renew --quiet --deploy-hook "systemctl reload nginx"

Then add the certificate paths to each server block as shown in Step 2.

Varnish Cache with Multi-Store: The X-Magento-Vary Trap

Running Varnish cache with multiple stores introduces a known cache conflict. Varnish keys cached responses using the X-Magento-Vary cookie. If a visitor hits your EU store without this cookie set, Varnish may serve cached content from your US store.

The typical request chain with Varnish: Client → NGINX (SSL termination, port 443) → Varnish (cache, port 80) → NGINX (backend, port 8080) → PHP-FPM. NGINX appears twice in this chain. The SSL and virtual host config from Step 2 applies to the front-facing NGINX. The backend NGINX on port 8080 passes the MAGE variables to PHP-FPM.

The fix: Add store-aware cache key logic to your VCL:

sub vcl_hash {
    hash_data(req.http.host);
    if (req.http.cookie ~ "X-Magento-Vary=([^;]+)") {
        hash_data(regsub(req.http.cookie,
            ".*X-Magento-Vary=([^;]+).*", "\1"));
    } else if (req.http.X-Magento-Vary) {
        hash_data(req.http.X-Magento-Vary);
    }
}

This parses the X-Magento-Vary value from the cookie header first. The fallback to the request header catches edge cases where Varnish strips cookies before the hash function runs. Without this fix, customers see the wrong store's content, prices, and language.

Assign separate Redis databases per store to prevent session bleed. Magento uses Redis DB 0 for cache and DB 1 for full page cache by default. Start session databases at 2 or higher:

// app/etc/env.php — unique DB per store, starting at 2+
'session' => [
    'save' => 'redis',
    'redis' => [
        'database' => '2',  // US = 2, EU = 3, B2B = 4
    ]
]

Performance Optimization

Magento NGINX configuration setup

Enable Full Page Cache

Set Magento to production mode and enable Varnish as the full page cache backend:

bin/magento deploy:mode:set production
bin/magento config:set system/full_page_cache/caching_application 2

GZIP Compression

Enable GZIP in your NGINX server block:

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;

HTTP/2

Add HTTP/2 support in your SSL listener:

listen 443 ssl http2;

This enables request multiplexing and header compression across all storefronts.

Separate PHP-FPM Pools

For high-traffic multi-site setups, run separate PHP-FPM pools per store. This prevents one store's traffic spike from starving the others. Magento 2.4.7+ supports PHP 8.3 and 8.4 (recommended as of 2026):

; /etc/php/8.3/fpm/pool.d/us-store.conf
[us-store]
listen = /run/php/us-store.sock
pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.max_requests = 500

The pm.max_requests setting recycles workers after 500 requests. This prevents memory leaks from accumulating across long-running PHP processes.

Point each NGINX server block to its pool's socket:

upstream fastcgi_us {
    server unix:/run/php/us-store.sock;
}

For managed Magento hosting, these optimizations come preconfigured.

SEO Considerations for Multi Website Setup

Aspect Configuration
Canonical Tags Set canonical URL per store view to prevent duplicate content across domains
Unique Content Each store view needs unique meta titles, descriptions, and content
Hreflang Tags Add hreflang annotations for regional stores (us, eu) to signal language and region
XML Sitemaps Generate separate sitemaps per store view and submit each to Google Search Console
Base URL Consistency Use either www or non-www. Redirect the other with a 301
SSL on All Domains Google ranks HTTPS pages higher. Secure every domain in your setup

For a deeper multistore setup guide, see our Magento 2 multistore tutorial.

Troubleshooting Common Issues

Redirect Loops After Adding a New Website

Cause 1: Auto-redirect to Base URL. Go to Stores > Configuration > General > Web > URL Options and set Auto-redirect to Base URL to No for the new website scope. The default "Yes (302)" creates a loop when NGINX and Magento disagree on the canonical domain.

Cause 2: Mixed HTTP/HTTPS base URLs. If web/secure/use_in_frontend is set to 1 but your base URL uses http://, Magento redirects every request to HTTPS, which redirects back to HTTP. Set both base URLs to https://.

Cause 3: Reverse proxy forwarding. Cloudflare, AWS ALB, or other proxies terminate SSL and forward requests to NGINX over plain HTTP. Magento sees HTTP, redirects to HTTPS, and the proxy sends it back as HTTP again. Add this to your NGINX fastcgi block:

fastcgi_param HTTPS on;

This tells PHP the connection is secure, stopping the redirect loop.

Wrong Store Loads on a Domain

MAGE_RUN_CODE in your NGINX config does not match the website code in Admin. These values are case-sensitive:

# NGINX config shows:
set $MAGE_RUN_CODE EU;

# But Admin panel has: eu (lowercase)
# Fix: Make them match

403 Forbidden Errors

File permissions in the Magento root directory are wrong. Set:

find var generated vendor pub/static pub/media app/etc -type f -exec chmod 644 {} \;
find var generated vendor pub/static pub/media app/etc -type d -exec chmod 755 {} \;

PHP-FPM Socket Permission Denied

When using separate PHP-FPM pools per store, the NGINX worker process needs access to each socket:

; In pool config
listen.owner = www-data
listen.group = www-data
listen.mode = 0660

Redis Session Conflicts Between Stores

Multiple stores sharing one Redis instance without key prefix separation causes session bleed. Assign different Redis databases per store in env.php:

'session' => [
    'save' => 'redis',
    'redis' => [
        'database' => '2',  // US = 2, EU = 3, B2B = 4
    ]
]

FAQ

1. How many websites can one Magento 2 NGINX installation handle?

There is no hard limit. The constraint is server resources (RAM, CPU, PHP-FPM workers). A well-configured server handles 5 to 10 websites without issues. Beyond that, consider separate PHP-FPM pools and load balancing.

2. Should I use website or store for MAGE_RUN_TYPE?

Use website when each domain represents a distinct website with its own product catalog and pricing. Use store when domains share the same website but need different store views (e.g., language variants).

3. Can I mix subdomains and subdirectories in one setup?

Yes. Use NGINX map directives for subdomain routing and location blocks for subdirectory routing. Both can coexist in a single configuration file.

4. How do I add a new website without downtime?

Prepare the NGINX virtual host file, DNS record, and SSL certificate first. Create the website in Magento Admin. Enable the NGINX config and reload. The reload is graceful and causes zero downtime.

5. What happens to the Magento Admin when running multiple websites?

The Admin panel is shared across all websites. You switch between website scopes using the scope selector in Stores > Configuration. All websites, stores, and store views are managed from one interface.

6. Does multi-site setup affect Magento performance?

Minimal impact. Each request processes through one PHP-FPM worker. NGINX resolves the MAGE_RUN_CODE variable in microseconds. Database queries increase if stores share product catalogs with different prices.

7. How do I handle different payment gateways per website?

Configure payment methods at the website scope. Go to Stores > Configuration, switch scope to the target website, and enable the payment provider for that scope.

8. Can I run Magento multi-site in Docker?

Yes. Configure multiple NGINX service entries in docker-compose.yml pointing to the same PHP-FPM container. Pass MAGE_RUN_CODE as environment variables per service. The markshust/docker-magento project provides reference configs for this setup.

9. Is this setup compatible with Adobe Commerce Cloud?

No. Adobe Commerce Cloud does not allow direct NGINX configuration. Cloud uses routes.yaml for domain routing and magento-vars.php instead of fastcgi_param for passing store codes. This guide applies to self-hosted and managed Magento hosting environments.

10. How do I verify which store NGINX is serving?

Check Magento's var/log/system.log for store initialization messages. In development, add fastcgi_param MAGE_IS_DEVELOPER_MODE 1; to your NGINX config and inspect X-Magento-Tags response headers for the store code.

CTA

Summary

Magento 2 NGINX multi website configuration runs multiple storefronts from one installation. The 4-step setup (Admin, virtual hosts, MAGE variables, base URLs) gets you running. For production, use env.php for base URL management and separate Redis databases per store.

The details other guides skip: Varnish cache conflicts from missing host-based hash keys, Let's Encrypt ACME challenge conflicts with Magento's routing, and redirect loops from reverse proxies forwarding wrong protocol headers.

Running multi-site without managing NGINX, Varnish, and PHP-FPM yourself? That's what managed Magento hosting handles.

CEO & Co-Founder

Raphael Thiel co-founded MGT-Commerce in 2011 together with Stefan Wieczorek and has built it into a leading Magento hosting provider serving 5,000+ customers on AWS. With 25+ years in e-commerce and cloud infrastructure, he oversees hosting architecture for enterprise clients. He also co-founded CloudPanel, an open-source server management platform.


Get the fastest Magento Hosting! Get Started