diff --git a/djblets/http/requests.py b/djblets/http/requests.py
new file mode 100644
index 0000000000000000000000000000000000000000..65f0b0f51645cfe713f9cf43fd46a9b91f8896a0
--- /dev/null
+++ b/djblets/http/requests.py
@@ -0,0 +1,46 @@
+"""Utilities for working with HTTP requests.
+
+Version Added:
+    5.3
+"""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from django.http import HttpRequest
+
+
+def get_http_request_ip(
+    request: HttpRequest,
+) -> str:
+    """Return the IP address from a client for a given HTTP request.
+
+    This will check the following headers for a suitable IP address:
+
+    * :mailheader:`X-Real-IP`
+    * :mailheader:`X-Forwarded-For`
+
+    If not found, it will fall back to the IP address the request originated
+    from, which may be a public address or an internal address within the
+    service's network.
+
+    Version Added:
+        5.3
+
+    Args:
+        request (django.http.HttpRequest):
+            The HTTP request from the client.
+
+    Returns:
+        str:
+        The IP address for the request.
+    """
+    try:
+        return request.META['HTTP_X_REAL_IP']
+    except KeyError:
+        try:
+            return request.META['HTTP_X_FORWARDED_FOR'].split(',')[0].strip()
+        except KeyError:
+            return request.META['REMOTE_ADDR']
diff --git a/djblets/http/tests/test_requests.py b/djblets/http/tests/test_requests.py
new file mode 100644
index 0000000000000000000000000000000000000000..da63dfa89be110da7bac7b7c18183d1a7249805c
--- /dev/null
+++ b/djblets/http/tests/test_requests.py
@@ -0,0 +1,81 @@
+"""Unit tests for djblets.http.requests.
+
+Version Added:
+    5.3
+"""
+
+from __future__ import annotations
+
+from django.test.client import RequestFactory
+
+from djblets.http.requests import get_http_request_ip
+from djblets.testing.testcases import TestCase
+
+
+class GetHTTPRequestIPTests(TestCase):
+    """Unit tests for get_http_request_ip.
+
+    Version Added:
+        5.3
+    """
+
+    def test_with_http_x_real_ip(self) -> None:
+        """Testing get_http_request_ip with X-Real-IP header"""
+        request = RequestFactory().get('/', headers={
+            'X-Real-IP': '1.2.3.4',
+        })
+
+        self.assertEqual(get_http_request_ip(request), '1.2.3.4')
+
+    def test_with_http_x_real_ip_ipv6(self) -> None:
+        """Testing get_http_request_ip with X-Real-IP header with IPv6"""
+        request = RequestFactory().get('/', headers={
+            'X-Real-IP': '2001:db8:85a3:8d3:1319:8a2e:370:7348',
+        })
+
+        self.assertEqual(get_http_request_ip(request),
+                         '2001:db8:85a3:8d3:1319:8a2e:370:7348')
+
+    def test_with_http_x_forwarded_for(self) -> None:
+        """Testing get_http_request_ip with X-Forwarded-For header"""
+        request = RequestFactory().get('/', headers={
+            'X-Forwarded-For': '1.2.3.4',
+        })
+
+        self.assertEqual(get_http_request_ip(request), '1.2.3.4')
+
+    def test_with_http_x_forwarded_for_multi(self) -> None:
+        """Testing get_http_request_ip with X-Forwarded-For header with
+        multiple IPs
+        """
+        request = RequestFactory().get('/', headers={
+            'X-Forwarded-For': '1.2.3.4, 4.5.5.67',
+        })
+
+        self.assertEqual(get_http_request_ip(request), '1.2.3.4')
+
+    def test_with_http_x_forwarded_for_ipv6(self) -> None:
+        """Testing get_http_request_ip with X-Forwarded-For header with IPv6
+        """
+        request = RequestFactory().get('/', headers={
+            'X-Forwarded-For': '2001:db8:85a3:8d3:1319:8a2e:370:7348, 1.2.3.4',
+        })
+
+        self.assertEqual(get_http_request_ip(request),
+                         '2001:db8:85a3:8d3:1319:8a2e:370:7348')
+
+    def test_with__remote_addr(self) -> None:
+        """Testing get_http_request_ip with REMOTE_ADDR"""
+        request = RequestFactory().get('/',
+                                       REMOTE_ADDR='1.2.3.4')
+
+        self.assertEqual(get_http_request_ip(request), '1.2.3.4')
+
+    def test_with__remote_addr_ipv6(self) -> None:
+        """Testing get_http_request_ip with REMOTE_ADDR with IPv6"""
+        request = RequestFactory().get(
+            '/',
+            REMOTE_ADDR='2001:db8:85a3:8d3:1319:8a2e:370:7348')
+
+        self.assertEqual(get_http_request_ip(request),
+                         '2001:db8:85a3:8d3:1319:8a2e:370:7348')
diff --git a/docs/djblets/coderef/index.rst b/docs/djblets/coderef/index.rst
index 0d8d49a57416c58198b0ce9a55d549cbc37ac088..88e33499753dde6faf6f4928ac02cd0869680f9b 100644
--- a/docs/djblets/coderef/index.rst
+++ b/docs/djblets/coderef/index.rst
@@ -248,6 +248,7 @@ HTTP Utilities
    :toctree: python
 
    djblets.http.middleware
+   djblets.http.requests
    djblets.http.responses
 
 
