404 on Store Switch with Product Store View Scoped URL Key

The problem is a bug in the model Mage_Core_Model_Url_Rewrite_Request (Magento 1.8) and Mage_Core_Model_Url_Rewrite (earlier versions).

The section of core code in 1.8 looks like this:

    // Section from Mage_Core_Model_Url_Rewrite_Request::_rewriteDb()

    $fromStore = $this->_request->getQuery('___from_store');
    if (!$this->_rewrite->getId() && $fromStore) {
        $stores = $this->_app->getStores();
        if (!empty($stores[$fromStore])) {
            $store = $stores[$fromStore];
            $fromStoreId = $store->getId();
        } else {
            return false;
        }

The Bug: the value of the query parameter is the store code, (in my case de, en or fr). The keys of the array returned by app->getStores() are the numeric store IDs. Thats why if (!empty($stores[$fromStore])) { always fails.

Once that bug is fixed, another bug becomes apparent later in the same method (I think only in 1.8):

$targetUrl = $this->_request->getBaseUrl() . '/' . $this->_rewrite->getRequestPath();

The request objects base url always is the Magento base_url, without the store code. Using $currentStore->getBaseUrl() instead there fixes that bug, too.

Once those two issues are fixed the language switcher works fine. Here is an extension which does exactly that for Magento 1.8 (CE): https://github.com/Vinai/VinaiKopp_StoreUrlRewrites

In Magento 1.7 the issue might be something different. I still thought I'd add this answer, just in case google brings somebody else here who is running 1.8 or newer.


Actually I found a workaround for this issue on Magento 1.7.0.2 if you are running Magento 1.8 looks to Vinai's detailed explanation:

It looks that part of the problem is related to the request controller Mage_Core_Controller_Request_Http.

If you look at line 161 there is this condition:

                elseif ($storeCode !== '') {
                    $this->setActionName('noRoute');
                }

Commenting it out fix the 404 error when I switch to a different Store in a category/product pages.

However for some unknown reason some time the store code is missed in the response Url but this is not causing issue anymore as both url works now:

  • MAGEDOMAIN/sony-vaio-vgn-txn27n-b-11-1-notebook-pc-french.html
  • MAGEDOMAIN/sony-vaio-vgn-txn27n-b-11-1-notebook-pc.html

It is stil not clear to me if the comment of this condition can cause other issue


Some updated info for Magento 1.9.1

The bug @Vinai pointed out looks solved in this version anyway for other reason the functionality is still broken ( for configurable products )

The problem real problem is probably here Mage_Catalog_Model_Resource_Url however I don't have time and I dont' want to touch a such delicate part of the core.

Explanation for a workaround:

The entry point is always this classMage_Core_Model_Url_Rewrite_Request and in particular the method _rewriteDb()

How _rewriteDb() works:

  1. First it try to load the request for the current store

(139): $this->_rewrite->loadByRequestPath($requestCases);

  1. then if I cannot find it (no id) and has a ___from_store parameter

(142): if (!$this->_rewrite->getId() && $fromStore) {

  1. try to load a rewrite for the ___from_store:

(152): $this->_rewrite->setStoreId($fromStoreId)->loadByRequestPath($requestCases);

  1. if it find it, it use the id_path to load the one for the current store:

(159): $this->_rewrite->setStoreId($currentStore->getId())->loadByIdPath($this->_rewrite->getIdPath());

Everything looks fine however there is an issue in the url_rewrite data and so with the index functionality ( at least for configurable products ):

  • even if we are switching store and the new store has different url a rewrite at line 139 is loaded.

The problem is that this rewrite points to the wrong id_path ( instead of pointing to the configurable product id it is pointing to one of its simple product id )

Now a workaround is to remove the !$this->_rewrite->getId() condition and so magento try to find a redirect always when there a $fromstore parameter

  • The best would be to fix the catalog_url index and remove the wrong rewrite it creates.

Here the code for the fast workaround ( you will need to create a module and rewrite Mage_Core_Model_Url_Rewrite_Request class by your self):

protected function _rewriteDb()
    {
        if (null === $this->_rewrite->getStoreId() || false === $this->_rewrite->getStoreId()) {
            $this->_rewrite->setStoreId($this->_app->getStore()->getId());
        }

        $requestCases = $this->_getRequestCases();
        $fromStore = $this->_request->getQuery('___from_store');

        if ($fromStore) {
            $stores = $this->_app->getStores(false, true);
            if (!empty($stores[$fromStore])) {
                /** @var $store Mage_Core_Model_Store */
                $store = $stores[$fromStore];
                $fromStoreId = $store->getId();
            } else {
                return parent::_rewriteDb();
            }

            $this->_rewrite->setStoreId($fromStoreId)->loadByRequestPath($requestCases);
            if (!$this->_rewrite->getId()) {
                return parent::_rewriteDb();
            }

            // Load rewrite by id_path
            $currentStore = $this->_app->getStore();
            $this->_rewrite->setStoreId($currentStore->getId())->loadByIdPath($this->_rewrite->getIdPath());

            $this->_setStoreCodeCookie($currentStore->getCode());

            $targetUrl = $currentStore->getBaseUrl() . $this->_rewrite->getRequestPath();
            $this->_sendRedirectHeaders($targetUrl, true);
        }

        if (!$this->_rewrite->getId()) {
            return parent::_rewriteDb();
        }

        $this->_request->setAlias(Mage_Core_Model_Url_Rewrite::REWRITE_REQUEST_PATH_ALIAS,
            $this->_rewrite->getRequestPath());
        $this->_processRedirectOptions();

        return true;
    }