diff --git a/opentech/apply/urls.py b/opentech/apply/urls.py
index 662aa72b21389a8ee73bde962044f1ecfb6caa0e..deb32700492d24ef9fe3db8d49680f0241fcb757 100644
--- a/opentech/apply/urls.py
+++ b/opentech/apply/urls.py
@@ -1,5 +1,7 @@
+from django.conf import settings
 from django.urls import include, path
 
+from .utils import views
 from .users import urls as users_urls
 from .dashboard import urls as dashboard_urls
 
@@ -14,4 +16,13 @@ urlpatterns = [
     path('hijack/', include('hijack.urls', 'hijack')),
 ]
 
+if settings.DEBUG:
+    urlpatterns += [
+        # Add views for testing 404 and 500 templates
+        path('test404/', views.page_not_found),
+    ]
+
 urlpatterns += base_urlpatterns
+
+
+handler404 = 'opentech.apply.utils.views.page_not_found'
diff --git a/opentech/apply/utils/__init__.py b/opentech/apply/utils/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..406254cfe657826bdc2a6ce7992d38cb46a4af9f 100644
--- a/opentech/apply/utils/__init__.py
+++ b/opentech/apply/utils/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'opentech.apply.utils.app.UtilsConfig'
diff --git a/opentech/apply/utils/app.py b/opentech/apply/utils/app.py
new file mode 100644
index 0000000000000000000000000000000000000000..b7215b4e1eb04ecd21099117b9989161f82be7ad
--- /dev/null
+++ b/opentech/apply/utils/app.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class UtilsConfig(AppConfig):
+    name = 'opentech.apply.utils'
+    label = 'apply_utils'
diff --git a/opentech/apply/utils/templates/apply/404.html b/opentech/apply/utils/templates/apply/404.html
new file mode 100644
index 0000000000000000000000000000000000000000..30b1072c3da83893225f7fe931f2292f5f4b5651
--- /dev/null
+++ b/opentech/apply/utils/templates/apply/404.html
@@ -0,0 +1,13 @@
+{% extends "base-apply.html" %}
+{% load wagtailcore_tags wagtailsettings_tags %}
+
+{% block title %}{{ settings.utils.SystemMessagesSettings.title_404 }}{% endblock %}
+
+{% block body_class %}template-404{% endblock %}
+
+{% block content %}
+<div class="wrapper wrapper--small wrapper--inner-space-large">
+    <h1>{{ settings.utils.SystemMessagesSettings.title_404 }}</h1>
+    {{ settings.utils.SystemMessagesSettings.body_404|richtext }}
+</div>
+{% endblock %}
diff --git a/opentech/apply/utils/views.py b/opentech/apply/utils/views.py
index d77666e5accbec25b5f044e3b5b315c3915341dc..41a7a01de17a628e7cc4fa8650231a48970786c8 100644
--- a/opentech/apply/utils/views.py
+++ b/opentech/apply/utils/views.py
@@ -1,10 +1,15 @@
 from django.contrib.auth.decorators import login_required
 from django.utils.decorators import method_decorator
+from django.views import defaults
 from django.views.generic import DetailView, View
 from django.views.generic.detail import SingleObjectTemplateResponseMixin
 from django.views.generic.edit import ModelFormMixin, ProcessFormView
 
 
+def page_not_found(request, exception=None, template_name='apply/404.html'):
+    return defaults.page_not_found(request, exception, template_name)
+
+
 @method_decorator(login_required, name='dispatch')
 class ViewDispatcher(View):
     admin_view: View = None
diff --git a/opentech/settings/base.py b/opentech/settings/base.py
index 12e28fd06bc60820f8960b3c3578f0be4d09e31d..b84356011745d9089b27884e9884e89f32c00add 100644
--- a/opentech/settings/base.py
+++ b/opentech/settings/base.py
@@ -73,6 +73,7 @@ INSTALLED_APPS = [
     'opentech.apply.review',
     'opentech.apply.determinations',
     'opentech.apply.stream_forms',
+    'opentech.apply.utils',
 
     'opentech.public.funds',
     'opentech.public.home',