diff --git a/djangoldp/models.py b/djangoldp/models.py
index e4670091da1a77f635927d1e5a5a30c99f4a61a6..5a9bc2d40bee53a03e61a627b885eda831a1b99e 100644
--- a/djangoldp/models.py
+++ b/djangoldp/models.py
@@ -9,7 +9,7 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError
 from django.db import models
 from django.db.models import BinaryField, DateTimeField
 from django.db.models.base import ModelBase
-from django.db.models.signals import post_save, pre_save
+from django.db.models.signals import post_save, pre_save, pre_delete, m2m_changed
 from django.dispatch import receiver
 from django.urls import reverse_lazy, get_resolver, NoReverseMatch
 from django.utils.datastructures import MultiValueDictKeyError
@@ -391,10 +391,11 @@ if 'djangoldp_account' not in settings.DJANGOLDP_PACKAGES:
     get_user_model().webid = webid
 
 
-@receiver(pre_save)
+@receiver([pre_save, pre_delete, m2m_changed])
 def invalidate_caches(instance, **kwargs):
-    if isinstance(instance, Model):
-        from djangoldp.serializers import LDListMixin, LDPSerializer
-        LDPPermissions.invalidate_cache()
-        LDListMixin.to_representation_cache.reset()
-        LDPSerializer.to_representation_cache.reset()
+    from djangoldp.serializers import LDListMixin, LDPSerializer
+    LDPPermissions.invalidate_cache()
+    LDListMixin.to_representation_cache.reset()
+
+    if hasattr(instance, 'urlid'):
+        LDPSerializer.to_representation_cache.invalidate(instance.urlid)
diff --git a/djangoldp/tests/djangoldp_urls.py b/djangoldp/tests/djangoldp_urls.py
index 2d43fc3dbd7715089a2a5ecd29d69bc29aa0c5cc..05d28e6fc0f28ed393673b56ea74697bb39e6c26 100644
--- a/djangoldp/tests/djangoldp_urls.py
+++ b/djangoldp/tests/djangoldp_urls.py
@@ -6,7 +6,7 @@ from djangoldp.views import LDPViewSet
 
 urlpatterns = [
     re_path(r'^messages/', LDPViewSet.urls(model=Message, permission_classes=[LDPPermissions], fields=["@id", "text", "conversation"], nested_fields=['conversation'])),
-    re_path(r'^conversations/', LDPViewSet.urls(model=Conversation, nested_fields=["message_set"], permission_classes=[LDPPermissions])),
+    re_path(r'^conversations/', LDPViewSet.urls(model=Conversation, nested_fields=["message_set", "observers"], permission_classes=[LDPPermissions])),
     re_path(r'^tasks/', LDPViewSet.urls(model=Task, permission_classes=[LDPPermissions])),
     re_path(r'^dates/', LDPViewSet.urls(model=DateModel, permission_classes=[LDPPermissions])),
     re_path(r'^dummys/', LDPViewSet.urls(model=Dummy, permission_classes=[LDPPermissions], lookup_field='slug',)),
diff --git a/djangoldp/tests/models.py b/djangoldp/tests/models.py
index b576ceb2be26134498ef956ad721048b622d0893..cc349a91256e1a95ca1f457a1578849bb27314ea 100644
--- a/djangoldp/tests/models.py
+++ b/djangoldp/tests/models.py
@@ -1,15 +1,12 @@
 from django.conf import settings
 from django.contrib.auth.models import AbstractUser
 from django.db import models
-from django.db.models import BinaryField, DateField
 from django.db.models.signals import post_save
 from django.dispatch import receiver
 from django.utils.datetime_safe import date
 
-from djangoldp.fields import LDPUrlField
 from djangoldp.models import Model
 from djangoldp.permissions import LDPPermissions
-from djangoldp.tests.permissions import HalfRandomPermissions
 
 
 class User(AbstractUser, Model):
@@ -65,6 +62,7 @@ class Conversation(models.Model):
     author_user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
     peer_user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="peers_conv",
                                   on_delete=models.DO_NOTHING)
+    observers = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='observed_conversations')
 
     class Meta(Model.Meta):
         anonymous_perms = ['view']
diff --git a/djangoldp/tests/settings_default.py b/djangoldp/tests/settings_default.py
index 2634324cb0ab0b1b711d5f985156b7d03695d69b..6773c2960879de046798131d2b78b80ffbeaf4fd 100644
--- a/djangoldp/tests/settings_default.py
+++ b/djangoldp/tests/settings_default.py
@@ -93,4 +93,4 @@ LDP_RDF_CONTEXT={
 }
 SEND_BACKLINKS=False
 GUARDIAN_AUTO_PREFETCH = True
-SERIALIZER_CACHE = False
+SERIALIZER_CACHE = True
diff --git a/djangoldp/tests/tests_cache.py b/djangoldp/tests/tests_cache.py
index ff4aed657bae7be0b803a4b863e2add45fb52d56..5fef5e7e7a2bf5260f38a12fca9dff549c5d1912 100644
--- a/djangoldp/tests/tests_cache.py
+++ b/djangoldp/tests/tests_cache.py
@@ -1,11 +1,10 @@
 from django.contrib.auth import get_user_model
-from django.test import TestCase
+from django.test import TestCase, override_settings
 from rest_framework.test import APIRequestFactory, APIClient
 from rest_framework.utils import json
 
-from djangoldp.models import Model
 from djangoldp.serializers import LDPSerializer, LDListMixin
-from djangoldp.tests.models import Skill, JobOffer, Invoice, LDPDummy, Resource, Post, Circle, Project, Conversation
+from djangoldp.tests.models import Conversation, Project
 
 
 class TestCache(TestCase):
@@ -22,6 +21,8 @@ class TestCache(TestCase):
     def tearDown(self):
         pass
 
+    # test container cache after new resource added
+    @override_settings(SERIALIZER_CACHE=True)
     def test_save_fk_graph_with_nested(self):
         response = self.client.get('/batchs/', content_type='application/ld+json')
         self.assertEqual(response.status_code, 200)
@@ -49,9 +50,10 @@ class TestCache(TestCase):
         self.assertEquals(response.data['ldp:contains'][0]['title'], "title")
         self.assertEquals(response.data['ldp:contains'][0]['invoice']['title'], "title 2")
 
+    # test resource cache after it is updated
+    @override_settings(SERIALIZER_CACHE=True)
     def test_update_with_new_fk_relation(self):
-        conversation = Conversation.objects.create(author_user=self.user,
-                                                   description="conversation description")
+        conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
         response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
         body = [
             {
@@ -71,3 +73,92 @@ class TestCache(TestCase):
         self.assertEquals('conversation update', response.data['description'])
         self.assertIn('@id', response.data['peer_user'])
 
+    # test container cache after member is deleted by view
+    @override_settings(SERIALIZER_CACHE=True)
+    def test_cached_container_deleted_resource_view(self):
+        conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
+        response = self.client.get('/conversations/', content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 1)
+
+        response = self.client.delete('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 204)
+
+        response = self.client.get('/conversations/', content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 0)
+
+    # test container cache after member is deleted manually
+    @override_settings(SERIALIZER_CACHE=True)
+    def test_cached_container_deleted_resource_manual(self):
+        conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
+        response = self.client.get('/conversations/', content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 1)
+
+        conversation.delete()
+
+        response = self.client.get('/conversations/', content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 0)
+
+    # test resource cache after it is deleted manually
+    @override_settings(SERIALIZER_CACHE=True)
+    def test_cached_resource_deleted_resource_manual(self):
+        conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
+        response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+
+        conversation.delete()
+
+        response = self.client.get('/conversations/{}/'.format(conversation.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 404)
+
+    # test container cache following m2m_changed - Project (which inherits from djangoldp.models.Model)
+    @override_settings(SERIALIZER_CACHE=True)
+    def test_cached_container_m2m_changed_project(self):
+        project = Project.objects.create(description='Test')
+        response = self.client.get('/projects/{}/team/'.format(project.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 0)
+
+        project.team.add(self.user)
+        response = self.client.get('/projects/{}/team/'.format(project.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 1)
+
+        project.team.remove(self.user)
+        response = self.client.get('/projects/{}/team/'.format(project.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 0)
+
+        project.team.add(self.user)
+        project.team.clear()
+        response = self.client.get('/projects/{}/team/'.format(project.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 0)
+
+    # test container cache following m2m_changed - Conversation (which does not inherit from djangoldp.models.Model)
+    @override_settings(SERIALIZER_CACHE=True)
+    def test_cached_container_m2m_changed_conversation(self):
+        conversation = Conversation.objects.create(author_user=self.user, description="conversation description")
+        response = self.client.get('/conversations/{}/observers/'.format(conversation.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 0)
+
+        conversation.observers.add(self.user)
+        response = self.client.get('/conversations/{}/observers/'.format(conversation.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 1)
+
+        conversation.observers.remove(self.user)
+        response = self.client.get('/conversations/{}/observers/'.format(conversation.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 0)
+
+        conversation.observers.add(self.user)
+        conversation.observers.clear()
+        response = self.client.get('/conversations/{}/observers/'.format(conversation.pk), content_type='application/ld+json')
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(len(response.data['ldp:contains']), 0)
+