Problem/Motivation

I've been very confused with the addition of canonical links in Drupal 7. The default behaviour seems to be to assign the actual node url of the node being displayed on the front page as the canonical uri. But that must be incorrect, since going to that uri will only redirect to the front page.

Steps to reproduce

TBD

Proposed resolution

TBD (may still be the same but should be confirmed)

Remaining tasks

Tests
Review

User interface changes

TBD

API changes

Data model changes

Release notes snippet

The logic of the AliasPathProcessor has been changed to return / when requesting the URL of the frontpage instead of an empty string.

Original Post

I've been very confused with the addition of canonical links in Drupal 7. The default behaviour seems to be to assign the actual node url of the node being displayed on the front page as the canonical uri. But that must be incorrect, since going to that uri will only redirect to the front page. So I've made a few changes to two files:

common.inc, line 2143, before:

  if ($path == '<front>') {
    $path = '';
  }

changed to:

  if ($path == '<front>' || $path == variable_get('site_frontpage', 'node')) {
    $path = '';
  }

Now the front page has "/" as canonical url. Also, other tags like < meta about ..> get updated.

Once I got going, I made two other changes as well in node.module, line 2592:

  $uri['options']['absolute'] = TRUE;
  // Set the node path as the canonical URL to prevent duplicate content.
  $canonical_href = url($uri['path'], $uri['options']);
  drupal_add_html_head_link(array('rel' => 'canonical', 'href' => $canonical_href), TRUE);
  // Set the non-aliased path as a default shortlink.
  $shortlink_href = url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)));
  if(strlen($shortlink_href) < strlen($canonical_href)) {
    drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => $shortlink_href), TRUE);
  }

This does two things:
1. Sets canonical links as absolute urls, which seems better if site can be accessed from different urls. Should perhaps be an option somewhere?
2. Only adds the shortlink IF its url is actually shorter than the aliased path. It seems weird to say that the shortlink of "/" is "/node/12345", or that another shortlink of "/abc" is "/node/34567".

What do you think about these changes? I think it's crucial SEO-wise to get it right. I'm not an SEO expert but I've asked around a bit and this seems to be along the right tracks. I hope that the Drupal core can be updated some way to be more optimized for SEO and web standards.

CommentFileSizeAuthor
#41 interdiff-1255092-40-41.txt681 bytesyogeshmpawar
#41 1255092-41.patch3.3 KByogeshmpawar
#40 drupal-n1255092-40.patch3.3 KBDamienMcKenna
#39 drupal-n1255092-39.patch2.43 KBDamienMcKenna
#38 interdiff_1255092_37-38.txt281 bytesankithashetty
#38 1255092-38.patch2.56 KBankithashetty
#37 fix_path_processor_front-1255092-37.patch2.56 KBRuuds
#34 fix_path_processor_front-1255092-34.patch2.78 KBNilesh Chhantbar
#32 fix_path_processor_front-1255092-32.patch1.43 KBpurushotam.rai
#31 fix_path_processor_front-1255092-31.patch1.42 KBpurushotam.rai
#25 interdiff_1255092-23-25.txt704 bytesmaximpodorov
#25 fix_path_processor_front-1255092-25.patch2.83 KBmaximpodorov
#23 interdiff_1255092-20-23.txt1.06 KBmaximpodorov
#23 fix_path_processor_front-1255092-23.patch2.83 KBmaximpodorov
#20 interdiff_1255092-18-20.txt1.17 KBmaximpodorov
#20 fix_path_processor_front-1255092-20.patch2.73 KBmaximpodorov
#18 fix_path_processor_front-1255092-18.patch2.73 KBextralooping

Issue fork drupal-1255092

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Damien Tournoud’s picture

Title: Incorrect canonical links on front page » url() should return / when asked for the URL of the frontpage
Version: 7.7 » 8.x-dev

This is definitely not optimal. Triaging.

The gist of this issue is that url() should return / when asked for the URL of a path that is assigned to the frontpage.

There is nothing wrong with relative canonical links. Drupal's default behavior is to output relative links everywhere (this behavior can be altered using hook_url_outbound_alter(). There is also nothing wrong with the "shortlink" being potentially longer then the "canonical" either. The original specification mentions "[The shortlink] will typically be shorter than other URIs".

pjcdawkins’s picture

I agree url() should return / for <front>.

#1: I agree the canonical link can be relative. But shouldn't the shortlink be an absolute URL?

dillix’s picture

Version: 8.x-dev » 7.x-dev

+1

marcingy’s picture

Version: 7.x-dev » 8.x-dev

Issue are fixed in d8 first

marcingy’s picture

Issue summary: View changes

fixed invisible tag

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

akalata’s picture

In D8 this is relevant when using \Drupal::service('path.alias_manager')->getAliasByPath('/node/' . $node->id());, returns '/node/[nid]' instead of '/' when the node is set as the front page.

borisson_’s picture

We fixed this in a client project by overwriting the UrlGenerator class's doGenerate method with this:

$url = parent::doGenerate($variables, $defaults, $tokens, $parameters, $query_params, $name);

// Load the front page config.
$frontPagePath = \Drupal::config('system.site')
  ->get('page.front');

// If the url currently being generated is to the front page, overwrite the
// output to /.
if ($frontPagePath == $url) {
  $url = '/';
}

return $url;

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

raphaeltbm’s picture

@borisson_ It seems that your solution fix automatically a lot of problems relatives to the frontpage url case issued in various modules (alternate hreflang url, canonical url, frontpage urls displayed in various places (view content, ...)). But, it's a bit too brutal, you can't access anymore to the internal path of the current object if it match as the frontpage and so it breaks a lot of things.

Stuff like:

Url::fromRouteMatch(\Drupal::routeMatch())->getInternalPath();
\Drupal::service('path.matcher')->isFrontPage();

Become ineffective for the object/entity/view/whatever defined as frontpage.

At what level should it be fixed? In UrlGenerator OR Entity->toUrl() OR ... ? I can't see a kind of global solution for this right now.

See my comment #2987635-4: Front Page canonical is Incorrect

I guess the frontpage canonical url being not the internal path or aliased path, is the most expected behaviour by most of the people, no?

If a new kind of rel canonical ('canonical-aware') or a new option for Entity->toUrl() ('frontpage-aware') have to be created, all core and contribs modules should update their way to retrieve the URL of an entity following the context.
Or there is just no real global solution possible in the current state of the core and it should stay the responsibility of each developer/module to manage the frontpage url case when needed?

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

bpizzillo’s picture

In case anyone is looking, I created a service implementing the OutboundPathProcessorInterface to return '/' for anyone generating a link to the front-page node ID. What I don't understand is why PathProcessorFront.php in core/lib/Drupal/Core/PathProcessor does not do this, but only replaces `/` with `/`.

my_module.services.yml

services:
  my_module.path_processor:
    class: Drupal\my_module\FixHomeUrlAliasOutboundPathProcessor
    tags:
      - { name: path_processor_outbound, priority: 400 }
    arguments: ['@config.factory']

FixHomeUrlAliasOutboundPathProcessor.php

<?php

namespace Drupal\my_module;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Symfony\Component\HttpFoundation\Request;

/**
 * Outbound path processor to fix home aliases.
 *
 * This needs to fire BEFORE the alias processor.
 */
class FixHomeUrlAliasOutboundPathProcessor implements OutboundPathProcessorInterface {

  /**
   * Constructs a FixHomeUrlAliasOutboundPathProcessor object.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config
   *   A config factory for retrieving the site front page configuration.
   */
  public function __construct(ConfigFactoryInterface $config) {
    $this->config = $config;
  }

  /**
   * {@inheritdoc}
   */
  public function processOutbound($path, &$options = [], Request $request = NULL, BubbleableMetadata $bubbleable_metadata = NULL) {
    // Get the route for the front page.
    $front_path = $this->config->get('system.site')->get('page.front');

    // There is no front page, so bail.
    if (empty($front_path)) {
      return $path;
    }

    if ($front_path === $path) {
      return '/';
    }
    else {
      return $path;
    }
  }

}
matthewv789’s picture

Have you tried entering "/" as the path alias for that node?

kevster’s picture

I have tried adding / as an alias to the homepage - the simple xml sitemap now generates two urls for the homepage:

https://www.example.com/

and

https://www.example.com

The second one should not be there at all?

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

extralooping’s picture

Version: 8.8.x-dev » 8.9.x-dev
Component: node system » routing system
Status: Active » Needs review
FileSize
2.73 KB

I made a patch for PathProcessorFront based on #14. I added some extra logic, as the path configured for frontpage can also be an alias. I did some testing and so far seems to works well. Besides further testing, we probably need to add unit test as well. I have no experience here and it's seems a little more complicated, maybe someone else iabel to provide ii?

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

maximpodorov’s picture

This patch may be more reliable on older Drupal core versions.

Status: Needs review » Needs work

The last submitted patch, 20: fix_path_processor_front-1255092-20.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

extralooping’s picture

Status: Needs work » Needs review

The patch from #20 fails, because it reintroduces deprecated namespaces.

maximpodorov’s picture

The path language must be considered as it can differ from the current language.

Status: Needs review » Needs work

The last submitted patch, 23: fix_path_processor_front-1255092-23.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

maximpodorov’s picture

Status: Needs review » Needs work

The last submitted patch, 25: fix_path_processor_front-1255092-25.patch, failed testing. View results

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

dieppon’s picture

#18 worked for me

JayKandari’s picture

#20 worked ... :)

purushotam.rai’s picture

Path Alias core module is optional and hence probably we cannot directly inject this service. We will have to find some alternative solution.

Checkout this: https://www.drupal.org/project/drupal/issues/3096092

With this patch applied on drupal/core, Drupal installation fails.

purushotam.rai’s picture

Temporary Fix to use path_alias.manager conditionally, created patch.

purushotam.rai’s picture

Somehow hasService is working differnetly, I've updated the temporary solution to use Drupal module handler.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Nilesh Chhantbar’s picture

zuker2’s picture

Nilesh Chhatbar Can u send to me this files after fix? something i do wrong and i have still problem after fix, site take down
or public paste surc both files - i don't use git to update- manualy edit

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Ruuds’s picture

Attached is a path that applies to 9.3.x. Drupal\Core\PathProcessor\PathProcessorFront lost its OutboundPathProcessorInterface implementation in 9.3.

ankithashetty’s picture

Just fixed the custom command failure error in #37, thanks!

DamienMcKenna’s picture

Status: Needs work » Needs review
FileSize
2.43 KB

So how about if the logic was moved into the path_alias module?

DamienMcKenna’s picture

I missed a bit in the last patch.

yogeshmpawar’s picture

Resolves CSpell error & added an interdiff.

Status: Needs review » Needs work

The last submitted patch, 41: 1255092-41.patch, failed testing. View results

DamienMcKenna’s picture

The tests failures are down from 97 to 15, that's an accomplishment ;-)

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Just postponed #3100350: Unable to save '/' root path alias on this one so triaging it some.

Cleanup the issue summary would help. Started with the template.

Will also need tests.

Anybody’s picture

Thanks @smustgrave. So @yogeshmpawar could you perhaps reroll this as MR and try to fix the remaining tests?

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Grevil made their first commit to this issue’s fork.

Grevil’s picture

I created an issue fork on 11.x and applied patch "1255092-41.patch" by @yogeshmpawar.

Grevil’s picture

Version: 11.x-dev » 10.1.x-dev

Hm, Jenkins seems to be configured differently for 11.x? At least 1 PHPStan error, doesn't really make sense.

Changing to 10.1.x-dev

Anybody’s picture

@Grevil: I think we should still target 10.1.x - if needed, we can switch later.

Grevil’s picture

Alright, no chance to run the tests remotely on neither 10.1.x nor 11.x. I'll test it locally...

Anybody’s picture

Status: Needs work » Needs review

The patch had a breaking change in the constructor parameters, which of course isn't allowed, at least if it's not consistently changed.

So for now I reverted that change, but kept the class property:

  public function __construct(AliasManagerInterface $alias_manager) {
    $this->aliasManager = $alias_manager;
    $this->config = \Drupal::service('config.factory');
  }

Unsure what's the DI strategy here in core for this case, perhaps someone else could have a look?

I guess the tests should run now.

smustgrave’s picture

Believe should be targeting 11.x as that’s the latest development branch. And changes can be backported

Anybody’s picture

@Grevil did you read #56?

Any good reasons why you broke it again later in https://git.drupalcode.org/project/drupal/-/merge_requests/4030/diffs?co...?

I don't think it's a good idea here to provide the generic config as 2nd parameter in __construct() in this class where there's no DI.
Where ever this is called outside, that call would have to be replaced to provide the 2nd parameter. Isn't that a BC here then?

Of course I might be wrong, but please leave a comment here then to explain the reasons.

You also revertet my other fixes without leaving a comment. Why?

Now we're back at the state where we were before with issues like

Running PHPStan on *all* files.
 ------ -------------------------------------------------------------------- 
  Line   core/modules/path_alias/src/PathProcessor/AliasPathProcessor.php    
 ------ -------------------------------------------------------------------- 
  71     Variable $system_config in empty() always exists and is not falsy.  
 ------ -------------------------------------------------------------------- 
BramDriesen’s picture

Status: Needs review » Needs work
Issue tags: +Needs change record, +Needs release note

Since we are adding dependencies to an existing interface, I guess we need a change record as this is a breaking change.

Grevil’s picture

@Anybody, the class you manually implemented the "\Drupal::service" call does indeed not implement the "ContainerInjectionInterface". But that's because it is a service itself and the dependency injection happens through the service.yml (see "core/modules/path_alias/path_alias.services.yml" -> "path_alias.path_processor").

Grevil’s picture

Status: Needs work » Needs review

From the MR by @Anybody:

Are we sure the $path is passed as /<front> indeed?
I didn't check that, but in the UI you typically use <front> without leading slash, so this should be double-checked!
Perhaps write a test for this to be 100% sure, if not yet existing.

This is definitely important as "/" is quite untypical!

I don't have much time the next couple of weeks, so if anyone could add a test for this and do some general clean-up, go for it!

smustgrave’s picture

Status: Needs review » Needs work

Did not test

Issue summary needs updating
Change record
Release notes

BramDriesen’s picture

Issue summary: View changes
Issue tags: -Needs change record, -Needs release note

Change record drafted: https://www.drupal.org/node/3362769
Release note added to the issue summary.

Still needs work for the CCF.

BramDriesen’s picture

Will leave it at needs work for the tests and issue summary updates.

Berdir’s picture

I think this should not be in the path_alias module, which is now technically optional although most people are using it. It should be a separate event subscriber in core.

DSushmita’s picture

Solution:

1. Install and enable the "Metatag" module.
2. Configure the Metatag module by navigating to admin/config/search/metatag.
3. Define a custom token for the canonical URL by clicking on "Add custom token" in the Metatag configuration.
4. Set the custom token as [node:url:absolute] for the canonical URL on the front page.
5. Save the configuration and clear the Drupal caches.

By following these steps, the Metatag module will allow you to define a custom token for the canonical URL. You can set this custom token as [node:URL: absolute] for the canonical URL on the front page. Saving the configuration and clearing the Drupal caches will ensure that the updated canonical URL settings take effect.

Anybody’s picture

@DSushmita thanks for the workaround, but at least for the cases discussed here, #67 isn't a real solution. Still, it might help some folks.

Grevil’s picture

What's everyone's opinion on @berdir's comment in #66? What would be a good place for this Event Subscriber, if we would move the implementation to core?

andypost’s picture

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

manuel.adan made their first commit to this issue’s fork.

manuel.adan’s picture

Another approach made to this in branch 1255092-front_page_outbound_url_path_processor, based as well in URL processor but outside of URL alias as suggested by Berdir at #66. I added outbound URL processing to the existing front page path processor.

It may fail in case of a front page path with query parameters were set, but it is also not well managed by other core parts like PathMatcher, see #3442235: Front page path with query parameters makes front path condition to fail. Added as related issue since support for query parameters should be added.

smustgrave’s picture

Status: Needs review » Needs work

Will need test coverage and issue summary update.

Left some comments.