diff --git a/layerindex/views.py b/layerindex/views.py
index 592311f..35186e6 100644
--- a/layerindex/views.py
+++ b/layerindex/views.py
@@ -19,6 +19,7 @@ from django import forms
 from django.contrib import messages
 from django.contrib.auth import logout
 from django.contrib.auth.decorators import login_required
+from django.contrib.auth.mixins import LoginRequiredMixin
 from django.contrib.auth.hashers import make_password
 from django.contrib.auth.models import Permission, User
 from django.contrib.messages.views import SuccessMessageMixin
@@ -1001,7 +1002,7 @@ class HistoryListView(ListView):
         return Revision.objects.all().order_by('-date_created')
 
 
-class EditProfileFormView(SuccessMessageMixin, UpdateView):
+class EditProfileFormView(LoginRequiredMixin, SuccessMessageMixin, UpdateView):
     form_class = EditProfileForm
 
     @method_decorator(never_cache)
diff --git a/tests/test_profile_email.py b/tests/test_profile_email.py
index 79e4adf..57feee5 100644
--- a/tests/test_profile_email.py
+++ b/tests/test_profile_email.py
@@ -65,3 +65,12 @@ class TestProfileEmailChange(TestCase):
         self.user.refresh_from_db()
         self.assertTrue(self.user.is_active)
         self.assertEqual(len(mail.outbox), 0)
+
+    def test_profile_requires_login(self):
+        self.client.logout()
+        for method in ('get', 'post'):
+            response = getattr(self.client, method)(reverse('profile'))
+            self.assertEqual(response.status_code, 302, method)
+            self.assertIn(reverse('login'), response['Location'])
+        self.user.refresh_from_db()
+        self.assertEqual(self.user.email, 'old@example.com')
