Make canonical URL in document HEAD absolute
True, for nodes you can't use hook_page_attachments_alter. You have to use hook_ENTITY_TYPE_view_alter!

And very likely you also find all of them in the end in hook_preprocess_html.

Here's how I got it working:

/**
 * Implements hook_ENTITY_TYPE_view_alter().
 */
function MYMODULE_node_view_alter(array &amp;$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
  if (isset($build['#attached']['html_head_link'])) {
    foreach ($build['#attached']['html_head_link'] as $key =&gt; $head) {
      if ((isset($head[0]['rel']) ? $head[0]['rel'] : FALSE) == 'canonical') {

        $url = \Drupal\Core\Url::fromRoute('&lt;current&gt;', [], ['absolute' =&gt; 'true'])
          -&gt;toString();

        $build['#attached']['html_head_link'][$key][0]['href'] = $url;
      }
    }
  };
}


Source: How to remove specific meta-tag from header section of page?



Alternatively you can use Metatag. All you have to do is to set the [node:url:absolute] token into the Canonical URL form field in the Advanced section of the Metatag configuration for all Content.



And only then, all of sudden, they become available to be preprocessed in hook_page_attachments_alter. There I removed their trailing slash which gets added by default.

/**
 * Implements hook_page_attachments_alter().
 */
function MYTHEME/MYMODULE_page_attachments_alter(array &amp;$attachments) {

  // Remove trailing slash from canonicals.
  foreach ($attachments['#attached']['html_head'] as $key =&gt; $head) {

    if ((isset($head[1]) ? $head[1] : FALSE) == 'canonical_url') {

      $href = isset($head[0]['#attributes']['href']) ? $head[0]['#attributes']['href'] : FALSE;

      if ($href) {
        $attachments['#attached']['html_head'][$key][0]['#attributes']['href'] = rtrim($href, '/');
      }
    }
  }
}