diff --git a/django/contrib/admin/templates/admin/submit_line.html b/django/contrib/admin/templates/admin/submit_line.html
index a1082ed196..b2b2054966 100644
--- a/django/contrib/admin/templates/admin/submit_line.html
+++ b/django/contrib/admin/templates/admin/submit_line.html
@@ -5,7 +5,10 @@
{% if show_save_as_new %}{% endif %}
{% if show_save_and_add_another %}{% endif %}
{% if show_save_and_continue %}{% endif %}
-{% if show_close %}{% translate 'Close' %}{% endif %}
+{% if show_close %}
+ {% url opts|admin_urlname:'changelist' as changelist_url %}
+ {% translate 'Close' %}
+{% endif %}
{% if show_delete_link and original %}
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
{% translate "Delete" %}
diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py
index 752b46b757..548444390f 100644
--- a/tests/admin_views/tests.py
+++ b/tests/admin_views/tests.py
@@ -7949,6 +7949,21 @@ class AdminKeepChangeListFiltersTests(TestCase):
self.assertRedirects(response, self.get_add_url())
post_data.pop("_addanother")
+ def test_change_view_close_link(self):
+ viewuser = User.objects.create_user(
+ username="view", password="secret", is_staff=True
+ )
+ viewuser.user_permissions.add(
+ get_perm(User, get_permission_codename("view", User._meta))
+ )
+ self.client.force_login(viewuser)
+ response = self.client.get(self.get_change_url())
+ close_link = re.search(
+ 'Close', response.content.decode()
+ )
+ close_link = close_link[1].replace("&", "&")
+ self.assertURLEqual(close_link, self.get_changelist_url())
+
def test_change_view_without_preserved_filters(self):
response = self.client.get(self.get_change_url(add_preserved_filters=False))
# The action attribute is omitted.