diff --git a/twilio/request_validator.py b/twilio/request_validator.py
index 72cdb6298..c24a52acd 100644
--- a/twilio/request_validator.py
+++ b/twilio/request_validator.py
@@ -21,19 +21,42 @@ def compare(string1, string2):
     result = True
     for c1, c2 in izip(string1, string2):
         result &= c1 == c2
+
     return result
 
 
 def remove_port(uri):
     """Remove the port number from a URI
 
-    :param uri: full URI that Twilio requested on your server
+    :param uri: parsed URI that Twilio requested on your server
 
     :returns: full URI without a port number
     :rtype: str
     """
+    if not uri.port:
+        return uri.geturl()
+
     new_netloc = uri.netloc.split(':')[0]
     new_uri = uri._replace(netloc=new_netloc)
+
+    return new_uri.geturl()
+
+
+def add_port(uri):
+    """Add the port number to a URI
+
+    :param uri: parsed URI that Twilio requested on your server
+
+    :returns: full URI with a port number
+    :rtype: str
+    """
+    if uri.port:
+        return uri.geturl()
+    
+    port = 443 if uri.scheme == "https" else 80
+    new_netloc = uri.netloc + ":" + str(port)
+    new_uri = uri._replace(netloc=new_netloc)
+
     return new_uri.geturl()
 
 
@@ -82,17 +105,21 @@ class RequestValidator(object):
             params = {}
 
         parsed_uri = urlparse(uri)
-        if parsed_uri.scheme == "https" and parsed_uri.port:
-            uri = remove_port(parsed_uri)
+        uri_with_port = add_port(parsed_uri)
+        uri_without_port = remove_port(parsed_uri)
 
         valid_signature = False  # Default fail
+        valid_signature_with_port = False
         valid_body_hash = True  # May not receive body hash, so default succeed
 
         query = parse_qs(parsed_uri.query)
         if "bodySHA256" in query and isinstance(params, string_types):
             valid_body_hash = compare(self.compute_hash(params), query["bodySHA256"][0])
-            valid_signature = compare(self.compute_signature(uri, {}), signature)
-        else:
-            valid_signature = compare(self.compute_signature(uri, params), signature)
+            params = {}
+
+        #  check signature of uri with and without port, 
+        #  since sig generation on back end is inconsistent
+        valid_signature = compare(self.compute_signature(uri_without_port, params), signature)
+        valid_signature_with_port = compare(self.compute_signature(uri_with_port, params), signature)
 
-        return valid_signature and valid_body_hash
+        return valid_body_hash and (valid_signature or valid_signature_with_port)

Incoming SMS webhook failing validation (non standard SSL port)
Hi,

I have an incoming SMS webhook using HTTPS but on a non standard port (4433). The incoming POST fails validation unless the changes made in #392 are reverted (i.e. the port number left in for validation). The [security notes](https://www.twilio.com/docs/api/security#notes) don't mention non standard ports. Should only the standard port be removed if using HTTPS?

https://github.com/twilio/twilio-python/blob/119fe50863b2e9df89a56711e8d3410288709593/twilio/request_validator.py#L77
diff --git a/tests/unit/test_request_validator.py b/tests/unit/test_request_validator.py
index c7f67d818..245c5ae43 100644
--- a/tests/unit/test_request_validator.py
+++ b/tests/unit/test_request_validator.py
@@ -53,6 +53,20 @@ class ValidationTest(unittest.TestCase):
         uri = self.uri.replace(".com", ".com:1234")
         assert_true(self.validator.validate(uri, self.params, self.expected))
 
+    def test_validation_removes_port_on_http(self):
+        expected = "Zmvh+3yNM1Phv2jhDCwEM3q5ebU="  # hash of http uri with port 1234
+        uri = self.uri.replace(".com", ".com:1234").replace("https", "http")
+        assert_true(self.validator.validate(uri, self.params, expected))
+
+    def test_validation_adds_port_on_https(self):
+        expected = "kvajT1Ptam85bY51eRf/AJRuM3w="  # hash of uri with port 443
+        assert_true(self.validator.validate(self.uri, self.params, expected))
+
+    def test_validation_adds_port_on_http(self):
+        uri = self.uri.replace("https", "http")
+        expected = "0ZXoZLH/DfblKGATFgpif+LLRf4="  # hash of uri with port 80
+        assert_true(self.validator.validate(uri, self.params, expected))
+
     def test_validation_of_body_succeeds(self):
         uri = self.uriWithBody
         is_valid = self.validator.validate(uri, self.body, "a9nBmqA0ju/hNViExpshrM61xv4=")

pip install -e .[dev]
parse_log_pytest
requirements.txt
nose pytest
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
bleach==4.1.0
certifi==2021.5.30
cffi==1.15.1
charset-normalizer==2.0.12
colorama==0.4.5
cryptography==40.0.2
docutils==0.18.1
flake8==5.0.4
idna==3.10
importlib-metadata==4.2.0
importlib-resources==5.4.0
iniconfig==1.1.1
jeepney==0.7.1
keyring==23.4.1
mccabe==0.7.0
mock==5.2.0
nose==1.3.7
packaging==21.3
pkginfo==1.10.0
pluggy==1.0.0
py==1.11.0
pycodestyle==2.9.1
pycparser==2.21
pyflakes==2.5.0
Pygments==2.14.0
PyJWT==2.4.0
pyparsing==3.1.4
PySocks==1.7.1
pytest==7.0.1
pytz==2025.2
readme-renderer==34.0
requests==2.27.1
requests-toolbelt==1.0.0
rfc3986==1.5.0
SecretStorage==3.3.3
six==1.17.0
SocksiPy-branch==1.1
tomli==1.2.3
tqdm==4.64.1
-e git+https://github.com/twilio/twilio-python.git@77b44a5a18395bf09ae114d24ac9c4b3efb67e78#egg=twilio
twine==3.8.0
typing_extensions==4.1.1
urllib3==1.26.20
webencodings==0.5.1
zipp==3.6.0

name: twilio-python
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - bleach==4.1.0
      - cffi==1.15.1
      - charset-normalizer==2.0.12
      - colorama==0.4.5
      - cryptography==40.0.2
      - docutils==0.18.1
      - flake8==5.0.4
      - idna==3.10
      - importlib-metadata==4.2.0
      - importlib-resources==5.4.0
      - iniconfig==1.1.1
      - jeepney==0.7.1
      - keyring==23.4.1
      - mccabe==0.7.0
      - mock==5.2.0
      - nose==1.3.7
      - packaging==21.3
      - pkginfo==1.10.0
      - pluggy==1.0.0
      - py==1.11.0
      - pycodestyle==2.9.1
      - pycparser==2.21
      - pyflakes==2.5.0
      - pygments==2.14.0
      - pyjwt==2.4.0
      - pyparsing==3.1.4
      - pysocks==1.7.1
      - pytest==7.0.1
      - pytz==2025.2
      - readme-renderer==34.0
      - requests==2.27.1
      - requests-toolbelt==1.0.0
      - rfc3986==1.5.0
      - secretstorage==3.3.3
      - six==1.17.0
      - socksipy-branch==1.1
      - tomli==1.2.3
      - tqdm==4.64.1
      - twine==3.8.0
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - webencodings==0.5.1
      - zipp==3.6.0
prefix: /opt/conda/envs/twilio-python

tests/unit/test_request_validator.py::ValidationTest::test_validation_adds_port_on_http tests/unit/test_request_validator.py::ValidationTest::test_validation_adds_port_on_https
tests/unit/test_request_validator.py::ValidationTest::test_compute_hash_unicode tests/unit/test_request_validator.py::ValidationTest::test_compute_signature tests/unit/test_request_validator.py::ValidationTest::test_compute_signature_bytecode tests/unit/test_request_validator.py::ValidationTest::test_validation tests/unit/test_request_validator.py::ValidationTest::test_validation_of_body_succeeds tests/unit/test_request_validator.py::ValidationTest::test_validation_removes_port_on_http tests/unit/test_request_validator.py::ValidationTest::test_validation_removes_port_on_https
twilio/request_validator.py
twilio/request_validator.py