diff --git a/cherrypy/_cperror.py b/cherrypy/_cperror.py
index 6c952e91..b597c645 100644
--- a/cherrypy/_cperror.py
+++ b/cherrypy/_cperror.py
@@ -271,7 +271,7 @@ class HTTPRedirect(CherryPyException):
                 307: 'This resource has moved temporarily to ',
             }[status]
             msg += '<a href=%s>%s</a>.'
-            msgs = [msg % (saxutils.quoteattr(u), u) for u in self.urls]
+            msgs = [msg % (saxutils.quoteattr(u), escape_html(u)) for u in self.urls]
             response.body = ntob('<br />\n'.join(msgs), 'utf-8')
             # Previous code may have set C-L, so we have to reset it
             # (allow finalize to set it).

diff --git a/cherrypy/test/test_core.py b/cherrypy/test/test_core.py
index 2e590a9d..f16efd58 100644
--- a/cherrypy/test/test_core.py
+++ b/cherrypy/test/test_core.py
@@ -150,6 +150,9 @@ class CoreRequestHandlingTest(helper.CPWebCase):
             def url_with_quote(self):
                 raise cherrypy.HTTPRedirect("/some\"url/that'we/want")
 
+            def url_with_xss(self):
+                raise cherrypy.HTTPRedirect("/some<script>alert(1);</script>url/that'we/want")
+
             def url_with_unicode(self):
                 raise cherrypy.HTTPRedirect(ntou('тест', 'utf-8'))
 
@@ -435,6 +438,13 @@ class CoreRequestHandlingTest(helper.CPWebCase):
         self.assertStatus(303)
         assertValidXHTML()
 
+    def test_redirect_with_xss(self):
+        """A redirect to a URL with HTML injected should result in page contents escaped."""
+        self.getPage('/redirect/url_with_xss')
+        self.assertStatus(303)
+        assert b'<script>' not in self.body
+        assert b'&lt;script&gt;' in self.body
+
     def test_redirect_with_unicode(self):
         """
         A redirect to a URL with Unicode should return a Location

Encode URLs in redirect responses
* **I'm submitting a ...**
[x] bug report
[ ] feature request
[ ] question about the decisions made in the repository

* **What is the current behavior?**
The standard response page for 30x errors contains the target URL as `href` as well as clear text.

* **What is the expected behavior?**
Even though browsers *mostly* will not display the result, but redirect instead, you never know:
Any input from unknown sources should be html-escaped.

* **Please tell us about your environment:**
CherryPy 3.8 until 10.1

cherrypy/test/test_core.py::CoreRequestHandlingTest::test_redirect_with_xss
cherrypy/test/test_core.py::CoreRequestHandlingTest::testCookies cherrypy/test/test_core.py::CoreRequestHandlingTest::testDefaultContentType cherrypy/test/test_core.py::CoreRequestHandlingTest::testFavicon cherrypy/test/test_core.py::CoreRequestHandlingTest::testFlatten cherrypy/test/test_core.py::CoreRequestHandlingTest::testRanges cherrypy/test/test_core.py::CoreRequestHandlingTest::testRedirect cherrypy/test/test_core.py::CoreRequestHandlingTest::testSlashes cherrypy/test/test_core.py::CoreRequestHandlingTest::testStatus cherrypy/test/test_core.py::CoreRequestHandlingTest::test_InternalRedirect cherrypy/test/test_core.py::CoreRequestHandlingTest::test_cherrypy_url cherrypy/test/test_core.py::CoreRequestHandlingTest::test_expose_decorator cherrypy/test/test_core.py::CoreRequestHandlingTest::test_multiple_headers cherrypy/test/test_core.py::CoreRequestHandlingTest::test_on_end_resource_status cherrypy/test/test_core.py::CoreRequestHandlingTest::test_redirect_with_unicode cherrypy/test/test_core.py::CoreRequestHandlingTest::test_gc cherrypy/test/test_core.py::ErrorTests::test_contextmanager cherrypy/test/test_core.py::ErrorTests::test_start_response_error cherrypy/test/test_core.py::ErrorTests::test_gc cherrypy/test/test_core.py::TestBinding::test_bind_ephemeral_port