diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index ff7873b..9de656c 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -884,6 +884,7 @@ class ModelAdmin(BaseModelAdmin):
             post_url = reverse('admin:%s_%s_changelist' %
                                (opts.app_label, opts.model_name),
                                current_app=self.admin_site.name)
+            post_url += self.changelist_redirect_querystring(request)
         else:
             post_url = reverse('admin:index',
                                current_app=self.admin_site.name)
@@ -956,6 +957,13 @@ class ModelAdmin(BaseModelAdmin):
             self.message_user(request, msg)
             return None
 
+    def changelist_redirect_querystring(self, request):
+        return request.session.get(self.changelist_redirect_session_key(request), '')
+
+    def changelist_redirect_session_key(self, request):
+        opts = self.model._meta
+        return 'admin_%s_%s_changelist_querystring' % (opts.app_label, opts.module_name)
+
     @csrf_protect_m
     @transaction.commit_on_success
     def add_view(self, request, form_url='', extra_context=None):
@@ -1282,6 +1290,12 @@ class ModelAdmin(BaseModelAdmin):
         }
         context.update(extra_context or {})
 
+        if request.method == 'GET' and not cl.is_popup:
+            # Store the querystring in the session so we can return to the
+            # same filtered changelist later, for example after an object has
+            # been saved or deleted.
+            request.session[self.changelist_redirect_session_key(request)] = cl.get_query_string()
+
         return TemplateResponse(request, self.change_list_template or [
             'admin/%s/%s/change_list.html' % (app_label, opts.model_name),
             'admin/%s/change_list.html' % app_label,
@@ -1322,9 +1336,11 @@ class ModelAdmin(BaseModelAdmin):
             if not self.has_change_permission(request, None):
                 return HttpResponseRedirect(reverse('admin:index',
                                                     current_app=self.admin_site.name))
-            return HttpResponseRedirect(reverse('admin:%s_%s_changelist' %
+            url = (reverse('admin:%s_%s_changelist' %
                                         (opts.app_label, opts.model_name),
                                         current_app=self.admin_site.name))
+            url += self.changelist_redirect_querystring(request)
+            return HttpResponseRedirect(url)
 
         object_name = force_text(opts.verbose_name)
 
diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py
index 7a3a5c0..6c98194 100644
--- a/tests/regressiontests/admin_changelist/tests.py
+++ b/tests/regressiontests/admin_changelist/tests.py
@@ -35,6 +35,7 @@ class ChangeListTests(TestCase):
     def _mocked_authenticated_request(self, url, user):
         request = self.factory.get(url)
         request.user = user
+        request.session = {}
         return request
 
     def test_select_related_preserved(self):
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index d3cfaa3..a605841 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -4015,3 +4015,103 @@ class AdminUserMessageTest(TestCase):
         self.assertContains(response,
                             '<li class="extra_tag info">Test tags</li>',
                             html=True)
+
+
+@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
+class AdminChangeListRedirectionTests(TestCase):
+    urls = "regressiontests.admin_views.urls"
+    fixtures = ['admin-views-users']
+
+    def setUp(self):
+        Person.objects.create(name="Chris", gender=1, alive=True, age=25)
+        Person.objects.create(name="Jack", gender=2, alive=False, age=47)
+        Person.objects.create(name="Bob", gender=1, alive=True, age=36)
+
+        self.client.login(username='super', password='secret')
+
+    def get_data(self):
+        return {
+            'name': 'Joe', 'gender': '1', 'alive': '1', 'age': '58'
+        }
+
+    def tearDown(self):
+        self.client.logout()
+
+    def get_filtered_changelist_url(self):
+        querystring = '?' + urlencode({
+                'alive': 1,
+            })
+        return "%s%s" % (reverse('admin:admin_views_person_changelist'), querystring)
+
+    def test_change_redirect(self):
+        """
+        Ensure that we're correctly redirected to the filtered changelist
+        after an existing object is saved.
+        Refs #6903.
+        """
+        # First open the changelist view with filters
+        self.client.get(self.get_filtered_changelist_url())
+
+        # Save an object
+        data = self.get_data()
+        data['_save'] = 1
+        person = Person.objects.get(name='Chris')
+        response = self.client.post(reverse('admin:admin_views_person_change', args=(person.pk,)), data)
+
+        # Check that we return to the filtered changelist
+        self.assertRedirects(response, self.get_filtered_changelist_url())
+
+    def test_change_popup_redirect(self):
+        """
+        Ensure that we're correctly redirected to the filtered changelist
+        after an existing object is saved, even if the changelist was opened
+        in a popup in the meantime.
+        Refs #6903.
+        """
+        # First open the changelist view with filters
+        self.client.get(self.get_filtered_changelist_url())
+
+        # Then open the non-filtered changelist view in a popup
+        self.client.get('%s?%s' % (reverse('admin:admin_views_person_changelist'), IS_POPUP_VAR))
+
+        # Save an object
+        data = self.get_data()
+        data['_save'] = 1
+        person = Person.objects.get(name='Chris')
+        response = self.client.post(reverse('admin:admin_views_person_change', args=(person.pk,)), data)
+
+        # Check that we return to the filtered changelist
+        self.assertRedirects(response, self.get_filtered_changelist_url())
+
+    def test_delete_redirect(self):
+        """
+        Ensure that we're correctly redirected to the filtered changelist
+        after an existing object is deleted.
+        Refs #6903.
+        """
+        # First open the changelist view with filters
+        self.client.get(self.get_filtered_changelist_url())
+
+        # Delete an object
+        person = Person.objects.get(name='Chris')
+        response = self.client.post(reverse('admin:admin_views_person_delete', args=(person.pk,)), {'post': 'yes'})
+
+        # Check that we return to the filtered changelist
+        self.assertRedirects(response, self.get_filtered_changelist_url())
+
+    def test_add_redirect(self):
+        """
+        Ensure that we're correctly redirected to the non-filtered changelist
+        after a new object is added.
+        Refs #6903.
+        """
+        # First open the changelist view with filters
+        self.client.get(self.get_filtered_changelist_url())
+
+        # Add a new object
+        data = self.get_data()
+        data['_save'] = 1
+        response = self.client.post(reverse('admin:admin_views_person_add'), data)
+
+        # Check that we return to the non-filtered changelist
+        self.assertRedirects(response, reverse('admin:admin_views_person_changelist'))
