DEV Community

Matteo Vitali
Matteo Vitali

Posted on • Edited on

Boost your Django projects's security with proper `Cache-Control` on views

Caching is great for performance, but sensitive data must never be cached! In Django, properly configuring Cache Control on your views ensures secure handling of sensitive content, such as login pages or user-specific data.

🔒 Why is Cache Control important?
Without proper cache settings, sensitive data may be stored in the user's browser or intermediary proxies, exposing it to potential risks.

💡 How to configure Cache Control in Django?
You can use the @never_cache decorator on function-based views to prevent them from being cached (as mentioned in the official documentation):

from django.views.decorators.cache import never_cache  

@never_cache  
def my_secure_view(request):  
    # Your secure logic here  
    return HttpResponse("This page is protected from caching!")  
Enter fullscreen mode Exit fullscreen mode

But what if you want to reuse this logic across multiple class-based views? You can create a reusable mixin for this purpose!

# myproject/views.py

from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache

@method_decorator(never_cache, name="dispatch")
class PrivateAreaMixin(LoginRequiredMixin):
    """Override LoginRequiredMixin by adding Cache-Control rules."""
Enter fullscreen mode Exit fullscreen mode

With this mixin, securing your class-based views becomes much simpler:

# myapp/views.py

from django.views.generic import TemplateView
from myproject.views import PrivateAreaMixin

class IndexView(PrivateAreaMixin, TemplateView):
    """The index view."""

    template_name = "index.html"
Enter fullscreen mode Exit fullscreen mode

🛠 Testing the Implementation
Testing is crucial to ensure everything works as expected. Here's how you can test the PrivateAreaMixin:

# myproject/tests/test_views.py

from django.test import TestCase, RequestFactory
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth import get_user_model
from django.http import HttpResponse
from django.views import View
from myproject.views import PrivateAreaMixin

class PrivateAreaMixinTest(TestCase):
    """Test private area Cache-Control override."""

    factory = RequestFactory()

    @classmethod
    def setUpTestData(cls):
        """Initialize test data."""
        cls.user = get_user_model().objects.create_user(
            username="testuser", 
            email="user@test.xyz", 
            password="5tr0ngP4ssW0rd",
        )

    def test_login_required_with_cache_control(self):
        """Test login required and Cache-Control dispatched."""

        class AView(PrivateAreaMixin, View):
            def get(self, request, *args, **kwargs):
                return HttpResponse()

        view = AView.as_view()

        # Test redirect for anonymous users
        request = self.factory.get("/")
        request.user = AnonymousUser()
        response = view(request)
        self.assertEqual(response.status_code, 302)
        self.assertEqual("/accounts/login/?next=/", response.url)

        # Test authenticated user and cache control headers
        request = self.factory.get("/")
        request.user = self.user
        response = view(request)
        self.assertEqual(response.status_code, 200)
        self.assertIn("Cache-Control", response.headers)
        self.assertEqual(
            response.headers["Cache-Control"],
            "max-age=0, no-cache, no-store, must-revalidate, private",
        )
Enter fullscreen mode Exit fullscreen mode

🔥 Best Practice Tip:
Using @never_cache with a mixin like PrivateAreaMixin makes your code reusable and clean. Combined with rigorous testing, this ensures your sensitive views remain secure and aligned with best practices.

How do you handle caching and sensitive data in your Django projects?

Update:
I’m excited to share that I’ve developed a new package, django-never-cache, inspired by the concepts expressed in this post.

This lightweight package simplifies applying Cache-Control rules to sensitive views by providing reusable utilities like mixins, middlewares and decorators. It is designed to make securing Django applications easier.

Check it out on GitHub: https://github.com/trottomv/django-never-cache

Feel free to try it out, share feedback, or even contribute to its development. 🚀

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (0)

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay