diff --git a/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php
index 0573eea..5bc7aea 100644
--- a/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php
@@ -8,9 +8,11 @@
 namespace Drupal\Core\EventSubscriber;
 
 use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Routing\RedirectResponseToExternalUrl;
 use Drupal\Core\Routing\RequestContext;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\HttpKernel\KernelEvents;
 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
 use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
@@ -90,6 +92,14 @@ public function checkRedirectUrl(FilterResponseEvent $event) {
           $response->setTargetUrl($destination);
         }
       }
+
+      // In case someone setup a custom RedirectResponse object ensure that the
+      // URL is not external. In case this is wanted, check for a custom flag
+      // on RedirectResponse.
+      $url = $response->getTargetUrl();
+      if (UrlHelper::isExternal($url) && !UrlHelper::externalIsLocal($url, $this->requestContext->getCompleteBaseUrl()) && !$response instanceof RedirectResponseToExternalUrl) {
+        throw new BadRequestHttpException('Redirects to external URLs are not allowed by default, use \Drupal\Core\Routing\RedirectResponseToExternalUrl for it');
+      }
     }
   }
 
diff --git a/core/lib/Drupal/Core/Routing/RedirectResponseToExternalUrl.php b/core/lib/Drupal/Core/Routing/RedirectResponseToExternalUrl.php
new file mode 100644
index 0000000..a18e23f
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/RedirectResponseToExternalUrl.php
@@ -0,0 +1,19 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Routing\RedirectResponse.
+ */
+
+namespace Drupal\Core\Routing;
+
+use \Symfony\Component\HttpFoundation\RedirectResponse;
+
+/**
+ * Overrides the symfony redirect response object to provide more security.
+ *
+ * @see \Drupal\Core\EventSubscriber\RedirectResponseSubscriber::checkRedirectUrl
+ */
+class RedirectResponseToExternalUrl extends RedirectResponse {
+
+}
diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
index 3b90bff..2255f29 100644
--- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php
+++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
@@ -617,6 +617,19 @@ function testDuplicateFieldName() {
   }
 
   /**
+   * Tests that external URLs in the 'destinations' query parameter are blocked.
+   */
+  public function testExternalDestinations() {
+    $options = [
+      'query' => ['destinations' => ['http://example.com']],
+    ];
+    $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.body/storage', [], 'Save field settings', $options);
+    // The external redirect should not fire.
+    $this->assertUrl('admin/structure/types/manage/article/fields/node.article.body/storage', $options);
+    $this->assertResponse(400);
+  }
+
+  /**
    * Tests that deletion removes field storages and fields as expected for a term.
    */
   function testDeleteTaxonomyField() {
diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php
index e100205..c4bef68 100644
--- a/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php
+++ b/core/tests/Drupal/Tests/Core/EventSubscriber/RedirectResponseSubscriberTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Core\EventSubscriber;
 
 use Drupal\Core\EventSubscriber\RedirectResponseSubscriber;
+use Drupal\Core\Routing\RedirectResponseToExternalUrl;
 use Drupal\Core\Routing\RequestContext;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\EventDispatcher\EventDispatcher;
@@ -87,12 +88,10 @@ public function testDestinationRedirect(Request $request, $expected) {
   public static function providerTestDestinationRedirect() {
     return array(
       array(new Request(), FALSE),
-      array(new Request(array('destination' => 'http://example.com')), FALSE),
-      array(new Request(array('destination' => 'http://example.com/foobar')), FALSE),
       array(new Request(array('destination' => 'http://example.ca/drupal')), FALSE),
       array(new Request(array('destination' => 'test')), 'http://example.com/drupal/test'),
-      array(new Request(array('destination' => '/test')), 'http://example.com/test'),
-      array(new Request(array('destination' => '/example.com')), 'http://example.com/example.com'),
+      array(new Request(array('destination' => '/drupal/test')), 'http://example.com/drupal/test'),
+      array(new Request(array('destination' => 'example.com')), 'http://example.com/drupal/example.com'),
       array(new Request(array('destination' => 'example:com')), 'http://example.com/drupal/example:com'),
       array(new Request(array('destination' => 'javascript:alert(0)')), 'http://example.com/drupal/javascript:alert(0)'),
       array(new Request(array('destination' => 'http://example.com/drupal/')), 'http://example.com/drupal/'),
@@ -101,6 +100,99 @@ public static function providerTestDestinationRedirect() {
   }
 
   /**
+   * @dataProvider providerTestDestinationRedirectToExternalUrl
+   *
+   * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+   */
+  public function testDestinationRedirectToExternalUrl($request, $expected) {
+    $dispatcher = new EventDispatcher();
+    $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
+    $response = new RedirectResponse(NULL);
+    $url_generator = $this->getMockBuilder('Drupal\Core\Routing\UrlGenerator')
+      ->disableOriginalConstructor()
+      ->setMethods(array('generateFromPath'))
+      ->getMock();
+
+    if ($expected) {
+      $url_generator
+        ->expects($this->any())
+        ->method('generateFromPath')
+        ->willReturnMap([
+          ['test', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/drupal/test'],
+          ['example.com', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/drupal/example.com'],
+          ['example:com', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/drupal/example:com'],
+          ['javascript:alert(0)', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/drupal/javascript:alert(0)'],
+          ['/test', ['query' => [], 'fragment' => '', 'absolute' => TRUE], FALSE, 'http://example.com/test'],
+        ]);
+    }
+
+    $request_context = $this->getMockBuilder('Drupal\Core\Routing\RequestContext')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $request_context->expects($this->any())
+      ->method('getCompleteBaseUrl')
+      ->willReturn('http://example.com/drupal');
+    $request->headers->set('HOST', 'example.com');
+
+    $listener = new RedirectResponseSubscriber($url_generator, $request_context);
+    $dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'checkRedirectUrl'));
+    $event = new FilterResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST, $response);
+    $dispatcher->dispatch(KernelEvents::RESPONSE, $event);
+
+    $target_url = $event->getResponse()->getTargetUrl();
+    if ($expected) {
+      $this->assertEquals($expected, $target_url);
+    }
+    else {
+      $this->assertEquals('http://example.com/drupal', $target_url);
+    }
+  }
+
+  /**
+   * @covers ::checkRedirectUrl
+   */
+  public function testRedirectWithOptInExternalUrl() {
+    $dispatcher = new EventDispatcher();
+    $kernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
+    $response = new RedirectResponseToExternalUrl('http://external-url.com');
+    $url_generator = $this->getMockBuilder('Drupal\Core\Routing\UrlGenerator')
+      ->disableOriginalConstructor()
+      ->setMethods(array('generateFromPath'))
+      ->getMock();
+
+    $request_context = $this->getMockBuilder('Drupal\Core\Routing\RequestContext')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $request_context->expects($this->any())
+      ->method('getCompleteBaseUrl')
+      ->willReturn('http://example.com/drupal');
+
+    $request = Request::create('');
+    $request->headers->set('HOST', 'example.com');
+
+    $listener = new RedirectResponseSubscriber($url_generator, $request_context);
+    $dispatcher->addListener(KernelEvents::RESPONSE, array($listener, 'checkRedirectUrl'));
+    $event = new FilterResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST, $response);
+    $dispatcher->dispatch(KernelEvents::RESPONSE, $event);
+
+    $target_url = $event->getResponse()->getTargetUrl();
+    $this->assertEquals('http://external-url.com', $target_url);
+  }
+
+  /**
+   * Data provider for testDestinationRedirectToExternalUrl().
+   */
+  public function providerTestDestinationRedirectToExternalUrl() {
+    return [
+      'absolute external url' => [new Request(['destination' => 'http://example.com']), 'http://example.com'],
+      'absolute external url with folder' => [new Request(['destination' => 'http://example.com/foobar']), 'http://example.com/foobar'],
+      'path without drupal basepath' => [new Request(['destination' => '/test']), 'http://example.com/test'],
+      'path with URL' => [new Request(['destination' => '/example.com']), 'http://example.com/example.com'],
+      'path with URL and two slashes' => [new Request(['destination' => '//example.com']), 'http://example.com//example.com'],
+    ];
+  }
+
+  /**
    * @expectedException \InvalidArgumentException
    *
    * @dataProvider providerTestDestinationRedirectWithInvalidUrl
