diff --git a/src/twisted/newsfragments/12096.feature b/src/twisted/newsfragments/12096.feature
new file mode 100644
index 0000000000..a07999d034
--- /dev/null
+++ b/src/twisted/newsfragments/12096.feature
@@ -0,0 +1,1 @@
+twisted.web.wsgi request environment now contains the peer port number as `REMOTE_PORT`.
\ No newline at end of file
diff --git a/src/twisted/web/wsgi.py b/src/twisted/web/wsgi.py
index 43227f40e3..29d1de8f2e 100644
--- a/src/twisted/web/wsgi.py
+++ b/src/twisted/web/wsgi.py
@@ -287,9 +287,11 @@ class _WSGIResponse:
 
         # All keys and values need to be native strings, i.e. of type str in
         # *both* Python 2 and Python 3, so says PEP-3333.
+        remotePeer = request.getClientAddress()
         self.environ = {
             "REQUEST_METHOD": _wsgiString(request.method),
-            "REMOTE_ADDR": _wsgiString(request.getClientAddress().host),
+            "REMOTE_ADDR": _wsgiString(remotePeer.host),
+            "REMOTE_PORT": _wsgiString(str(remotePeer.port)),
             "SCRIPT_NAME": _wsgiString(scriptName),
             "PATH_INFO": _wsgiString(pathInfo),
             "QUERY_STRING": _wsgiString(queryString),
@@ -535,6 +537,9 @@ class WSGIResource:
     An L{IResource} implementation which delegates responsibility for all
     resources hierarchically inferior to it to a WSGI application.
 
+    The C{environ} argument passed to the application, includes the
+    C{REMOTE_PORT} key to complement the C{REMOTE_ADDR} key.
+
     @ivar _reactor: An L{IReactorThreads} provider which will be passed on to
         L{_WSGIResponse} to schedule calls in the I/O thread.
 

diff --git a/src/twisted/web/test/test_wsgi.py b/src/twisted/web/test/test_wsgi.py
index b6be4f0f4b..ccb736c6bf 100644
--- a/src/twisted/web/test/test_wsgi.py
+++ b/src/twisted/web/test/test_wsgi.py
@@ -715,7 +715,11 @@ class EnvironTests(WSGITestsMixin, TestCase):
         The C{'REMOTE_ADDR'} key of the C{environ} C{dict} passed to the
         application contains the address of the client making the request.
         """
-        d = self.render("GET", "1.1", [], [""])
+
+        def channelFactory():
+            return DummyChannel(peer=IPv4Address("TCP", "192.168.1.1", 12344))
+
+        d = self.render("GET", "1.1", [], [""], channelFactory=channelFactory)
         d.addCallback(self.environKeyEqual("REMOTE_ADDR", "192.168.1.1"))
 
         return d
@@ -735,6 +739,20 @@ class EnvironTests(WSGITestsMixin, TestCase):
 
         return d
 
+    def test_remotePort(self):
+        """
+        The C{'REMOTE_PORT'} key of the C{environ} C{dict} passed to the
+        application contains the port of the client making the request.
+        """
+
+        def channelFactory():
+            return DummyChannel(peer=IPv4Address("TCP", "192.168.1.1", 12344))
+
+        d = self.render("GET", "1.1", [], [""], channelFactory=channelFactory)
+        d.addCallback(self.environKeyEqual("REMOTE_PORT", "12344"))
+
+        return d
+
     def test_headers(self):
         """
         HTTP request headers are copied into the C{environ} C{dict} passed to

WSGI applications run inside Twisted Web don’t get access to the client port
**Is your feature request related to a problem? Please describe.**
The problem we’re trying to solve is that WSGI applications run inside Twisted Web don’t get access to the client port (though the client address is made available through REMOTE_ADDR). We have an application in which the client’s port is necessary. 

When looking at the [`_WSGIResponse` class](https://github.com/twisted/twisted/blob/11c9f5808da4383678c9ff830d74137efd32d22d/src/twisted/web/wsgi.py#L292), we notice that only `REMOTE_ADDR` is set:
```
        # All keys and values need to be native strings, i.e. of type str in
        # *both* Python 2 and Python 3, so says PEP-3333.
        self.environ = {
            "REQUEST_METHOD": _wsgiString(request.method),
            "REMOTE_ADDR": _wsgiString(request.getClientAddress().host), <------
            "SCRIPT_NAME": _wsgiString(scriptName),
            "PATH_INFO": _wsgiString(pathInfo),
            "QUERY_STRING": _wsgiString(queryString),
            "CONTENT_TYPE": _wsgiString(request.getHeader(b"content-type") or ""),
            "CONTENT_LENGTH": _wsgiString(request.getHeader(b"content-length") or ""),
            "SERVER_NAME": _wsgiString(request.getRequestHostname()),
            "SERVER_PORT": _wsgiString(str(request.getHost().port)),
            "SERVER_PROTOCOL": _wsgiString(request.clientproto),
        }
```

**Describe the solution you'd like**
To provide support for retrieving the remote port as done with the remote IP address. This could be done by updating `environ` to include a new "REMOTE_PORT" attribute as following:
```
        self.environ = {
            "REQUEST_METHOD": _wsgiString(request.method),
            "REMOTE_ADDR": _wsgiString(request.getClientAddress().host), <------
            "REMOTE_PORT": _wsgiString(str(request.getClientAddress().port)),
            "SCRIPT_NAME": _wsgiString(scriptName),
```

**Describe alternatives you've considered**
Besides implementing a monkey patch, another alternative is to try and retrieve the port value from the WSGI context, which might have undesired side affects.

**Additional context**
src/twisted/web/test/test_wsgi.py::EnvironTests::test_remotePort
src/twisted/web/test/test_wsgi.py::WSGIResourceTests::test_applicationAndRequestThrow src/twisted/web/test/test_wsgi.py::WSGIResourceTests::test_interfaces src/twisted/web/test/test_wsgi.py::WSGIResourceTests::test_unsupported src/twisted/web/test/test_wsgi.py::EnvironTests::test_contentLength src/twisted/web/test/test_wsgi.py::EnvironTests::test_contentLengthIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_contentType src/twisted/web/test/test_wsgi.py::EnvironTests::test_contentTypeIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_environIsDict src/twisted/web/test/test_wsgi.py::EnvironTests::test_headers src/twisted/web/test/test_wsgi.py::EnvironTests::test_pathInfo src/twisted/web/test/test_wsgi.py::EnvironTests::test_pathInfoIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_queryString src/twisted/web/test/test_wsgi.py::EnvironTests::test_queryStringIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_remoteAddr src/twisted/web/test/test_wsgi.py::EnvironTests::test_remoteAddrIPv6 src/twisted/web/test/test_wsgi.py::EnvironTests::test_requestMethod src/twisted/web/test/test_wsgi.py::EnvironTests::test_requestMethodIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_scriptName src/twisted/web/test/test_wsgi.py::EnvironTests::test_scriptNameIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_serverName src/twisted/web/test/test_wsgi.py::EnvironTests::test_serverNameIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_serverPort src/twisted/web/test/test_wsgi.py::EnvironTests::test_serverPortIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_serverProtocol src/twisted/web/test/test_wsgi.py::EnvironTests::test_serverProtocolIsNativeString src/twisted/web/test/test_wsgi.py::EnvironTests::test_wsgiErrors src/twisted/web/test/test_wsgi.py::EnvironTests::test_wsgiErrorsAcceptsOnlyNativeStringsInPython3 src/twisted/web/test/test_wsgi.py::EnvironTests::test_wsgiMultiprocess src/twisted/web/test/test_wsgi.py::EnvironTests::test_wsgiMultithread src/twisted/web/test/test_wsgi.py::EnvironTests::test_wsgiRunOnce src/twisted/web/test/test_wsgi.py::EnvironTests::test_wsgiURLScheme src/twisted/web/test/test_wsgi.py::EnvironTests::test_wsgiVersion src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_iterable src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_iterableAfterRead src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readAll src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readMoreThan src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readNegative src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readNone src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readSome src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readTwice src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readline src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlineMoreThan src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlineNegative src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlineNone src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlineSome src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlineTwice src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlines src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlinesAfterRead src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlinesMoreThan src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlinesNegative src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlinesNone src/twisted/web/test/test_wsgi.py::InputStreamBytesIOTests::test_readlinesSome src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_iterable src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_iterableAfterRead src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readAll src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readMoreThan src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readNegative src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readNone src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readSome src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readTwice src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readline src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlineMoreThan src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlineNegative src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlineNone src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlineSome src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlineTwice src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlines src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlinesAfterRead src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlinesMoreThan src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlinesNegative src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlinesNone src/twisted/web/test/test_wsgi.py::InputStreamTemporaryFileTests::test_readlinesSome src/twisted/web/test/test_wsgi.py::StartResponseTests::test_applicationProvidedContentType src/twisted/web/test/test_wsgi.py::StartResponseTests::test_applicationProvidedServerAndDate src/twisted/web/test/test_wsgi.py::StartResponseTests::test_content src/twisted/web/test/test_wsgi.py::StartResponseTests::test_delayedUntilContent src/twisted/web/test/test_wsgi.py::StartResponseTests::test_delayedUntilReturn src/twisted/web/test/test_wsgi.py::StartResponseTests::test_headerKeyMustBeNativeString src/twisted/web/test/test_wsgi.py::StartResponseTests::test_headerValueMustBeNativeString src/twisted/web/test/test_wsgi.py::StartResponseTests::test_headers src/twisted/web/test/test_wsgi.py::StartResponseTests::test_headersMustBeSequence src/twisted/web/test/test_wsgi.py::StartResponseTests::test_headersMustEachBeSequence src/twisted/web/test/test_wsgi.py::StartResponseTests::test_headersShouldBePlainList src/twisted/web/test/test_wsgi.py::StartResponseTests::test_headersShouldEachBeTuple src/twisted/web/test/test_wsgi.py::StartResponseTests::test_headersShouldEachHaveKeyAndValue src/twisted/web/test/test_wsgi.py::StartResponseTests::test_multipleStartResponse src/twisted/web/test/test_wsgi.py::StartResponseTests::test_startResponseWithException src/twisted/web/test/test_wsgi.py::StartResponseTests::test_startResponseWithExceptionTooLate src/twisted/web/test/test_wsgi.py::StartResponseTests::test_status src/twisted/web/test/test_wsgi.py::StartResponseTests::test_statusMustBeNativeString src/twisted/web/test/test_wsgi.py::StartResponseTests::test_write src/twisted/web/test/test_wsgi.py::StartResponseTests::test_writeAcceptsOnlyByteStrings src/twisted/web/test/test_wsgi.py::ApplicationTests::test_applicationCalledInThread src/twisted/web/test/test_wsgi.py::ApplicationTests::test_applicationCloseException src/twisted/web/test/test_wsgi.py::ApplicationTests::test_applicationExceptionAfterStartResponse src/twisted/web/test/test_wsgi.py::ApplicationTests::test_applicationExceptionAfterWrite src/twisted/web/test/test_wsgi.py::ApplicationTests::test_applicationExceptionBeforeStartResponse src/twisted/web/test/test_wsgi.py::ApplicationTests::test_close src/twisted/web/test/test_wsgi.py::ApplicationTests::test_connectionClosedDuringIteration src/twisted/web/test/test_wsgi.py::ApplicationTests::test_iteratedValuesWrittenFromThread src/twisted/web/test/test_wsgi.py::ApplicationTests::test_statusWrittenFromThread src/twisted/web/test/test_wsgi.py::ApplicationTests::test_writeCalledFromThread