Configure run codes and run types in Nginx

in Nginx configuration Tags: Nginx

One Magento installation can serve multiple websites or store views that use different languages, domain names and categories.
This enables one Magento installalation and Admin to manage different stores on multiple domains.

For example, you can create one set of categories and products for one domain and another set for a domain that has different categories and products for a shop focussed on another language or country.

There are several methods of configuring your Magento shop to use domains or subdirectories for storefronts.
You can choose either serving only storefronts in subdirectories, serving storefronts from domains or a mix of both.

If you choose to use only subdirectories, it’s very difficult to configure storefronts for domains as well, so before you start make sure you know how it should be configured for your shop.

Prepare your Magento shop

Before configuring Nginx, make sure all storefront configuration is present. Make sure you have the store code for each storefront you want to serve.
The main Magento documentation on configuring multiple storefronts can be found here for Magento 1 and here for Magento 2.

Nginx mappings

An Nginx mapping is a filter that sets a specific variable depending on another variable. In case of the http.magerunmaps we use the $host variable, to determine which value should be used to configure the $storecode and $storetype variable.

If a mapping starts with the keyword hostnames, Nginx uses a hostname mapping, which enabled wildcards for subdomains by using a dot (.) as a wildcard.

Nginx will iterate over the configured values on each request. If the used domain of the request matches a domain in the domain mapping, the value for that domain will be set as $storecode or $storetype.

By setting a default value using the default tag, Nginx will never set an empty value for the variable, but instead use the default when no match is found for the given domain.

Mappings are extremely useful to avoid long dreaded if, else statements in your Nginx configuration.
More information about mappings can be found in the Nginx documentation

Configure Magento to use a different domain for each storefront

The most populair method of configuring a different domain for each storefront. If you use this way, each storefront is served from another domain or subdomain.

This can be done using the generic http.magerunmaps file in the /data/web/nginx directory. This configuration makes sure that for each domain, the correct MAGE_RUN_CODE and MAGE_RUN_TYPE is used into the PHP environment, ensuring Magento knows which store code to use for which domain.

If you want want to use a mix of both domains and subdirectories to serve your storefronts, use this method to configure all your domains so we can add the storefronts that are served from a subdirectory manually later.

Configuring your http.magerunmaps

To serve a specific storefront for each domain, use the following snippet:

map $host $storecode {
  hostnames;
  default nl_store;
    .shoe.nl nl_store;
    .belgianshoes.com nl_store;
    shoes.otherdomain.com be_store;
    .belgischeschoenen.com be_store;
}

# run something.example.come in website mode (usually not required)
map $host $storetype {
  hostnames;
  default store;
  something.example.com website;
}

The values for $storecode and $storetype will be used by Nginx to set the environment variables MAGE_RUN_CODE and MAGE_RUN_TYPE within the php environment, which are used by Magento to determine which storefront and store type should be used.

Configure Magento to use a subdirectory for each storefront

If you want to use only subdirectories, these are the instructions you a looking for. This method does not account for any domain configuration, so if you want to use both multiple domain names and subdirectories, use the method mentioned below.

Creating symlinks for each subdirectory

Most of the snippet account for an index.php in the subdirectory, which can be accomplished without copying files but instead by creating a symlink from the subdirectory to the main public directory. This ensures for each storefront the index.php and all other files (media etc) are in place without the need to copy files to each subdirectory.

To create the symlinks, use the ln utility and link the subdirectory to the main public/ directory:

  • Create a symlink for multiple subdirectories:
    for STOREFRONT in de de fr nl be; do
      ln -s /data/web/public /data/web/public/$STOREFRONT
    done
    
  • Create a symlink for a single subdirectory:
    ln -s /data/web/public /data/web/public/subdirectory
    

Configuring your http.magerunmaps

If you want to serve all storefronts from subdirectories, you can route each storefront to use its own particular subdirectory. This can be done using a mapping that routes each sub directory to its destined storefront using a regex on the requested url.

To do this, make sure the store code is the same as the subdirectory (IE: storefront ‘nl’ for subdirectory ‘nl’, storefront ‘be’ for subdirectory ‘be’).

Now after your storefront configuration in the Magento admin backend is done, use the following mapping to link your storefront to specific subdirectories:

map $request_uri $storecode {
    default default;
    ~^/nl storecode_nl;
    ~^/de storefront_de;
    ~^fr  storecode_fr;
    ~^be  storecode_be;
}

This snippet sets a store code depending on the $request_uri, serving a different storefront for each subdirectory used.
If you want to use this construction, a symlink should be created for each endpoint (/de/, /nl/, /fr/, /be/ etc)

This snippet should be saved in the http.magerunmaps, where you can comment out all the other configuration as we’re not using the preconfigured snippets.

NB: Never create a mapping for /! As this will match all requests, only a single store code will be served for all endpoints.

Configuring a location block to instruct Nginx to serve the correct subdirectory

After configuring the http.magerunmaps, some additional configuration is required to instruct Nginx to serve the correct files when some browser is requesting the subdirectory.

When you created the store configuration in the backend, you should create a snippet in /data/web/nginx as server.storefronts.
You can do this using the following configuration:

location ~ ^/(?<uri_prefix>(shop_de|shop_fr|shop_nl|shop_be)) {
    rewrite / /$uri_prefix/index.php break;

    location ~ \.php$ {
        echo_exec @phpfpm;
    }   
}

In the example you’ll have to adjust the shop_de|shop_fr|shop_nl|shop_be to the subfolders you are using in your web root.

Combine domains and subdirectories in one Magento installation

If you want to combine both storefronts as subdirectories and separate domains for storefronts, configure the domains first by creating your http.magerunmaps as mentioned earlier.

If you want to serve a storefront from a subdirectory, while maintaining the http.magerunmaps structure of serving a storefront per domain, you can manually configure the storecode for a specific location. In this example we use a subdirectory for ‘be’ and ‘nl’:

location ~ ^/(?<uri_prefix>(nl|be)) {
    if ($uri_prefix = be) {
        set $storecode "storeview_be";
    }
    if ($uri_prefix = nl) {
        set $storecode "storeview_nl";
    }
    rewrite / /$uri_prefix/index.php break;

    location ~ \.php$ {
        echo_exec @phpfpm;
    }   
}

This sets a manual overridden $storecode variable for a specific location, leaving the domain mapping in the http.magerunmaps intact.

Setting the store code in your index.php

The last method we’ll describe is the last resort which always works, but is not as transparent as the earlier mentioned ways of configuring.

You can create any configuration possible by setting the store code in your index.php.
For example if you want to serve a specific storefront all domains that match a certain regex, you can manually configure the store code in your script.

Magento sets the store code at the bottom of the default index.php.
By adjusting the logic used, you can create any construction you can think of.

By default, this is how the store code and store type are set in the index.php:

/* Store or website code */
$mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';

/* Run store or run website */
$mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store';

Mage::run($mageRunCode, $mageRunType);

Now if we want to serve a specific storefront for all domains that contain a certain keyword:

if (preg_match('doghouse', $_SERVER['REQUEST_URI']) {
    $mageRunCode = 'storefront_be';
} else {
    $mageRunCode = 'storefront_nl';
}
Mage::run($mageRunCode, $mageRunType);

This is a very simple, maybe even a bit silly example, but what i’m trying to illustrate: With some PHP skills, the sky is the limit 🙂

Matching *.php

It is not possible in Nginx to match a script matching *.php as this location is already defined.
Setting locations and handlers are on a request base. This means that it is not possible to define a handler for a php file.
All php files are send to php-fpm using a special handler, and overriding this handler would result in a php file not being executed but being offered as a download.
This is not what your want!

Alternatively you can redirect all *.php urls to a clean version of the url and from there use the subdomain approach.

Testing storefronts

Serving specific storefronts for testing

If you want to test your storefronts before switching the DNS, you can easily switch storefronts by editing your http.magerunmaps.
To do this, add your appname to the mapping and configure a storefront for it:

map $host $storecode {
  hostnames;
  default default;
  appname.hypernode.io be_site;
}

Now if you visit your Hypernode on appname.hypernode.io, the configured storefront (in this case the be_site) is shown.

Testing which storefront is selected for a domain

To test which storefront code is used for a particular domain, use the following snippet in your Nginx config:

location /testing {
    add_header Content-Type text/plain;
    return 200 "#### $storecode ####";
}

Now if you request https://your.domain.com/testing to get the store code shown in your browser.

Tips and tricks

  • Do not use wildcards as these will not work. Wildcards do not match on domain maps. instead use .shoe.nl as a wildcard for both www.shoe.nl and shoe.nl.
  • Because the first line of the map is hostnames, a string like .shoe.nl will match both www.shoe.nl and shoe.nl.
  • Don’t add port numbers for the staging environment, your domains will match the same domain name as the production site.
  • If no domain matches the one requested, the default store code is used if the default configuration option is given. Otherwise a 404 will be returned.
  • Additionally as a helper tool, you can use n98-magerun hypernode:maps-generate to generate an example mapping based on your current storefront configuration.

1