diff --git a/hypha/apply/activity/templates/activity/include/listing_base.html b/hypha/apply/activity/templates/activity/include/listing_base.html
index 2336b81eb69304326aa066c8f728db74aec8c805..af43a912d0212e159ebffcd6c9d461f3e7abf788 100644
--- a/hypha/apply/activity/templates/activity/include/listing_base.html
+++ b/hypha/apply/activity/templates/activity/include/listing_base.html
@@ -1,4 +1,4 @@
-{% load i18n activity_tags bleach_tags markdown_tags submission_tags apply_tags heroicons %}
+{% load i18n activity_tags nh3_tags markdown_tags submission_tags apply_tags heroicons %}
 
 <div class="feed__item feed__item--{{ activity.type }} border shadow-sm rounded-sm pb-2 " id="communications#{{ activity.id }}">
     <div class="feed__pre-content hidden lg:block">
@@ -48,7 +48,7 @@
                      data-visibility="{{activity.visibility}}"
                      data-edit-url="{% url 'api:v1:comments-edit' pk=activity.pk %}"
                 >
-                    {{ activity|display_for:request.user|submission_links|markdown|bleach }}
+                    {{ activity|display_for:request.user|submission_links|markdown|nh3 }}
                 </div>
                 <style>
                     @media only screen and (min-width: 1024px){
@@ -60,7 +60,7 @@
                 <div class="js-edit-block pe-3" aria-live="polite"></div>
             {% else %}
                 <div class="px-3 prose">
-                    {{ activity|display_for:request.user|submission_links|markdown|bleach }}
+                    {{ activity|display_for:request.user|submission_links|markdown|nh3 }}
                 </div>
             {% endif %}
 
diff --git a/hypha/apply/activity/templates/activity/include/notifications_dropdown.html b/hypha/apply/activity/templates/activity/include/notifications_dropdown.html
index 65d7830fbac72b004565e03d1ad0bf8d40cc7b3c..0866fcae53ba8bbf9217d138922d0fdbb1950636 100644
--- a/hypha/apply/activity/templates/activity/include/notifications_dropdown.html
+++ b/hypha/apply/activity/templates/activity/include/notifications_dropdown.html
@@ -1,4 +1,4 @@
-{% load i18n activity_tags bleach_tags markdown_tags submission_tags apply_tags %}
+{% load i18n activity_tags nh3_tags markdown_tags submission_tags apply_tags %}
 
 <div class="notifications notifications--dropdown">
     <div class="notifications__content zeta" role="activity">
diff --git a/hypha/apply/activity/templates/activity/notifications.html b/hypha/apply/activity/templates/activity/notifications.html
index 647b09ca3f3ccc91220da782b478133b0d37b8cd..130b90d030cd9bf6b10e7a76b32d3c71f00d63fd 100644
--- a/hypha/apply/activity/templates/activity/notifications.html
+++ b/hypha/apply/activity/templates/activity/notifications.html
@@ -1,6 +1,5 @@
 {% extends "base-apply.html" %}
-{% load i18n static activity_tags apply_tags bleach_tags markdown_tags submission_tags heroicons %}
-
+{% load i18n static activity_tags apply_tags nh3_tags markdown_tags submission_tags heroicons %}
 {% block content %}
     <div class="admin-bar">
         <div class="admin-bar__inner">
diff --git a/hypha/apply/activity/templates/messages/email/determination.html b/hypha/apply/activity/templates/messages/email/determination.html
index 99f53547de21d00620e0a0f17d9a68ca8fa82808..325d559b140eba377d7b33d41ef745cccecf9f95 100644
--- a/hypha/apply/activity/templates/messages/email/determination.html
+++ b/hypha/apply/activity/templates/messages/email/determination.html
@@ -1,8 +1,8 @@
 {% extends "messages/email/applicant_base.html" %}
-{% load bleach_tags i18n %}
+{% load nh3_tags i18n %}
 
 {% block content %}{% trans "Your application has been reviewed and the outcome is" %}: {{ determination.clean_outcome }}
 
-    {{ determination.message|bleach|striptags }}
+    {{ determination.message|nh3|striptags }}
 
     {% trans "Read the full determination here" %}: {{ request.scheme }}://{{ request.get_host }}{{ determination.get_absolute_url }}{% endblock %}
diff --git a/hypha/apply/api/v1/serializers.py b/hypha/apply/api/v1/serializers.py
index 01288cc79fa8b5cecbf5d42f0f4a3ef00e964982..d80715f24634bbb2488ec8853292f5c568240d86 100644
--- a/hypha/apply/api/v1/serializers.py
+++ b/hypha/apply/api/v1/serializers.py
@@ -1,5 +1,5 @@
 from django.contrib.auth import get_user_model
-from django_bleach.templatetags.bleach_tags import bleach_value
+from django_nh3.templatetags.nh3_tags import nh3_value
 from rest_framework import serializers
 
 from hypha.apply.activity.models import Activity
@@ -416,7 +416,7 @@ class CommentSerializer(serializers.ModelSerializer):
         )
 
     def get_message(self, obj):
-        return bleach_value(markdown_to_html(obj.message))
+        return nh3_value(markdown_to_html(obj.message))
 
     def get_editable(self, obj):
         return self.context["request"].user == obj.user
diff --git a/hypha/apply/dashboard/templates/dashboard/applicant_dashboard.html b/hypha/apply/dashboard/templates/dashboard/applicant_dashboard.html
index 6d53d17f8e0a63b77db1f20fe27ae4bc04f9ba82..992c709d4aa131ef2608595492ca4d1f6e67e3b9 100644
--- a/hypha/apply/dashboard/templates/dashboard/applicant_dashboard.html
+++ b/hypha/apply/dashboard/templates/dashboard/applicant_dashboard.html
@@ -1,6 +1,6 @@
 {% extends "base-apply.html" %}
 {% load render_table from django_tables2 %}
-{% load i18n static wagtailcore_tags workflow_tags statusbar_tags heroicons dashboard_statusbar_tags apply_tags invoice_tools markdown_tags bleach_tags %}
+{% load i18n static wagtailcore_tags workflow_tags statusbar_tags heroicons dashboard_statusbar_tags apply_tags invoice_tools markdown_tags nh3_tags %}
 {% block body_class %}bg-light-grey{% endblock %}
 
 {% block title %}{% trans "Dashboard" %}{% endblock %}
@@ -31,7 +31,7 @@
                 {% for task in my_tasks.data %}
                     <div class="bg-white p-1 flex mb-1 items-center">
                         <svg class="icon icon--dashboard-tasks"><use xlink:href="#{{ task.icon }}"></use></svg>
-                        <div class="flex-1">{{ task.text|markdown|bleach }}</div>
+                        <div class="flex-1">{{ task.text|markdown|nh3 }}</div>
                         <a class="button button-primary m-2" href="{{ task.url }}">View</a>
                     </div>
                 {% endfor %}
diff --git a/hypha/apply/dashboard/templates/dashboard/contracting_dashboard.html b/hypha/apply/dashboard/templates/dashboard/contracting_dashboard.html
index 3048ee17059d9a7d4b9ca7c26bf513475c610ed1..2054d55a80e8f99dc2aaf6ce2175210baea26a1c 100644
--- a/hypha/apply/dashboard/templates/dashboard/contracting_dashboard.html
+++ b/hypha/apply/dashboard/templates/dashboard/contracting_dashboard.html
@@ -1,6 +1,6 @@
 {% extends "base-apply.html" %}
 {% load render_table from django_tables2 %}
-{% load i18n static markdown_tags bleach_tags %}
+{% load i18n static markdown_tags nh3_tags %}
 
 {% block title %}{% trans "Dashboard" %}{% endblock %}
 
@@ -21,7 +21,7 @@
                 {% for task in my_tasks.data %}
                     <div class="bg-white p-1 flex mb-1 items-center">
                         <svg class="icon icon--dashboard-tasks"><use xlink:href="#{{ task.icon }}"></use></svg>
-                        <div class="flex-1">{{ task.text|markdown|bleach }}</div>
+                        <div class="flex-1">{{ task.text|markdown|nh3 }}</div>
                         <a class="button button-primary m-2" href="{{ task.url }}">View</a>
                     </div>
                 {% endfor %}
diff --git a/hypha/apply/dashboard/templates/dashboard/dashboard.html b/hypha/apply/dashboard/templates/dashboard/dashboard.html
index 6252c72f68ec129baf4e18e2416a10716dc72c20..41667459240e08c359bc9263bb73b683d73639e9 100644
--- a/hypha/apply/dashboard/templates/dashboard/dashboard.html
+++ b/hypha/apply/dashboard/templates/dashboard/dashboard.html
@@ -1,6 +1,6 @@
 {% extends "base-apply.html" %}
 {% load render_table from django_tables2 %}
-{% load i18n static bleach_tags markdown_tags %}
+{% load i18n static nh3_tags markdown_tags %}
 
 {% block extra_css %}
     {{ my_reviewed.filterset.form.media.css }}
@@ -30,7 +30,7 @@
                     {% for task in my_tasks.data %}
                         <div class="bg-white p-1 flex mb-1 items-center">
                             <svg class="icon icon--dashboard-tasks"><use xlink:href="#{{ task.icon }}"></use></svg>
-                            <div class="flex-1">{{ task.text|markdown|bleach }}</div>
+                            <div class="flex-1">{{ task.text|markdown|nh3 }}</div>
                             <a class="button button-primary m-2" href="{{ task.url }}">View</a>
                         </div>
                     {% endfor %}
diff --git a/hypha/apply/dashboard/templates/dashboard/finance_dashboard.html b/hypha/apply/dashboard/templates/dashboard/finance_dashboard.html
index 34f6b69628650062f007eb116c9c6fa1c9109a37..6320843ea76d50ecca0db23a4636d96b72bd3d66 100644
--- a/hypha/apply/dashboard/templates/dashboard/finance_dashboard.html
+++ b/hypha/apply/dashboard/templates/dashboard/finance_dashboard.html
@@ -1,6 +1,6 @@
 {% extends "base-apply.html" %}
 {% load render_table from django_tables2 %}
-{% load i18n static markdown_tags bleach_tags %}
+{% load i18n static markdown_tags nh3_tags %}
 
 {% block title %}{% trans "Dashboard" %}{% endblock %}
 
@@ -21,7 +21,7 @@
                 {% for task in my_tasks.data %}
                     <div class="bg-white p-1 flex mb-1 items-center">
                         <svg class="icon icon--dashboard-tasks"><use xlink:href="#{{ task.icon }}"></use></svg>
-                        <div class="flex-1">{{ task.text|markdown|bleach }}</div>
+                        <div class="flex-1">{{ task.text|markdown|nh3 }}</div>
                         <a class="button button-primary m-2" href="{{ task.url }}">View</a>
                     </div>
                 {% endfor %}
diff --git a/hypha/apply/determinations/models.py b/hypha/apply/determinations/models.py
index 0707ba0cf68ac3e9a42d1b1a8de4a48361fce84b..09bbfb22cf1df34d0282749a4e529b3d7d851a0a 100644
--- a/hypha/apply/determinations/models.py
+++ b/hypha/apply/determinations/models.py
@@ -1,4 +1,4 @@
-import bleach
+import nh3
 from django.conf import settings
 from django.core.serializers.json import DjangoJSONEncoder
 from django.db import models
@@ -130,7 +130,7 @@ class Determination(DeterminationFormFieldsMixin, AccessFormData, models.Model):
 
     @property
     def stripped_message(self):
-        return bleach.clean(self.message, tags=[], strip=True)
+        return nh3.clean(self.message, tags=set())
 
     @property
     def clean_outcome(self):
diff --git a/hypha/apply/determinations/templates/determinations/base_determination_form.html b/hypha/apply/determinations/templates/determinations/base_determination_form.html
index ec982f879a425d470be29f2eb27d7964e57a81d6..153c84bc66fe3b4e3c46ebc64bd4ba36ef393c72 100644
--- a/hypha/apply/determinations/templates/determinations/base_determination_form.html
+++ b/hypha/apply/determinations/templates/determinations/base_determination_form.html
@@ -1,5 +1,5 @@
 {% extends "base-apply.html" %}
-{% load i18n static bleach_tags %}
+{% load i18n static nh3_tags %}
 {% block title %}{% if object %}{% trans "Edit a Determination" %} {% if object.is_draft %}{% trans "draft" %}{% endif %}{% else %}{% trans "Create a Determination" %}{% endif %}{% endblock %}
 {% block content %}
 
diff --git a/hypha/apply/determinations/templates/determinations/determination_detail.html b/hypha/apply/determinations/templates/determinations/determination_detail.html
index c160ddfe28a0e77fffedece25cf76e7599fb6fea..0ca28fead0722bd89681540f6a2599d2c926b731 100644
--- a/hypha/apply/determinations/templates/determinations/determination_detail.html
+++ b/hypha/apply/determinations/templates/determinations/determination_detail.html
@@ -1,5 +1,5 @@
 {% extends "base-apply.html" %}
-{% load i18n bleach_tags heroicons %}
+{% load i18n nh3_tags heroicons %}
 
 {% block title %}{% trans "Determination for" %} {{ determination.submission.title }}{% endblock %}
 
@@ -32,14 +32,14 @@
 
     <div class="rich-text rich-text--answers prose">
         <h4>{% trans "Determination message" %}</h4>
-        {{ determination.message|bleach }}
+        {{ determination.message|nh3 }}
         {% for group in determination.detailed_data.values %}
             {% if group.title %}
-                <h4>{{ group.title|bleach }}</h4>
+                <h4>{{ group.title|nh3 }}</h4>
             {% endif %}
             {% for question, answer in group.questions %}
                 <h5>{{ question }}</h5>
-                {% if answer %}{% if answer == True %}{{ answer|yesno:"Agree,Disagree" }}{% else %}{{ answer|bleach }}{% endif %}{% else %}-{% endif %}
+                {% if answer %}{% if answer == True %}{{ answer|yesno:"Agree,Disagree" }}{% else %}{{ answer|nh3 }}{% endif %}{% else %}-{% endif %}
             {% endfor %}
         {% endfor %}
     </div>
diff --git a/hypha/apply/funds/differ.py b/hypha/apply/funds/differ.py
index 66ccf9ffbd9db78fc104f2b7dad267ee1f4b42ca..0c289eb98df37b3fafaddaebac7c88478d24b60f 100644
--- a/hypha/apply/funds/differ.py
+++ b/hypha/apply/funds/differ.py
@@ -1,7 +1,8 @@
 import re
 from difflib import SequenceMatcher
+from typing import Tuple
 
-from bleach.sanitizer import Cleaner
+import nh3
 from django.utils.html import format_html
 from django.utils.safestring import mark_safe
 
@@ -16,13 +17,26 @@ def wrap_added(text):
     return format_html('<span class="bg-green-200">{}</span>', mark_safe(text))
 
 
-def compare(answer_a, answer_b, should_bleach=True):
-    if should_bleach:
-        cleaner = Cleaner(tags=["h4"], attributes={}, strip=True)
+def compare(answer_a: str, answer_b: str, should_clean: bool = True) -> Tuple[str, str]:
+    """Compare two strings, populate diff HTML and insert it, and return a tuple of the given strings.
+
+    Args:
+        answer_a:
+            The original string
+        answer_b:
+            The string to compare to the original
+        should_clean:
+            Optional boolean to determine if the string should be sanitized with NH3 (default=True)
+
+    Returns:
+        A tuple of the original strings with diff HTML inserted.
+    """
+
+    if should_clean:
         answer_a = re.sub("(<li[^>]*>)", r"\1â—¦ ", answer_a)
         answer_b = re.sub("(<li[^>]*>)", r"\1â—¦ ", answer_b)
-        answer_a = cleaner.clean(answer_a)
-        answer_b = cleaner.clean(answer_b)
+        answer_a = nh3.clean(answer_a, tags={"h4"}, attributes={})
+        answer_b = nh3.clean(answer_b, tags={"h4"}, attributes={})
 
     diff = SequenceMatcher(None, answer_a, answer_b)
     from_diff = []
diff --git a/hypha/apply/funds/forms.py b/hypha/apply/funds/forms.py
index d1face9a80302aee16653d5cb4e9a3a148303d14..3921248b033559b27dd7a65cb38b4db2d69dcb82 100644
--- a/hypha/apply/funds/forms.py
+++ b/hypha/apply/funds/forms.py
@@ -3,7 +3,7 @@ from functools import partial
 from itertools import groupby
 from operator import methodcaller
 
-import bleach
+import nh3
 from django import forms
 from django.db.models import Q
 from django.utils.safestring import mark_safe
@@ -448,7 +448,7 @@ def make_role_reviewer_fields():
     staff_reviewers = User.objects.staff().only("full_name", "pk")
 
     for role in ReviewerRole.objects.all().order_by("order"):
-        role_name = bleach.clean(role.name, strip=True)
+        role_name = nh3.clean(role.name, tags=set())
         field_name = f"role_reviewer_{role.id}"
         field = forms.ModelChoiceField(
             queryset=staff_reviewers,
diff --git a/hypha/apply/projects/templates/application_projects/report_detail.html b/hypha/apply/projects/templates/application_projects/report_detail.html
index a2cf266eff7f733abf0b256505ba269b8b37348e..b0209e7be5fb6dc6b8cd5d92b1e631037eb025a0 100644
--- a/hypha/apply/projects/templates/application_projects/report_detail.html
+++ b/hypha/apply/projects/templates/application_projects/report_detail.html
@@ -1,5 +1,5 @@
 {% extends "base-apply.html" %}
-{% load i18n static bleach_tags heroicons %}
+{% load i18n static nh3_tags heroicons %}
 
 {% block title %}{% trans "Report" %} | {{ object.project.title }}{% endblock %}
 {% block body_class %}{% endblock %}
@@ -28,12 +28,12 @@
                 {% else %}
                     <h4>{% trans "Public Report" %}</h4>
                     <div class="rich-text">
-                        {{ object.current.public_content|bleach|safe }}
+                        {{ object.current.public_content|nh3|safe }}
                     </div>
 
                     <h4>{% trans "Private Report" %}</h4>
                     <div class="rich-text">
-                        {{ object.current.private_content|bleach|safe }}
+                        {{ object.current.private_content|nh3|safe }}
                     </div>
                     {% for file in object.current.files.all %}
                         {% if forloop.first %}
diff --git a/hypha/apply/projects/templates/application_projects/vendor_detail.html b/hypha/apply/projects/templates/application_projects/vendor_detail.html
index c45bf7c16926eaf367c3a83619368fb2bc2b5047..9fb4d2f0b31a4dda8ef3fa47a5c0052fd5763c1e 100644
--- a/hypha/apply/projects/templates/application_projects/vendor_detail.html
+++ b/hypha/apply/projects/templates/application_projects/vendor_detail.html
@@ -1,5 +1,5 @@
 {% extends "base-apply.html" %}
-{% load bleach_tags i18n approval_tools heroicons %}
+{% load nh3_tags i18n approval_tools heroicons %}
 {% user_can_edit_project object request.user as editable %}
 {% block title %}{% trans "Contracting Information for" %} {{ project.title }} {% endblock %}
 
@@ -31,7 +31,7 @@
     <div class="rich-text rich-text--answers">
         {% for group in vendor_detailed_response.values %}
             {% if group.title %}
-                <h1>{{ group.title|bleach }}</h4>
+                <h1>{{ group.title|nh3 }}</h4>
             {% endif %}
             {% for question, answer in group.questions %}
                 <h5>{{ question }}</h5>
@@ -44,7 +44,7 @@
                         </div>
                     </div>
                 {% else %}
-                    <p>{% if answer == True or answer == False %}{{ answer|yesno:"Yes,No" }}{% else %}{% if answer %}{{ answer|bleach }}{% else %}-{% endif %}{% endif %}</p>
+                    <p>{% if answer == True or answer == False %}{{ answer|yesno:"Yes,No" }}{% else %}{% if answer %}{{ answer|nh3 }}{% else %}-{% endif %}{% endif %}</p>
                 {% endif %}
             {% endfor %}
         {% endfor %}
diff --git a/hypha/apply/review/templates/review/render_scored_answer_field.html b/hypha/apply/review/templates/review/render_scored_answer_field.html
index a3ac5718d0616c3b8d8d3dd110ddfa9db5d0e42a..6b94842c47316f47441da8dadcbabd087c1a3dbf 100644
--- a/hypha/apply/review/templates/review/render_scored_answer_field.html
+++ b/hypha/apply/review/templates/review/render_scored_answer_field.html
@@ -1,8 +1,8 @@
-{% load bleach_tags %}
+{% load nh3_tags %}
 {% block data_display %}
     <div>
         <h5>{{ value.field_label }}</h5>
         <div>{{ score }}</div>
-        <div>{{ comment|bleach }}</div>
+        <div>{{ comment|nh3 }}</div>
     </div>
 {% endblock %}
diff --git a/hypha/apply/review/templates/review/review_detail.html b/hypha/apply/review/templates/review/review_detail.html
index 706828fd41c20049471d3f0bb651c5d74a366f6f..535f6ff36305251af0386f2eff2aa7dfe6baed67 100644
--- a/hypha/apply/review/templates/review/review_detail.html
+++ b/hypha/apply/review/templates/review/review_detail.html
@@ -1,5 +1,5 @@
 {% extends "base-apply.html" %}
-{% load i18n bleach_tags submission_tags heroicons %}
+{% load i18n nh3_tags submission_tags heroicons %}
 {% block title %}{% trans "Review for" %} {{ review.submission.title }}{% endblock %}
 {% block content %}
 
diff --git a/hypha/apply/review/templates/review/review_list.html b/hypha/apply/review/templates/review/review_list.html
index 3cfdd3298db1beb7a8b7f82ce29d8d86fde41c59..272f0b560a86e18b67bf0e183997e0af911ca169 100644
--- a/hypha/apply/review/templates/review/review_list.html
+++ b/hypha/apply/review/templates/review/review_list.html
@@ -1,5 +1,5 @@
 {% extends "base-apply.html" %}
-{% load i18n bleach_tags review_tags workflow_tags %}
+{% load i18n nh3_tags review_tags workflow_tags %}
 
 {% block title %}{% trans "Reviews" %}{% endblock %}
 
@@ -28,7 +28,7 @@
                         {% elif answers.question == "Opinions"%}
                             <td class="reviews-list__td">{{ answer }}</td>
                         {% else %}
-                            <td class="reviews-list__td">{{ answer|bleach }}</td>
+                            <td class="reviews-list__td">{{ answer|nh3 }}</td>
                         {% endif %}
                     {% endfor %}
                 </tr>
diff --git a/hypha/apply/stream_forms/blocks.py b/hypha/apply/stream_forms/blocks.py
index 70d264dd58cc49614085e6357ffd758c776ea60c..13dc30015fd6d3d92436198cffb4e00925c711ba 100644
--- a/hypha/apply/stream_forms/blocks.py
+++ b/hypha/apply/stream_forms/blocks.py
@@ -1,5 +1,5 @@
 # Credit to https://github.com/BertrandBordage for initial implementation
-import bleach
+import nh3
 from anyascii import anyascii
 from dateutil.parser import isoparse, parse
 from django import forms
@@ -11,7 +11,7 @@ from django.utils.encoding import force_str
 from django.utils.html import conditional_escape
 from django.utils.text import slugify
 from django.utils.translation import gettext_lazy as _
-from django_bleach.templatetags.bleach_tags import bleach_value
+from django_nh3.templatetags.nh3_tags import nh3_value
 from wagtail.blocks import (
     BooleanBlock,
     CharBlock,
@@ -89,7 +89,7 @@ class FormFieldBlock(StructBlock):
         }
 
     def prepare_data(self, value, data, serialize=False):
-        return bleach_value(str(data))
+        return nh3_value(str(data))
 
     def render(self, value, context):
         data = context.get("data")
@@ -141,7 +141,7 @@ class CharFieldBlock(OptionalFormFieldBlock):
     def get_searchable_content(self, value, data):
         # CharField acts as a fallback. Force data to string
         data = str(data)
-        return bleach.clean(data or "", tags=[], strip=True)
+        return nh3.clean(data or "", tags=set())
 
 
 class MultiInputCharFieldBlock(CharFieldBlock):
@@ -165,7 +165,7 @@ class TextFieldBlock(OptionalFormFieldBlock):
         template = "stream_forms/render_unsafe_field.html"
 
     def get_searchable_content(self, value, data):
-        return bleach.clean(data or "", tags=[], strip=True)
+        return nh3.clean(data or "", tags=set())
 
 
 class NumberFieldBlock(OptionalFormFieldBlock):
diff --git a/hypha/apply/stream_forms/templates/stream_forms/render_markdown_field.html b/hypha/apply/stream_forms/templates/stream_forms/render_markdown_field.html
index f9b58617702719f094a6549eb5aa0aaf303b021c..4ef393bd465e29ef720db84d0907e2102ab38d29 100644
--- a/hypha/apply/stream_forms/templates/stream_forms/render_markdown_field.html
+++ b/hypha/apply/stream_forms/templates/stream_forms/render_markdown_field.html
@@ -1,8 +1,8 @@
 {% extends "stream_forms/render_field.html" %}
-{% load bleach_tags markdown_tags %}
+{% load nh3_tags markdown_tags %}
 {% block data_display %}
     {% if data %}
-        {{ data|markdown|bleach }}
+        {{ data|markdown|nh3 }}
     {% else %}
         {{ block.super }}
     {% endif %}
diff --git a/hypha/apply/stream_forms/templates/stream_forms/render_unsafe_field.html b/hypha/apply/stream_forms/templates/stream_forms/render_unsafe_field.html
index 9725f800f6d0f4aa3b84d1e06bc2aa481c649c36..3138a4d25213df1027775c460dca877e3d2b5283 100644
--- a/hypha/apply/stream_forms/templates/stream_forms/render_unsafe_field.html
+++ b/hypha/apply/stream_forms/templates/stream_forms/render_unsafe_field.html
@@ -1,13 +1,13 @@
 {% extends "stream_forms/render_field.html" %}
-{% load bleach_tags %}
+{% load nh3_tags %}
 {% block data_display %}
     {% if data %}
         {% if value.format == 'url' %}
-            <a class="link" href="{{ data|bleach }}" target="_blank" rel="noopener noreferrer">{{ data|bleach }}</a>
+            <a class="link" href="{{ data|nh3 }}" target="_blank" rel="noopener noreferrer">{{ data|nh3 }}</a>
         {% elif value.format == 'email' %}
-            <a class="u-email" href="mailto:{{ data|bleach }}">{{ data|bleach }}</a>
+            <a class="u-email" href="mailto:{{ data|nh3 }}">{{ data|nh3 }}</a>
         {% else %}
-            <p>{{ data|bleach }}</p>
+            <p>{{ data|nh3 }}</p>
         {% endif %}
     {% else %}
         {{ block.super }}
diff --git a/hypha/apply/templates/forms/includes/field.html b/hypha/apply/templates/forms/includes/field.html
index b9dec1d834e61c38dd9768460b3220fe25e4b8ad..f4c2802c155974e7b35edb97f895e2202385857d 100644
--- a/hypha/apply/templates/forms/includes/field.html
+++ b/hypha/apply/templates/forms/includes/field.html
@@ -1,5 +1,5 @@
 {% load i18n util_tags %}
-{% load bleach_tags markdown_tags heroicons %}
+{% load nh3_tags markdown_tags heroicons %}
 {% with widget_type=field|widget_type field_type=field|field_type %}
 
     <div class="form__group {{ field.id_for_label }} form__group--{{ widget_type }} {% if widget_type == 'checkbox_input' %} form__group--checkbox{% endif %}{% if widget_type == 'clearable_file_input' or widget_type == 'multi_file_input' or widget_type == 'single_file_field_widget' or widget_type == 'multi_file_field_widget' %} form__group--file{% endif %}{% if field.help_text %} form__group--wrap{% endif %}{% if field.errors %} form__error{% endif %}{% if is_application and field.field.group_number > 1 %} field-group field-group-{{ field.field.group_number }}{% endif %}{% if is_application and field.field.grouper_for %} form-fields-grouper{% endif %}"{% if is_application and field.field.grouper_for %}data-grouper-for="{{ field.field.grouper_for }}" data-toggle-on="{{ field.field.choices.0.0 }}" data-toggle-off="{{ field.field.choices.1.0 }}"{% endif %}{% if is_application and field.field.group_number > 1 %} data-hidden="{% if not show_all_group_fields and not field.field.visible %}true{% else %}false{% endif %}" data-required="{{ field.field.required_when_visible }}"{% endif %}{% if field.field.word_limit %} data-word-limit="{{ field.field.word_limit }}"{% endif %}>
@@ -34,7 +34,7 @@
         {% endif %}
 
         {% if field.help_text %}
-            <div class="form__help prose prose-sm">{{ field.help_text|markdown|bleach }}</div>
+            <div class="form__help prose prose-sm">{{ field.help_text|markdown|nh3 }}</div>
         {% endif %}
 
         {% if field.field.help_link %}
diff --git a/hypha/apply/utils/blocks.py b/hypha/apply/utils/blocks.py
index 7f74b0c5086c4090aa3f46d0329af44032581553..baee0b40e16d78a2451e58fc456ad1f0b300bd26 100644
--- a/hypha/apply/utils/blocks.py
+++ b/hypha/apply/utils/blocks.py
@@ -1,6 +1,6 @@
 from collections import Counter
 
-import bleach
+import nh3
 from django.core.exceptions import ValidationError
 from django.forms.utils import ErrorList
 from django.utils.safestring import mark_safe
@@ -48,7 +48,7 @@ class RichTextFieldBlock(TextFieldBlock):
         icon = "form"
 
     def get_searchable_content(self, value, data):
-        return bleach.clean(data or "", tags=[], strip=True)
+        return nh3.clean(data or "", tags=set())
 
     def no_response(self):
         return "<p>-</p>"
@@ -64,7 +64,7 @@ class MarkdownTextFieldBlock(TextFieldBlock):
         template = "stream_forms/render_markdown_field.html"
 
     def get_searchable_content(self, value, data):
-        return bleach.clean(data or "", tags=[], strip=True)
+        return nh3.clean(data or "", tags=set())
 
     def no_response(self):
         return "<p>-</p>"
diff --git a/hypha/home/templates/apply_home/includes/apply_listing.html b/hypha/home/templates/apply_home/includes/apply_listing.html
index 83d8cbf39aea07dc71780a3a71367b1143dc467f..d4b4ac9e4918bfab147aa4f121f30150cee135c6 100644
--- a/hypha/home/templates/apply_home/includes/apply_listing.html
+++ b/hypha/home/templates/apply_home/includes/apply_listing.html
@@ -1,4 +1,4 @@
-{% load i18n bleach_tags wagtailcore_tags markdown_tags heroicons %}
+{% load i18n nh3_tags wagtailcore_tags markdown_tags heroicons %}
 
 {% if page.open_round and page.list_on_front_page %}
   <div class="flex justify-between items-center px-4 py-8 gap-4 hover:bg-slate-50 transition-colors">
@@ -17,7 +17,7 @@
 
       {% if page.description %}
         <p>
-          {{ page.description|markdown|bleach }}
+          {{ page.description|markdown|nh3 }}
         </p>
       {% endif %}
     </div>
diff --git a/hypha/public/news/templates/news/news_index.html b/hypha/public/news/templates/news/news_index.html
new file mode 100644
index 0000000000000000000000000000000000000000..1cdeb6220791654bc5cc98b63cc49d78b6abc027
--- /dev/null
+++ b/hypha/public/news/templates/news/news_index.html
@@ -0,0 +1,56 @@
+{% extends "base.html" %}
+{% load wagtailcore_tags wagtailimages_tags static markdown_tags nh3_tags %}
+{% block feedlinks %}<link rel="alternate" type="application/rss+xml" title="{{ page.title }}" href="{% url "news_feed" %}">{% endblock %}
+{% block body_class %}light-grey-bg{% endblock %}
+
+{% block content %}
+    <div class="wrapper wrapper--small wrapper--inner-space-medium">
+
+        {% if page.introduction %}
+            <h4 class="heading heading--listings-introduction">{{ page.introduction|markdown|nh3 }}</h4>
+        {% endif %}
+
+        <form class="form" method="GET">
+            <div class="form__select form__select--narrow form__select--inline">
+                <select name="news_type">
+                    <option value="">All</option>
+                    {% for news_type in news_types %}
+                        <option value="{{ news_type.0 }}" {% if request.GET.news_type == news_type.0|slugify %}selected="selected"{% endif %}>{{ news_type.1 }}</option>
+                    {% endfor %}
+                </select>
+            </div>
+            <button class="link link--button link--button__stretch" type="submit">Filter</button>
+        </form>
+
+        {% if news %}
+            <div class="wrapper wrapper--listings wrapper--top-space">
+                {% for n in news %}
+                    <a class="listing" href="{% pageurl n %}">
+                        {% if n.listing_image %}
+                            {% image n.listing_image fill-450x300 %}
+                        {% endif %}
+                        <h4 class="listing__title" role="listitem">
+                            {{ n.listing_title|default:n.title }}
+                        </h4>
+                        {% if n.listing_summary or n.introduction %}
+                            <h6 class="listing__teaser">{{ n.listing_summary|default:n.introduction }}</h6>
+                        {% endif %}
+                        <span class="listing__meta">
+                            {{ n.display_date|date:"SHORT_DATE_FORMAT" }}
+                            {% if n.authors.all %}
+                                | By:
+                                {% for author in n.authors.all %}
+                                    {{ author.author }}
+                                {% endfor %}
+                            {% endif %}
+                        </span>
+                    </a>
+                {% endfor %}
+            </div>
+            {% include "includes/pagination.html" with paginator_page=news %}
+        {% else %}
+            {# no items #}
+        {% endif %}
+
+    </div>
+{% endblock %}
diff --git a/hypha/settings/base.py b/hypha/settings/base.py
index 6c4eb4854d6341eecfe016cabb3caf472d40b850..e5fb607357c61b36dde7fe2fef3adf6cab60a6eb 100644
--- a/hypha/settings/base.py
+++ b/hypha/settings/base.py
@@ -383,9 +383,9 @@ SOCIAL_AUTH_PIPELINE = (
     "hypha.apply.users.pipeline.make_otf_staff",
 )
 
-# Bleach Settings
+# NH3 Settings
 
-BLEACH_ALLOWED_TAGS = [
+NH3_ALLOWED_TAGS = [
     "a",
     "b",
     "big",
@@ -426,18 +426,20 @@ BLEACH_ALLOWED_TAGS = [
     "tr",
     "ul",
 ]
-BLEACH_ALLOWED_ATTRIBUTES = [
-    "class",
-    "colspan",
-    "href",
-    "rowspan",
-    "target",
-    "title",
-    "width",
-]
-BLEACH_ALLOWED_STYLES = []
-BLEACH_STRIP_TAGS = True
-BLEACH_STRIP_COMMENTS = True
+
+NH3_ALLOWED_ATTRIBUTES = {
+    "*": [
+        "class",
+        "colspan",
+        "href",
+        "rowspan",
+        "target",
+        "title",
+        "width",
+    ]
+}
+
+NH3_STRIP_COMMENTS = True
 
 
 # Hijack Settings
diff --git a/hypha/settings/django.py b/hypha/settings/django.py
index 40e092d9fb353a9fbce21207bfe3b55aa4a6af49..3f3cac9bff4657631ec89834eaaf6f5a986e8981 100644
--- a/hypha/settings/django.py
+++ b/hypha/settings/django.py
@@ -57,7 +57,7 @@ INSTALLED_APPS = [
     "django_filters",
     "django_select2",
     "addressfield",
-    "django_bleach",
+    "django_nh3",
     "django_fsm",
     "django_pwned_passwords",
     "django_slack",
diff --git a/requirements.txt b/requirements.txt
index 7ce4429913666f734e4c483e574f6f6c4ec0c1f6..1ab5b86ba21da4361635d5ff006df92b0396e979 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,7 +10,7 @@ click==8.1.7
 dj-database-url==2.1.0
 django-anymail==10.2
 django-basic-auth-ip-whitelist==0.5
-django-bleach==3.1.0
+django-nh3==0.1.1
 django-countries==7.5.1
 django-elevate==2.0.3
 django-extensions==3.2.3