Not sure if this is a bug in this project, or a Magento2 bug itself

The question is site is configured to use store code in urls, and that's working fine... but urls without any store code are loading too, causing SEO problems due to content duplicity

Example: "en" is the store code of default storeView

http://ourproject.com/home -> loads fine

http://ourproject.com/en/home -> loads fine

http://ourproject.com/home should redirect 301 to http://ourproject.com/en/home

Any ideas about how to force (in a clean way) any url to use store code of default storeView?


We use Apache as webserver


We finally solved this with a light module, using controller_action_predispatch event

This assumes a site with 2 store views, with following store codes

es <- default


etc / frontend / events.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="controller_action_predispatch">
        <observer name="force_storecode_redirect" instance="Vendor\Module\Observer\ForceStorecodeRedirectObserver" shared="false" />

Observer / ForceStorecodeRedirectObserver.php

namespace Vendor\Module\Observer;
use Magento\Framework\Event\ObserverInterface;

class ForceStorecodeRedirectObserver implements ObserverInterface
    protected $storeManager;
    protected $url;
    /** @var string $defaultStorecode */
    protected $defaultStorecode = 'es';
    /** @var array $storeCodes - array of existing storecodes*/
    protected $storeCodes = [];

    public function __construct(
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\UrlInterface $url
    ) {
       $this->storeManager = $storeManager;
       $this->url = $url;
       $this->storeCodes = array_keys($this->storeManager->getStores(false, true));

    public function execute(\Magento\Framework\Event\Observer $observer)
         $urlParts = parse_url($this->url->getCurrentUrl());
         $path = $urlParts['path'];

         // get storecode from URL
         $urlCode = trim(substr($path, 0, 4), '/');

         // If path does not already contain an existing storecode
         if (!in_array($urlCode, $this->storeCodes)) {
             $path = ltrim($path, '/');
             if ($path == 'en') {
                 $path = '';

             // Redirect to URL including storecode
             header("HTTP/1.1 301 Moved Permanently");
             header("Location: " . $this->storeManager->getStore()->getBaseUrl() . $path);

You could to this through nginx:

## Redirect URLs without storecode to URLs with storecodes
set $storecode undefined;
if ( $http_host ~ ^(www\.)?ourproject.com ) {
    set $storecode en;

if ( $request_uri ~ ^/(skin|js|media|feeds|api|rest|index.php/admin|sitemaps)(|/$|/) ) {
    set $storecode reset;

if ( $request_uri ~ ^/(en)(|/$|/) ) {
    set $storecode reset;

if ( $storecode = en ) {
    rewrite ^(.*) http://$host/$storecode$1 permanent;

Let me know if you have any questions.

  • What would be the equivalent in Apache / htaccess? – Raul Sanchez Apr 24 '18 at 14:11
  • For me this gives an error when accessing the Admin backend: Requested store cannot be found. It tries to receive a frontend storeview by the code admin_custom which obviously does not exist. Even if I add the admin_custom to the exeptions list in the second if-expression. – Jacques Aug 20 '19 at 14:58
  • @Jacques you should add index.php/admin_custom to the exceptions behind sitemap with a pipe – Mr. Lewis Aug 21 '19 at 15:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.