 core/modules/file/src/Entity/File.php              | 31 ++++++++++++++++++++++
 .../hal/src/Normalizer/ContentEntityNormalizer.php | 12 +++++++--
 .../EntityReferenceFieldItemNormalizer.php         |  5 ++--
 .../EntityReferenceFieldItemNormalizerTest.php     | 14 ++++++++--
 .../Drupal/Tests/Listeners/DeprecationListener.php |  1 -
 5 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/core/modules/file/src/Entity/File.php b/core/modules/file/src/Entity/File.php
index a9ade9e..dee25ab 100644
--- a/core/modules/file/src/Entity/File.php
+++ b/core/modules/file/src/Entity/File.php
@@ -7,6 +7,7 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Url;
 use Drupal\file\FileInterface;
 use Drupal\user\UserInterface;
 
@@ -71,12 +72,42 @@ public function setFileUri($uri) {
    * @see file_url_transform_relative()
    */
   public function url($rel = 'canonical', $options = []) {
+    // @todo Remove this work-around before Drupal 9.0.0. This was introduced as
+    // a temporary work-around in https://www.drupal.org/node/2277705 to provide
+    // REST support for file entities. https://www.drupal.org/node/2825487 makes
+    // this obsolete.
     return file_create_url($this->getFileUri());
   }
 
   /**
    * {@inheritdoc}
    */
+  public function toUrl($rel = 'canonical', array $options = []) {
+    // Lie because we must provide BC.
+    // @see ::url()
+    // @todo Remove in https://www.drupal.org/node/2825487.
+    if ($rel === 'canonical') {
+      return Url::fromUri($this->url());
+    }
+    return parent::toUrl($rel, $options);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasLinkTemplate($rel) {
+    // Lie because we must provide BC.
+    // @see ::url()
+    // @todo Remove in https://www.drupal.org/node/2825487.
+    if ($rel === 'canonical') {
+      return TRUE;
+    }
+    return parent::hasLinkTemplate($rel);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getMimeType() {
     return $this->get('filemime')->value;
   }
diff --git a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
index 4a9f7c4..c41029b 100644
--- a/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
+++ b/core/modules/hal/src/Normalizer/ContentEntityNormalizer.php
@@ -179,17 +179,25 @@ public function denormalize($data, $class, $format = NULL, array $context = [])
    *
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity.
+   * @param array $context
+   *   Normalization/serialization context.
+   *
    * @return string
    *   The entity URI.
    */
-  protected function getEntityUri(EntityInterface $entity) {
+  protected function getEntityUri(EntityInterface $entity, array $context = []) {
     // Some entity types don't provide a canonical link template, at least call
     // out to ->url().
     if ($entity->isNew() || !$entity->hasLinkTemplate('canonical')) {
       return $entity->url('canonical', []);
     }
     $url = $entity->urlInfo('canonical', ['absolute' => TRUE]);
-    return $url->setRouteParameter('_format', 'hal_json')->toString();
+    if (!$url->isExternal()) {
+      $url->setRouteParameter('_format', 'hal_json');
+    }
+    $generated_url = $url->toString(TRUE);
+    $this->addCacheableDependency($context, $generated_url);
+    return $generated_url->getGeneratedUrl();
   }
 
   /**
diff --git a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php
index ea2e020..c0a83b7 100644
--- a/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php
+++ b/core/modules/serialization/src/Normalizer/EntityReferenceFieldItemNormalizer.php
@@ -51,8 +51,9 @@ public function normalize($field_item, $format = NULL, array $context = []) {
       // Add a 'url' value if there is a reference and a canonical URL. Hard
       // code 'canonical' here as config entities override the default $rel
       // parameter value to 'edit-form.
-      if ($url = $entity->url('canonical')) {
-        $values['url'] = $url;
+      if ($entity->hasLinkTemplate('canonical') && $url = $entity->toUrl('canonical')->toString(TRUE)) {
+        $this->addCacheableDependency($context, $url);
+        $values['url'] = $url->getGeneratedUrl();
       }
     }
     return $values;
diff --git a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
index e9df564..f0849d7 100644
--- a/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
+++ b/core/modules/serialization/tests/src/Unit/Normalizer/EntityReferenceFieldItemNormalizerTest.php
@@ -4,12 +4,14 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\GeneratedUrl;
 use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\Core\Entity\EntityRepositoryInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
+use Drupal\Core\Url;
 use Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer;
 use Drupal\Tests\UnitTestCase;
 use Prophecy\Argument;
@@ -104,9 +106,17 @@ public function testSupportsDenormalization() {
   public function testNormalize() {
     $test_url = '/test/100';
 
+    $generated_url = (new GeneratedUrl())->setGeneratedUrl($test_url);
+
+    $url = $this->prophesize(Url::class);
+    $url->toString(TRUE)
+      ->willReturn($generated_url);
+
     $entity = $this->prophesize(EntityInterface::class);
-    $entity->url('canonical')
-      ->willReturn($test_url)
+    $entity->hasLinkTemplate('canonical')
+      ->willReturn(TRUE);
+    $entity->toUrl('canonical')
+      ->willReturn($url)
       ->shouldBeCalled();
     $entity->uuid()
       ->willReturn('080e3add-f9d5-41ac-9821-eea55b7b42fb')
diff --git a/core/tests/Drupal/Tests/Listeners/DeprecationListener.php b/core/tests/Drupal/Tests/Listeners/DeprecationListener.php
index 80b3d31..b19d626 100644
--- a/core/tests/Drupal/Tests/Listeners/DeprecationListener.php
+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListener.php
@@ -109,7 +109,6 @@ public static function getSkippedDeprecations() {
       'CommentVariablePerCommentType is deprecated in Drupal 8.4.x and will be removed before Drupal 9.0.x. Use \Drupal\node\Plugin\migrate\source\d6\NodeType instead.',
       'The Drupal\config_translation\Plugin\migrate\source\d6\I18nProfileField is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use Drupal\config_translation\Plugin\migrate\source\d6\ProfileFieldTranslation',
       'The Drupal\migrate_drupal\Plugin\migrate\source\d6\i18nVariable is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Instead, use Drupal\migrate_drupal\Plugin\migrate\source\d6\VariableTranslation',
-      'Implicit cacheability metadata bubbling (onto the global render context) in normalizers is deprecated since Drupal 8.5.0 and will be removed in Drupal 9.0.0. Use the "cacheability" serialization context instead, for explicit cacheability metadata bubbling. See https://www.drupal.org/node/2918937',
       'Automatically creating the first item for computed fields is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. Use \Drupal\Core\TypedData\ComputedItemListTrait instead.',
       '"\Drupal\Core\Entity\ContentEntityStorageBase::doLoadRevisionFieldItems()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. "\Drupal\Core\Entity\ContentEntityStorageBase::doLoadMultipleRevisionsFieldItems()" should be implemented instead. See https://www.drupal.org/node/2924915.',
       'Passing a single revision ID to "\Drupal\Core\Entity\Sql\SqlContentEntityStorage::buildQuery()" is deprecated in Drupal 8.5.x and will be removed before Drupal 9.0.0. An array of revision IDs should be given instead. See https://www.drupal.org/node/2924915.',
