diff --git a/arkindex/project/mixins.py b/arkindex/project/mixins.py
index db62982097553bc2e262e1d7d8a9f2f2bbd91272..5a26c9de6548f7d56bc5f628ca2349a5bb81e958 100644
--- a/arkindex/project/mixins.py
+++ b/arkindex/project/mixins.py
@@ -1,6 +1,7 @@
 from django.shortcuts import get_object_or_404
 from django.conf import settings
 from django.core.exceptions import PermissionDenied
+from django.views.decorators.cache import cache_page
 from rest_framework.exceptions import APIException, ValidationError
 from rest_framework.serializers import Serializer
 from arkindex.documents.models import Corpus, Right, Element
@@ -144,3 +145,18 @@ class DeprecatedMixin(object):
         method = request.method.lower()
         if method in self.http_method_names and hasattr(self, method):
             raise DeprecatedAPIException(detail=getattr(self, 'deprecation_message', None))
+
+
+class CachedViewMixin(object):
+    """
+    Add this mixin to any class-based view to cache it.
+    """
+    cache_timeout = None
+    cache_prefix = None
+
+    def __init__(self, *args, **kwargs):
+        if self.cache_timeout:
+            self.dispatch = cache_page(
+                self.cache_timeout,
+                key_prefix=self.cache_prefix,
+            )(self.dispatch)
diff --git a/arkindex/project/urls.py b/arkindex/project/urls.py
index 5ece3a876dccbdb1d5612a292b28cfe9f14f90bd..5e26e34ba4f10bc012687a633407a3ad3e447e0c 100644
--- a/arkindex/project/urls.py
+++ b/arkindex/project/urls.py
@@ -3,13 +3,14 @@ from django.conf import settings
 from django.contrib import admin
 from django.contrib.staticfiles.urls import staticfiles_urlpatterns
 from arkindex.project.api_v1 import api
-from arkindex.project.views import FrontendView, CdnHome
+from arkindex.project.views import FrontendView, CdnHome, OpenAPIDocsView
 
 # Fallback to the dummy frontend view when CDN_ASSETS_URL is not set
 frontend_view = FrontendView if settings.CDN_ASSETS_URL is None else CdnHome
 
 urlpatterns = [
     path('api/v1/', include((api, 'api'), namespace='api')),
+    path('api-docs/', OpenAPIDocsView.as_view(), name='openapi-docs'),
     path('ponos/', include('ponos.urls')),
     path('admin/', admin.site.urls),
     # Frontend URLs the backend needs with django.urls.reverse
diff --git a/arkindex/project/views.py b/arkindex/project/views.py
index 80604c466df66deb275c2743a845b973110af258..ddb94f2a13ffef849741bd5f6db3fdb539ab1f4a 100644
--- a/arkindex/project/views.py
+++ b/arkindex/project/views.py
@@ -1,6 +1,7 @@
 from django.views.generic import View, TemplateView
-from django.views.decorators.cache import cache_page
 from django.conf import settings
+from arkindex.project.mixins import CachedViewMixin
+import arkindex
 
 
 class FrontendView(View):
@@ -10,16 +11,13 @@ class FrontendView(View):
     """
 
 
-class CdnHome(TemplateView):
+class CdnHome(CachedViewMixin, TemplateView):
     """
     Index template to use frontend assets from a CDN
     """
     template_name = 'index.html'
-
-    def dispatch(self, *args, **kwargs):
-        # Cache rendered page
-        # On dev, by default a dummy cache is enabled
-        return cache_page(60 * 10)(super().dispatch)(*args, **kwargs)
+    cache_timeout = 86400
+    cache_prefix = settings.FRONTEND_VERSION
 
     def get_context_data(self, *args, **kwargs):
         ctx = super().get_context_data(*args, **kwargs)
@@ -30,3 +28,12 @@ class CdnHome(TemplateView):
         ctx['frontend_version'] = settings.FRONTEND_VERSION
         ctx['frontend_sentry_dsn'] = settings.FRONTEND_SENTRY_DSN
         return ctx
+
+
+class OpenAPIDocsView(CachedViewMixin, TemplateView):
+    """
+    OpenAPI documentation via ReDoc
+    """
+    template_name = 'redoc.html'
+    cache_timeout = 86400
+    cache_prefix = arkindex.VERSION
diff --git a/arkindex/templates/redoc.html b/arkindex/templates/redoc.html
new file mode 100644
index 0000000000000000000000000000000000000000..1501bfdb9ac2ba7bf7f4c34ca3cd84349973f27e
--- /dev/null
+++ b/arkindex/templates/redoc.html
@@ -0,0 +1,23 @@
+{% load package_version %}
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Arkindex API {% package_version %}</title>
+    <!-- needed for adaptive design -->
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
+    <!-- ReDoc doesn't change outer page styles -->
+    <style>
+      body {
+        margin: 0;
+        padding: 0;
+      }
+    </style>
+  </head>
+  <body>
+    <!-- Add ?format=openapi to make ReDoc's download button get a YAML file, not HTML -->
+    <redoc spec-url="{% url 'api:openapi-schema' %}?format=openapi"></redoc>
+    <script src="https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js"> </script>
+  </body>
+</html>