diff --git a/lib/tests/weblib_test.php b/lib/tests/weblib_test.php
index a13ecc12caa..5c3d4d48643 100644
--- a/lib/tests/weblib_test.php
+++ b/lib/tests/weblib_test.php
@@ -64,6 +64,56 @@ class core_weblib_testcase extends advanced_testcase {
         $CFG->formatstringstriptags = $originalformatstringstriptags;
     }
 
+    /**
+     * The format string static caching should include the filters option to make
+     * sure filters are correctly applied when requested.
+     */
+    public function test_format_string_static_caching_with_filters() {
+        global $CFG;
+
+        $this->resetAfterTest(true);
+        $this->setAdminUser();
+        $generator = $this->getDataGenerator();
+        $course = $generator->create_course();
+        $user = $generator->create_user();
+        $rawstring = 'Shortname <a href="#">link</a> curseword';
+        $expectednofilter = strip_links($rawstring);
+        $expectedfilter = 'Shortname link \*\**';
+        $striplinks = true;
+        $context = context_course::instance($course->id);
+        $options = [
+            'context' => $context,
+            'escape' => true,
+            'filter' => false
+        ];
+
+        $this->setUser($user);
+
+        // Format the string without filters. It should just strip the
+        // links.
+        $nofilterresult = format_string($rawstring, $striplinks, $options);
+        $this->assertEquals($expectednofilter, $nofilterresult);
+
+        // Add the censor filter. Make sure it's enabled globally.
+        $CFG->filterall = true;
+        $CFG->stringfilters = 'censor';
+        $CFG->filter_censor_badwords = 'curseword';
+        filter_set_global_state('censor', TEXTFILTER_ON);
+        filter_set_local_state('censor', $context->id, TEXTFILTER_ON);
+        // This time we want to apply the filters.
+        $options['filter'] = true;
+        $filterresult = format_string($rawstring, $striplinks, $options);
+        $this->assertRegExp("/$expectedfilter/", $filterresult);
+
+        filter_set_local_state('censor', $context->id, TEXTFILTER_OFF);
+
+        // Confirm that we get back the cached string. The result should be
+        // the same as the filtered text above even though we've disabled the
+        // censor filter in between.
+        $cachedresult = format_string($rawstring, $striplinks, $options);
+        $this->assertRegExp("/$expectedfilter/", $cachedresult);
+    }
+
     public function test_s() {
         // Special cases.
         $this->assertSame('0', s(0));
diff --git a/lib/weblib.php b/lib/weblib.php
index ae480fdec60..66f5d720476 100644
--- a/lib/weblib.php
+++ b/lib/weblib.php
@@ -1441,7 +1441,9 @@ function format_string($string, $striplinks = true, $options = null) {
     }
 
     // Calculate md5.
-    $md5 = md5($string.'<+>'.$striplinks.'<+>'.$options['context']->id.'<+>'.$options['escape'].'<+>'.current_language());
+    $cachekeys = array($string, $striplinks, $options['context']->id,
+        $options['escape'], current_language(), $options['filter']);
+    $md5 = md5(implode('<+>', $cachekeys));
 
     // Fetch from cache if possible.
     if (isset($strcache[$md5])) {