I have a configurable product consisting of simple product. The URL is something like


I want to change this to have the canonical URL as http://www.domain.com/my-configurable-product.html

How do I change the URL of the Magento2 page? I have the following in my observer which is fired from the view_block_abstract_to_html_before event:

class ModifyCanonicalUrl implements \Magento\Framework\Event\ObserverInterface {
    private $_logger;

    public function __construct(\Psr\Log\LoggerInterface $logger) {
        // Observer initialization code...
        // You can use dependency injection to get any class this observer may need.
        $this->_logger = $logger;

    public function execute(\Magento\Framework\Event\Observer $observer) {
        // Observer execution code...
        $block = $observer->getBlock();
        $this->_logger->addDebug("URL = " . $block->getUrl());
        // Check if this is for a Product Page
        // Any other additional checks?
        // Find current product id
        // Check if it is a simple product
        // Replace the canonical URL with the configurable product url.

What is the equivalent code for the above lines in comments?

1 Answer 1


I wanted to do the same thing, spended a couple hours. In magento1 i solved it by removing "link_rel" item from the Mage_Page_Block_Html_Head by url and added the new one.

In magento2 there is the "pageAssets" object in the "pageConfig" which holds all the meta stuff. Problem is that you can not get the unrendered object. There is the getAssetCollection method, but when you call it, all assets will be rendered and can not be modified.

So i started searching another way and the only thing i found besides rewriting the "product view block" was a plugin which comes before $this->pageConfig->addRemotePageAsset.

class ManipulateCanonical
     * @var \Magento\Framework\Registry
    private $registry;
     * @var \XXX\ProductChildUpdate\Helper\Data
    private $helper;
     * @var \Magento\Catalog\Model\Product
    private $productModel;

    public function __construct(
        \Magento\Framework\Registry $registry,
        \Magento\Framework\App\Request\Http $request,
        \XXX\ProductChildUpdate\Helper\Data $helper,
        \Magento\Catalog\Model\Product $productModel
    ) {
        $this->registry = $registry;
        $this->request = $request;
        $this->helper = $helper;
        $this->productModel = $productModel;

    public function beforeAddRemotePageAsset(
        \Magento\Framework\View\Page\Config $subject,
        $properties = [],
        $name = null
    ) {
        if($this->request->getFullActionName() != 'catalog_product_view'
            || $contentType != 'canonical'
            || !$this->registry->registry('product')
            || ($this->registry->registry('product') && $this->registry->registry('product')->getTypeId() != \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE)
        ) {
            return null;

        $product = $this->registry->registry('product');

        $configRelations = $this->helper->getConfigurableRelations();
        if(isset($configRelations[$product->getId()])) {
            $url = $product->getUrlModel()->getUrl($this->productModel, ['_ignore_category' => true]);

        return [$url, $contentType, $properties, $name];

Only thing you have to change is the $this->helper->getConfigurableRelations(). In magento1 i created that method to get the corresponding config product. In magento2 i saw a method in the "configurable-product" module which does that already. \Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable::getParentIdsByChild.

I will stay with my helper method because i have some customizations there. But for you the getParentIdsByChild should be enough.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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