diff --git a/opentech/public/esi/__init__.py b/opentech/public/esi/__init__.py
deleted file mode 100644
index d4bd564be6488ffd0d5ce092c0f2c3dfc84b63b8..0000000000000000000000000000000000000000
--- a/opentech/public/esi/__init__.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from django.conf import settings
-from django.urls import reverse
-from django.template.loader import render_to_string
-
-
-ESI_REGISTRY = {}
-
-
-def register_inclusion_tag(register):
-    def esi_inclusion_tag(template):
-        def dec(func):
-            name = func.__name__
-
-            if name in ESI_REGISTRY:
-                raise Exception("There's already an esi_inclusion_tag called '%s'" % name)
-
-            ESI_REGISTRY[name] = {
-                'get_context': func,
-                'template': template
-            }
-
-            @register.simple_tag(name=name, takes_context=True)
-            def tag_func(context):
-                if settings.ESI_ENABLED:
-                    root_url = context['request'].site.root_url
-                    # Note that ESI has been used on the request object
-                    # This allows the ESI middleware to know when to set the X-ESI header on the response
-                    context['request']._esi_include_used = True
-                    return '<esi:include src="%s/esi/%s/" />' % (root_url, name)
-                else:
-                    return render_to_string(template, func(context))
-
-            return tag_func
-        return dec
-    return esi_inclusion_tag
-
-
-def purge_esi():
-    from opentech.public.utils.cache import purge_cache_on_all_sites
-
-    for name in ESI_REGISTRY:
-        # TODO: might need a separate domain for ESI and call wagtail.contrib.frontend_cache.utils.purge_url_from_cache
-        purge_cache_on_all_sites(reverse('esi', args=[name]))
diff --git a/opentech/public/esi/middleware.py b/opentech/public/esi/middleware.py
deleted file mode 100644
index bce7d1e40ffe97a9906c1d7833367759e2841453..0000000000000000000000000000000000000000
--- a/opentech/public/esi/middleware.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from django.conf import settings
-from django.core.exceptions import MiddlewareNotUsed
-
-
-class ESIMiddleware:
-    """
-    Adds "X-ESI: 1" header into the response if and ESI include has been used
-    """
-    def __init__(self, get_response):
-        if not settings.ESI_ENABLED:
-            raise MiddlewareNotUsed
-        self.get_response = get_response
-
-    def __call__(self, request):
-        response = self.get_response(request)
-
-        if getattr(request, '_esi_include_used', False):
-            response['X-ESI'] = '1'
-
-        return response
diff --git a/opentech/public/esi/models.py b/opentech/public/esi/models.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/opentech/public/esi/views.py b/opentech/public/esi/views.py
deleted file mode 100644
index ae12f4d880e709dbac185b83973b5ac5b5133760..0000000000000000000000000000000000000000
--- a/opentech/public/esi/views.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from django.shortcuts import render_to_response
-
-from opentech.public.esi import ESI_REGISTRY
-
-
-def esi(request, name):
-    template = ESI_REGISTRY[name]['template']
-    context = ESI_REGISTRY[name]['get_context']()
-    return render_to_response(template, context)
diff --git a/opentech/public/forms/__init__.py b/opentech/public/forms/__init__.py
deleted file mode 100644
index bb2741c81da1f285a4da9406913764ca18223bf1..0000000000000000000000000000000000000000
--- a/opentech/public/forms/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-default_app_config = 'opentech.public.forms.apps.FormsConfig'
diff --git a/opentech/public/forms/apps.py b/opentech/public/forms/apps.py
deleted file mode 100644
index 71dfe9124852b4f28997515934e3d068178e41ac..0000000000000000000000000000000000000000
--- a/opentech/public/forms/apps.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from django.apps import AppConfig
-
-
-class FormsConfig(AppConfig):
-    name = 'opentech.public.forms'
-    label = 'public_forms'
diff --git a/opentech/public/forms/migrations/0001_initial.py b/opentech/public/forms/migrations/0001_initial.py
deleted file mode 100644
index 87ed0d07a3901b2b31a7846ea6f9646eafdb5c7b..0000000000000000000000000000000000000000
--- a/opentech/public/forms/migrations/0001_initial.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11.4 on 2017-08-24 14:42
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-import modelcluster.fields
-import wagtail.core.fields
-
-
-class Migration(migrations.Migration):
-
-    initial = True
-
-    dependencies = [
-        ('wagtailcore', '0040_page_draft_title'),
-        ('images', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.CreateModel(
-            name='FormField',
-            fields=[
-                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
-                ('label', models.CharField(help_text='The label of the form field', max_length=255, verbose_name='label')),
-                ('field_type', models.CharField(choices=[('singleline', 'Single line text'), ('multiline', 'Multi-line text'), ('email', 'Email'), ('number', 'Number'), ('url', 'URL'), ('checkbox', 'Checkbox'), ('checkboxes', 'Checkboxes'), ('dropdown', 'Drop down'), ('multiselect', 'Multiple select'), ('radio', 'Radio buttons'), ('date', 'Date'), ('datetime', 'Date/time')], max_length=16, verbose_name='field type')),
-                ('required', models.BooleanField(default=True, verbose_name='required')),
-                ('choices', models.TextField(blank=True, help_text='Comma separated list of choices. Only applicable in checkboxes, radio and dropdown.', verbose_name='choices')),
-                ('default_value', models.CharField(blank=True, help_text='Default value. Comma separated values supported for checkboxes.', max_length=255, verbose_name='default value')),
-                ('help_text', models.CharField(blank=True, max_length=255, verbose_name='help text')),
-            ],
-            options={
-                'ordering': ['sort_order'],
-                'abstract': False,
-            },
-        ),
-        migrations.CreateModel(
-            name='FormPage',
-            fields=[
-                ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
-                ('social_text', models.CharField(blank=True, max_length=255)),
-                ('listing_title', models.CharField(blank=True, help_text='Override the page title used when this page appears in listings', max_length=255)),
-                ('listing_summary', models.CharField(blank=True, help_text="The text summary used when this page appears in listings. It's also used as the description for search engines if the 'Search description' field above is not defined.", max_length=255)),
-                ('to_address', models.CharField(blank=True, help_text='Optional - form submissions will be emailed to these addresses. Separate multiple addresses by comma.', max_length=255, verbose_name='to address')),
-                ('from_address', models.CharField(blank=True, max_length=255, verbose_name='from address')),
-                ('subject', models.CharField(blank=True, max_length=255, verbose_name='subject')),
-                ('introduction', models.TextField(blank=True)),
-                ('thank_you_text', wagtail.core.fields.RichTextField(blank=True, help_text='Text displayed to the user on successful submission of the form')),
-                ('action_text', models.CharField(blank=True, help_text='Form action text. Defaults to "Submit"', max_length=32)),
-                ('listing_image', models.ForeignKey(blank=True, help_text='Choose the image you wish to be displayed when this page appears in listings', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
-                ('social_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')),
-            ],
-            options={
-                'abstract': False,
-            },
-            bases=('wagtailcore.page', models.Model),
-        ),
-        migrations.AddField(
-            model_name='formfield',
-            name='page',
-            field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='form_fields', to='public_forms.FormPage'),
-        ),
-    ]
diff --git a/opentech/public/forms/migrations/0002_formpage_header_image.py b/opentech/public/forms/migrations/0002_formpage_header_image.py
deleted file mode 100644
index 3ce1bf394c1bf381294d9fae5a23c9036a379ba4..0000000000000000000000000000000000000000
--- a/opentech/public/forms/migrations/0002_formpage_header_image.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11.8 on 2018-01-05 15:03
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('images', '0001_initial'),
-        ('public_forms', '0001_initial'),
-    ]
-
-    operations = [
-        migrations.AddField(
-            model_name='formpage',
-            name='header_image',
-            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage'),
-        ),
-    ]
diff --git a/opentech/public/forms/migrations/__init__.py b/opentech/public/forms/migrations/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/opentech/public/forms/models.py b/opentech/public/forms/models.py
deleted file mode 100644
index 29a9d12bea2d8f052374fe7a174aff4e34dc652d..0000000000000000000000000000000000000000
--- a/opentech/public/forms/models.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from django.db import models
-
-from modelcluster.fields import ParentalKey
-
-from wagtail.core.fields import RichTextField
-from wagtail.admin.edit_handlers import (
-    FieldPanel, FieldRowPanel,
-    MultiFieldPanel, InlinePanel
-)
-from wagtail.contrib.forms.models import AbstractEmailForm, AbstractFormField
-from wagtail.search import index
-
-from opentech.public.utils.models import BasePage
-
-
-class FormField(AbstractFormField):
-    page = ParentalKey('FormPage', related_name='form_fields')
-
-
-class FormPage(AbstractEmailForm, BasePage):
-    subpage_types = []
-
-    introduction = models.TextField(blank=True)
-    thank_you_text = RichTextField(blank=True, help_text="Text displayed to the user on successful submission of the form")
-    action_text = models.CharField(max_length=32, blank=True, help_text="Form action text. Defaults to \"Submit\"")
-
-    search_fields = BasePage.search_fields + [
-        index.SearchField('introduction'),
-    ]
-
-    content_panels = BasePage.content_panels + [
-        FieldPanel('introduction'),
-        InlinePanel('form_fields', label="Form fields"),
-        FieldPanel('action_text'),
-        FieldPanel('thank_you_text'),
-        MultiFieldPanel([
-            FieldRowPanel([
-                FieldPanel('from_address', classname="col6"),
-                FieldPanel('to_address', classname="col6"),
-            ]),
-            FieldPanel('subject'),
-        ], "Email"),
-    ]
diff --git a/opentech/public/forms/templates/public_forms/form_landing_page.html b/opentech/public/forms/templates/public_forms/form_landing_page.html
deleted file mode 100644
index 007d6b697076738451253c5ce57b517722d2a1c0..0000000000000000000000000000000000000000
--- a/opentech/public/forms/templates/public_forms/form_landing_page.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends "base.html" %}
-{% load wagtailcore_tags wagtailimages_tags static %}
-
-{% block content %}
-
-    <div class="intro">
-        <div class="container">
-            <h1>Thank you</h1>
-            <div>{{ page.thank_you_text|richtext }}</div>
-        </div>
-    </div>
-
-{% endblock %}
diff --git a/opentech/public/forms/templates/public_forms/form_page.html b/opentech/public/forms/templates/public_forms/form_page.html
deleted file mode 100644
index 115d742fc5751b31e3caaf30a99528e8777d1d23..0000000000000000000000000000000000000000
--- a/opentech/public/forms/templates/public_forms/form_page.html
+++ /dev/null
@@ -1,78 +0,0 @@
-{% extends "base.html" %}
-{% load wagtailcore_tags wagtailimages_tags util_tags static %}
-{% block header_modifier %}header--light-bg{% endblock %}
-{% block content %}
-
-{% if form.errors or form.non_field_errors %}
-    <div class="wrapper wrapper--medium wrapper--error">
-        <svg class="icon icon--error"><use xlink:href="#error"></use></svg>
-        <h5 class="heading heading--no-margin heading--regular">There were some errors with your form. Please amend the fields highlighted below</h5>
-        {% if form.non_field_errors %}
-            <ul>
-                {% for error in form.non_field_errors %}
-                    <li class="error">{{ error }}</li>
-                {% endfor %}
-            </ul>
-        {% endif %}
-    </div>
-{% endif %}
-
-<div class="wrapper wrapper--medium wrapper--light-grey-bg wrapper--form">
-    <p>{{ page.introduction }}</p>
-
-    {% if page.form_fields %}
-        <form class="form" action="{% pageurl page %}" method="post">
-            {% csrf_token %}
-
-            {% for field in form %}
-                {% with widget_type=field|widget_type field_type=field|field_type %}
-
-                    {% if widget_type == 'checkbox_input' %}
-                        <div class="form__group {% if field.errors %}form__error{% endif %}">
-                            <label for="{{ field.id_for_label }}" class="form__question form__question--{{ field_type }} {{ widget_type }}" {% if field.field.required %}required{% endif %}>
-                                <span>{{ field.label }}</span>
-                                {% if field.field.required %}
-                                    <span class="form__required">*</span>
-                                {% endif %}
-                            </label>
-                            {% if field.help_text %}
-                                <p class="form__help">{{ field.help_text }}</p>
-                            {% endif %}
-                            <div class="form__item">
-                                {{ field }}
-                                {% if field.errors %}<h6 class="form__error-text">{{ field.errors.as_text }}</h6>{% endif %}
-                                <label for="{{ field.id_for_label }}"></label>
-                            </div>
-                        </div>
-                    {% else %}
-                        <div class="form__group {% if field.errors %}form__error{% endif %}">
-                            <label for="{{ field.id_for_label }}" class="form__question form__question--{{ field_type }} {{ widget_type}}" {% if field.field.required %}required{% endif %}>
-                                <span>{{ field.label }}</span>
-                                {% if field.field.required %}
-                                    <span class="form__required">*</span>
-                                {% endif %}
-                            </label>
-                            {% if field.help_text %}
-                                <p class="form__help">{{ field.help_text }}</p>
-                            {% endif %}
-                            <div class="form__item">
-                                {% if widget_type == 'date_input' or widget_type == 'date_time_input' %}
-                                    <div class="{{ widget_type }}">
-                                {% endif %}
-                                    {{ field }}
-                                {% if field.errors %}<h6 class="form__error-text">{{ field.errors.as_text }}</h6>{% endif %}
-                                {% if widget_type == 'date_input' or widget_type == 'date_time_input' %}
-                                    </div>
-                                {% endif %}
-                                </div>
-                            </div>
-                    {% endif %}
-
-                {% endwith %}
-            {% endfor %}
-            <input class="link link--button-secondary" type="submit" value="{% if page.action_text %}{{ page.action_text|safe }}{% else %}Submit{% endif %}" />
-        </form>
-    {% endif %}
-</div>
-
-{% endblock %}
diff --git a/opentech/public/navigation/migrations/0002_remove_unused_navigation_elements.py b/opentech/public/navigation/migrations/0002_remove_unused_navigation_elements.py
index c6ad36c26d5142d3f9c8906b12f8c3543a62f14a..e3a9d091357f485095340e4bd6c7d8e6647bfa70 100644
--- a/opentech/public/navigation/migrations/0002_remove_unused_navigation_elements.py
+++ b/opentech/public/navigation/migrations/0002_remove_unused_navigation_elements.py
@@ -1,4 +1,5 @@
-# Generated by Django 2.0.2 on 2018-08-08 15:18
+# Generated by Django 2.0.2 on 2018-08-09 11:13
+
 
 from django.db import migrations
 
diff --git a/opentech/public/navigation/models.py b/opentech/public/navigation/models.py
index 35adefb7bc8d1fa792584d090ab003b13183de96..adce6c8af60137134d9b0072d09a82a1c68004e3 100644
--- a/opentech/public/navigation/models.py
+++ b/opentech/public/navigation/models.py
@@ -4,8 +4,6 @@ from wagtail.admin.edit_handlers import StreamFieldPanel
 from wagtail.core import blocks
 from wagtail.core.fields import StreamField
 
-from opentech.public.esi import purge_esi
-
 
 class LinkBlock(blocks.StructBlock):
     page = blocks.PageChooserBlock()
@@ -15,14 +13,6 @@ class LinkBlock(blocks.StructBlock):
         template = 'navigation/blocks/menu_item.html',
 
 
-class LinkColumnWithHeader(blocks.StructBlock):
-    heading = blocks.CharBlock(required=False, help_text="Leave blank if no header required.")
-    links = blocks.ListBlock(LinkBlock())
-
-    class Meta:
-        template = 'navigation/blocks/footer_column.html',
-
-
 @register_setting(icon='list-ul')
 class NavigationSettings(BaseSetting, ClusterableModel):
     primary_navigation = StreamField(
@@ -34,11 +24,3 @@ class NavigationSettings(BaseSetting, ClusterableModel):
     panels = [
         StreamFieldPanel('primary_navigation'),
     ]
-
-    def save(self, *args, **kwargs):
-        super().save(*args, **kwargs)
-        purge_esi()
-
-    def delete(self, *args, **kwargs):
-        super().delete(*args, **kwargs)
-        purge_esi()
diff --git a/opentech/public/navigation/templatetags/navigation_tags.py b/opentech/public/navigation/templatetags/navigation_tags.py
index 8ae04b03aec4391ab61627dc6752fa6271b0f963..84ac38eddfe2816b4808fd7602d0449130d05d79 100644
--- a/opentech/public/navigation/templatetags/navigation_tags.py
+++ b/opentech/public/navigation/templatetags/navigation_tags.py
@@ -1,16 +1,13 @@
 from django import template
 
-from opentech.public.esi import register_inclusion_tag
 from opentech.public.navigation.models import NavigationSettings
 
 
 register = template.Library()
 
-esi_inclusion_tag = register_inclusion_tag(register)
-
 
 # Primary nav snippets
-@esi_inclusion_tag('navigation/primarynav.html')
+@register.inclusion_tag('navigation/primarynav.html', takes_context=True)
 def primarynav(context):
     request = context['request']
     site = context.get('PUBLIC_SITE', request.site)
diff --git a/opentech/public/urls.py b/opentech/public/urls.py
index 44aef2a3ec606d73d586e65d4e3feb361ea0ba67..40b2bb96aa3f0b5df11dc0c953999759bd8b36d5 100644
--- a/opentech/public/urls.py
+++ b/opentech/public/urls.py
@@ -1,12 +1,10 @@
 from django.urls import include, path
 
-from .esi import views as esi_views
 from .search import views as search_views
 from .mailchimp import urls as newsletter_urls
 
 
 urlpatterns = [
-    path('esi/<slug>/', esi_views.esi, name='esi'),
     path('search/', search_views.search, name='search'),
     path('newsletter/', include(newsletter_urls))
 ]
diff --git a/opentech/public/utils/wagtail_hooks.py b/opentech/public/utils/wagtail_hooks.py
index 674b4fbfe1227fd71dd5cd0020b6e4456090e725..ec99c6dc25710552d8f0cf59acc69216fcde660c 100644
--- a/opentech/public/utils/wagtail_hooks.py
+++ b/opentech/public/utils/wagtail_hooks.py
@@ -1,4 +1,3 @@
-from wagtail.core import hooks
 from wagtail.contrib.modeladmin.options import ModelAdminGroup, ModelAdmin, modeladmin_register
 
 
@@ -23,9 +22,3 @@ class TaxonomiesModelAdminGroup(ModelAdminGroup):
 
 
 modeladmin_register(TaxonomiesModelAdminGroup)
-
-
-# Hide forms from the side menu, remove if adding public.forms back in
-@hooks.register('construct_main_menu')
-def hide_snippets_menu_item(request, menu_items):
-    menu_items[:] = [item for item in menu_items if item.name != 'forms']
diff --git a/opentech/settings/base.py b/opentech/settings/base.py
index 163b9fabe504d2b451102d1276dd6da4bae6c7e2..a1c0454510c658577ec5bfacebaccbeb9e5d3b53 100644
--- a/opentech/settings/base.py
+++ b/opentech/settings/base.py
@@ -26,7 +26,6 @@ INSTALLED_APPS = [
     'opentech.apply.determinations',
     'opentech.apply.stream_forms',
 
-    'opentech.public.esi',
     'opentech.public.funds',
     'opentech.public.home',
     'opentech.public.mailchimp',
@@ -95,7 +94,6 @@ MIDDLEWARE = [
 
     'wagtail.core.middleware.SiteMiddleware',
     'wagtail.contrib.redirects.middleware.RedirectMiddleware',
-    'opentech.public.esi.middleware.ESIMiddleware',
 
     'opentech.apply.middleware.apply_url_conf_middleware',
 ]