diff --git a/reviewboard/notifications/tests.py b/reviewboard/notifications/tests.py
index 8c464ddacae24299a0fbd6e61038acd112f964b3..0dfc317864d5366de92f811fb837e54a465b9ee3 100644
--- a/reviewboard/notifications/tests.py
+++ b/reviewboard/notifications/tests.py
@@ -6,6 +6,7 @@ from django.conf import settings
 from django.contrib.auth.models import User
 from django.core import mail
 from django.template import TemplateSyntaxError
+from django.utils import six
 from django.utils.datastructures import MultiValueDict
 from django.utils.six.moves.urllib.request import urlopen
 from djblets.mail.testing import DmarcDnsTestsMixin
@@ -1601,6 +1602,24 @@ class WebHookDispatchTests(SpyAgency, TestCase):
              '  "item3": true\n'
              '}'))
 
+    def test_dispatch_non_ascii_custom_payload(self):
+        """Testing dispatch_webhook_event with non-ASCII custom payload"""
+        non_ascii_content = '{"sign": "{{sign|escapejs}}"}'
+
+        handler = WebHookTarget(events='my-event',
+                                url=self.ENDPOINT_URL,
+                                encoding=WebHookTarget.ENCODING_JSON,
+                                use_custom_content=True,
+                                custom_content=non_ascii_content)
+
+        self._test_dispatch(
+            handler,
+            'my-event',
+            {'sign': '\u00A4'},
+            'application/json',
+            '{"sign": "\u00A4"}'.encode('utf-8')
+        )
+
     def test_dispatch_form_data(self):
         """Test dispatch_webhook_event with Form Data payload"""
         handler = WebHookTarget(events='my-event',
@@ -1616,6 +1635,21 @@ class WebHookDispatchTests(SpyAgency, TestCase):
             'application/x-www-form-urlencoded',
             'payload=%7B%22items%22%3A+%5B1%2C+2%2C+3%5D%7D')
 
+    def test_dispatch_non_ascii_form_data(self):
+        """Testing dispatch_webhook_event with non-ASCII Form Data payload"""
+        handler = WebHookTarget(events='my-event',
+                                url=self.ENDPOINT_URL,
+                                encoding=WebHookTarget.ENCODING_FORM_DATA)
+
+        self._test_dispatch(
+            handler,
+            'my-event',
+            {
+                'sign': '\u00A4',
+            },
+            'application/x-www-form-urlencoded',
+            'payload=%7B%22sign%22%3A+%22%5Cu00a4%22%7D')
+
     def test_dispatch_json(self):
         """Test dispatch_webhook_event with JSON payload"""
         handler = WebHookTarget(events='my-event',
@@ -1631,6 +1665,21 @@ class WebHookDispatchTests(SpyAgency, TestCase):
             'application/json',
             '{"items": [1, 2, 3]}')
 
+    def test_dispatch_non_ascii_json(self):
+        """Testing dispatch_webhook_event with non-ASCII JSON payload"""
+        handler = WebHookTarget(events='my-event',
+                                url=self.ENDPOINT_URL,
+                                encoding=WebHookTarget.ENCODING_JSON)
+
+        self._test_dispatch(
+            handler,
+            'my-event',
+            {
+                'sign': '\u00A4',
+            },
+            'application/json',
+            '{"sign": "\\u00a4"}')
+
     def test_dispatch_xml(self):
         """Test dispatch_webhook_event with XML payload"""
         handler = WebHookTarget(events='my-event',
@@ -1655,6 +1704,24 @@ class WebHookDispatchTests(SpyAgency, TestCase):
              ' </items>\n'
              '</rsp>'))
 
+    def test_dispatch_non_ascii_xml(self):
+        """Testing dispatch_webhook_event with non-ASCII XML payload"""
+        handler = WebHookTarget(events='my-event',
+                                url=self.ENDPOINT_URL,
+                                encoding=WebHookTarget.ENCODING_XML)
+
+        self._test_dispatch(
+            handler,
+            'my-event',
+            {
+                'sign': '\u00A4',
+            },
+            'application/xml',
+            ('<?xml version="1.0" encoding="utf-8"?>\n'
+             '<rsp>\n'
+             ' <sign>\u00A4</sign>\n'
+             '</rsp>').encode('utf-8'))
+
     def test_dispatch_with_secret(self):
         """Test dispatch_webhook_event with HMAC secret"""
         handler = WebHookTarget(events='my-event',
@@ -1749,11 +1816,29 @@ class WebHookDispatchTests(SpyAgency, TestCase):
             else:
                 self.assertNotIn('X-hub-signature', request.headers)
 
+            # Check that all sent data are binary strings.
+            self.assertIsInstance(request.get_full_url(), six.binary_type)
+
+            for h in request.headers:
+                self.assertIsInstance(h, six.binary_type)
+                self.assertNotIsInstance(request.headers[h], six.text_type)
+
+            self.assertIsInstance(request.data, six.binary_type)
+
         self.spy_on(urlopen, call_fake=_urlopen)
 
+        # We need to ensure that logging.exception is not called
+        # in order to avoid silent swallowing of test assertion failures
+        self.spy_on(logging.exception)
+
         request = FakeHTTPRequest(None)
         dispatch_webhook_event(request, [handler], event, payload)
 
+        # Assuming that if logging.exception is called, an assertion
+        # error was raised - and should thus be raised further.
+        if logging.exception.spy.called:
+            raise logging.exception.spy.calls[0].args[2]
+
 
 class WebHookTargetManagerTests(TestCase):
     """Unit tests for WebHookTargetManager."""
diff --git a/reviewboard/notifications/webhooks.py b/reviewboard/notifications/webhooks.py
index fc044a31b6cd23fefbe8ad8658fbe84784b04da7..d18e5ad35df0e2d232b0cf7d0dedc6517047cebd 100644
--- a/reviewboard/notifications/webhooks.py
+++ b/reviewboard/notifications/webhooks.py
@@ -113,6 +113,8 @@ def dispatch_webhook_event(request, webhook_targets, event, payload):
             try:
                 body = render_custom_content(webhook_target.custom_content,
                                              payload)
+
+                body = body.encode('utf-8')
             except Exception as e:
                 logging.exception('Could not render WebHook payload: %s', e)
                 continue
@@ -125,6 +127,7 @@ def dispatch_webhook_event(request, webhook_targets, event, payload):
                     if encoding == webhook_target.ENCODING_JSON:
                         adapter = JSONEncoderAdapter(encoder)
                         body = adapter.encode(payload, request=request)
+                        body = body.encode('utf-8')
                     elif encoding == webhook_target.ENCODING_XML:
                         adapter = XMLEncoderAdapter(encoder)
                         body = adapter.encode(payload, request=request)
@@ -134,6 +137,7 @@ def dispatch_webhook_event(request, webhook_targets, event, payload):
                             'payload': adapter.encode(payload,
                                                       request=request),
                         })
+                        body = body.encode('utf-8')
                     else:
                         logging.error('Unexpected WebHookTarget encoding "%s" '
                                       'for ID %s',
@@ -144,27 +148,30 @@ def dispatch_webhook_event(request, webhook_targets, event, payload):
                                       e)
                     continue
 
-                body = body.encode('utf-8')
                 bodies[encoding] = body
             else:
                 body = bodies[encoding]
 
         headers = {
-            'X-ReviewBoard-Event': event,
-            'Content-Type': webhook_target.encoding,
-            'Content-Length': len(body),
-            'User-Agent': 'ReviewBoard-WebHook/%s' % get_package_version(),
+            b'X-ReviewBoard-Event': event.encode('utf-8'),
+            b'Content-Type': webhook_target.encoding.encode('utf-8'),
+            b'Content-Length': len(body),
+            b'User-Agent':
+                ('ReviewBoard-WebHook/%s' % get_package_version())
+                .encode('utf-8'),
         }
 
         if webhook_target.secret:
             signer = hmac.new(webhook_target.secret.encode('utf-8'), body,
                               hashlib.sha1)
-            headers['X-Hub-Signature'] = 'sha1=%s' % signer.hexdigest()
+            headers[b'X-Hub-Signature'] = \
+                ('sha1=%s' % signer.hexdigest()).encode('utf-8')
 
         logging.info('Dispatching webhook for event %s to %s',
                      event, webhook_target.url)
         try:
-            urlopen(Request(webhook_target.url, body, headers))
+            url = webhook_target.url.encode('utf-8')
+            urlopen(Request(url, body, headers))
         except Exception as e:
             logging.exception('Could not dispatch WebHook to %s: %s',
                               webhook_target.url, e)
