diff --git a/django/utils/http.py b/django/utils/http.py
index 1433df4ff0..07b6ae246a 100644
--- a/django/utils/http.py
+++ b/django/utils/http.py
@@ -349,7 +349,10 @@ def _is_safe_url(url, allowed_hosts, require_https=False):
     # urlparse is not so flexible. Treat any url with three slashes as unsafe.
     if url.startswith('///'):
         return False
-    url_info = _urlparse(url)
+    try:
+        url_info = _urlparse(url)
+    except ValueError:  # e.g. invalid IPv6 addresses
+        return False
     # Forbid URLs like http:///example.com - with a scheme, but without a hostname.
     # In that URL, example.com is not the hostname but, a path component. However,
     # Chrome will still consider example.com to be the hostname, so we must not
diff --git a/docs/releases/1.11.2.txt b/docs/releases/1.11.2.txt
index f4d1398094..fd6b7083e9 100644
--- a/docs/releases/1.11.2.txt
+++ b/docs/releases/1.11.2.txt
@@ -15,3 +15,6 @@ Bugfixes
 * Changed ``contrib.gis`` to raise ``ImproperlyConfigured`` rather than
   ``GDALException`` if ``gdal`` isn't installed, to allow third-party apps to
   catch that exception (:ticket:`28178`).
+
+* Fixed ``django.utils.http.is_safe_url()`` crash on invalid IPv6 URLs
+  (:ticket:`28142`).
diff --git a/tests/utils_tests/test_http.py b/tests/utils_tests/test_http.py
index 80e795d3af..e3b9425184 100644
--- a/tests/utils_tests/test_http.py
+++ b/tests/utils_tests/test_http.py
@@ -100,6 +100,8 @@ class TestUtilsHttp(unittest.TestCase):
             'http:999999999',
             'ftp:9999999999',
             '\n',
+            'http://[2001:cdba:0000:0000:0000:0000:3257:9652/',
+            'http://2001:cdba:0000:0000:0000:0000:3257:9652]/',
         )
         for bad_url in bad_urls:
             with ignore_warnings(category=RemovedInDjango21Warning):