
Redirect disabled product pages instead of 404 page in Magento 2
In this blog post I’ll show how to implement a Magento 2 Extension which allows to redirect from disabled product url to
- a custom url
- assigned category of a highest level as next fallback if no custom url is entered on product level (custom attribute)
- home page as last fallback if no categories found
instead of showing a 404 Not Found page.
We will call the extension N98_DisabledProductRedirect
Extension Code
Basic files
registration.php
<?php use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register( ComponentRegistrar::MODULE, 'N98_DisabledProductRedirect', __DIR__ );
etc/module.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="N98_DisabledProductRedirect" setup_version="1.0.0"> <sequence> <module name="Magento_Catalog"/> </sequence> </module> </config>
Plugin on Product View frontend Controller
etc/frontend/di.xml
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework/ObjectManager/etc/config.xsd"> <type name="Magento\Catalog\Controller\Product\View"> <plugin name="disabled_product_redirect_plugin" type="N98\DisabledProductRedirect\Plugin\ProductRedirectPlugin" /> </type> </config>
Plugin/ProductRedirectPlugin.php
<?php declare(strict_types=1); namespace N98\DisabledProductRedirect\Plugin; use Magento\Catalog\Api\ProductRepositoryInterface; use Magento\Catalog\Model\Product; use Magento\Framework\Controller\Result\RedirectFactory; use Magento\Framework\Exception\NoSuchEntityException; use N98\DisabledProductRedirect\Service\GetRedirectUrlForDisabledProductService; class ProductRedirectPlugin { /** * @var ProductRepositoryInterface */ private ProductRepositoryInterface $productRepository; /** * @var RedirectFactory */ private RedirectFactory $redirectFactory; /** * @var GetRedirectUrlForDisabledProductService */ private GetRedirectUrlForDisabledProductService $getRedirectUrlForDisabledProductService; /** * ProductRedirectPlugin constructor. * * @param ProductRepositoryInterface $productRepository * @param RedirectFactory $redirectFactory * @param GetRedirectUrlForDisabledProductService $getRedirectUrlForDisabledProductService */ public function __construct( ProductRepositoryInterface $productRepository, RedirectFactory $redirectFactory, GetRedirectUrlForDisabledProductService $getRedirectUrlForDisabledProductService ) { $this->productRepository = $productRepository; $this->redirectFactory = $redirectFactory; $this->getRedirectUrlForDisabledProductService = $getRedirectUrlForDisabledProductService; } /** * Main plugin method. * * @param \Magento\Catalog\Controller\Product\View $subject * @param callable $proceed * @return \Magento\Framework\Controller\Result\Redirect * @throws \Magento\Framework\Exception\LocalizedException */ public function aroundExecute($subject, callable $proceed) { $productId = $subject->getRequest()->getParam('id'); try { $product = $this->productRepository->getById($productId); if ((int)$product->getStatus() === Product\Attribute\Source\Status::STATUS_DISABLED) { $redirectUrl = $this->getRedirectUrlForDisabledProductService->execute($product); $resultRedirect = $this->redirectFactory->create(); $resultRedirect->setUrl($redirectUrl); $resultRedirect->setHttpResponseCode(302); return $resultRedirect; } } catch (NoSuchEntityException $e) { return $proceed(); } return $proceed(); } }
Service “Get Redirect URL for disabled product”
Service/GetRedirectUrlForDisabledProductService.php
<?php declare(strict_types=1); namespace N98\DisabledProductRedirect\Service; use Magento\Catalog\Api\Data\ProductInterface; use Magento\Store\Model\StoreManagerInterface; class GetRedirectUrlForDisabledProductService { /** * GetRedirectUrlForDisabledProductService constructor. * * @param StoreManagerInterface $storeManager */ public function __construct(private readonly StoreManagerInterface $storeManager) { } /** * Get redirect URL for a disabled product * * @param ProductInterface $product * @return string * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function execute(ProductInterface $product): string { // Check for a defined-per-product URL attribute $customRedirectUrl = $product->getData('redirect_url_if_disabled'); if ($customRedirectUrl) { return $customRedirectUrl; } // Fallback to the highest-level category URL /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $categoryCollection */ $categoryCollection = $product->getCategoryCollection(); $categoryCollection->addAttributeToSelect('url_key')->load(); $highestLevelCategory = null; foreach ($categoryCollection as $category) { if (!$highestLevelCategory || $category->getLevel() < $highestLevelCategory->getLevel()) { $highestLevelCategory = $category; } } if ($highestLevelCategory) { /** @var \Magento\Catalog\Model\Category $highestLevelCategory */ return $highestLevelCategory->getUrl(); } // Default fallback URL (home page) return $this->storeManager->getStore()->getBaseUrl(); } }
Data Patch to create attribute “redirect_url_if_disabled”
Setup/Patch/Data/AddRedirectUrlProductAttribute.php
<?php declare(strict_types=1); namespace N98\DisabledProductRedirect\Setup\Patch\Data; use Magento\Catalog\Model\Product; use Magento\Eav\Setup\EavSetup; use Magento\Eav\Setup\EavSetupFactory; use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\Patch\DataPatchInterface; class AddRedirectUrlProductAttribute implements DataPatchInterface { /** * AddRedirectUrlProductAttribute constructor. * * @param EavSetupFactory $eavSetupFactory * @param ModuleDataSetupInterface $moduleDataSetup */ public function __construct( private readonly EavSetupFactory $eavSetupFactory, private readonly ModuleDataSetupInterface $moduleDataSetup ) { } /** * Add attribute "redirect_url_if_disabled" to products and assign to default attribute set * * @return void * @throws \Magento\Framework\Exception\LocalizedException * @throws \Magento\Framework\Validator\ValidateException */ public function apply() { /** @var EavSetup $eavSetup */ $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]); $eavSetup->addAttribute(Product::ENTITY, 'redirect_url_if_disabled', [ 'type' => 'text', 'label' => 'Redirect URL (if product is disabled)', 'input' => 'textarea', 'required' => false, 'sort_order' => 100, 'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE, 'group' => 'General', ]); $attributeSetId = $eavSetup->getDefaultAttributeSetId(Product::ENTITY); $attributeGroupId = $eavSetup->getAttributeGroupId(Product::ENTITY, $attributeSetId, 'General'); $eavSetup->addAttributeToGroup( Product::ENTITY, $attributeSetId, $attributeGroupId, 'redirect_url_if_disabled', 100 ); } /** * @inheritDoc */ public static function getDependencies() { return []; } /** * @inheritDoc */ public function getAliases() { return []; } }
Conclusion
As you could see in the code, the module is pretty simple. For redirect code we used 302 but also 301 can be used or you can make it configurable via Store Configuration.
Feel free to leave a comment.
P.S. In your comment you can answer a quiz question: was the blog post featured image AI generated or created manually? 😊
Magento Certified Developer Plus
Magento 2 Professional Developer
Magento 2 Solution Specialist