diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -1725,6 +1725,7 @@
         return set_error(serror, ScriptError::EVAL_FALSE);
     }
 
+    bool segwit_exempt = false;
     // Additional validation for spend-to-script-hash transactions:
     if ((flags & SCRIPT_VERIFY_P2SH) && scriptPubKey.IsPayToScriptHash()) {
         // scriptSig must be literals-only or validation fails
@@ -1744,32 +1745,25 @@
         CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end());
         popstack(stack);
 
-        // Bail out early if SCRIPT_DISALLOW_SEGWIT_RECOVERY is not set, the
-        // redeem script is a p2sh segwit program, and it was the only item
-        // pushed onto the stack.
+        // If SCRIPT_DISALLOW_SEGWIT_RECOVERY is not set and redeem script is a
+        // p2sh segwit program, and it was the only item pushed onto the stack,
+        // then mark for special exemptions from regular script rules.
         if ((flags & SCRIPT_DISALLOW_SEGWIT_RECOVERY) == 0 && stack.empty() &&
             pubKey2.IsWitnessProgram()) {
-            // must set metricsOut for all successful returns
-
-            // Prior to activation of this flag, all transactions will count as
-            // having a sigchecks count of 0 for accounting purposes outside of
-            // VerifyScript.
-            if (!(flags & SCRIPT_REPORT_SIGCHECKS)) {
-                metrics.nSigChecks = 0;
-            }
-            metricsOut = metrics;
-            return set_success(serror);
+            segwit_exempt = true;
         }
 
-        if (!EvalScript(stack, pubKey2, flags, checker, metrics, serror)) {
-            // serror is set
-            return false;
-        }
-        if (stack.empty()) {
-            return set_error(serror, ScriptError::EVAL_FALSE);
-        }
-        if (!CastToBool(stack.back())) {
-            return set_error(serror, ScriptError::EVAL_FALSE);
+        if (!segwit_exempt) {
+            if (!EvalScript(stack, pubKey2, flags, checker, metrics, serror)) {
+                // serror is set
+                return false;
+            }
+            if (stack.empty()) {
+                return set_error(serror, ScriptError::EVAL_FALSE);
+            }
+            if (!CastToBool(stack.back())) {
+                return set_error(serror, ScriptError::EVAL_FALSE);
+            }
         }
     }
 
@@ -1777,7 +1771,7 @@
     // as the non-P2SH evaluation of a P2SH script will obviously not result in
     // a clean stack (the P2SH inputs remain). The same holds for witness
     // evaluation.
-    if ((flags & SCRIPT_VERIFY_CLEANSTACK) != 0) {
+    if (!segwit_exempt && (flags & SCRIPT_VERIFY_CLEANSTACK)) {
         // Disallow CLEANSTACK without P2SH, as otherwise a switch
         // CLEANSTACK->P2SH+CLEANSTACK would be possible, which is not a
         // softfork (and P2SH should be one).
@@ -1787,7 +1781,7 @@
         }
     }
 
-    if (flags & SCRIPT_VERIFY_INPUT_SIGCHECKS) {
+    if (!segwit_exempt && (flags & SCRIPT_VERIFY_INPUT_SIGCHECKS)) {
         // This limit is intended for standard use, and is based on an
         // examination of typical and historical standard uses.
         // - allowing P2SH ECDSA multisig with compressed keys, which at an
diff --git a/src/test/sigcheckcount_tests.cpp b/src/test/sigcheckcount_tests.cpp
--- a/src/test/sigcheckcount_tests.cpp
+++ b/src/test/sigcheckcount_tests.cpp
@@ -334,8 +334,8 @@
     CHECK_VERIFYSCRIPT(CScript() << sigschnorr, CScript() << pub << OP_CHECKSIG,
                        SCRIPT_VERIFY_NONE, 1);
 
-    // Correct behaviour occurs for segwit recovery special case (which returns
-    // success from an alternative location)
+    // Correct behaviour occurs for segwit recovery special case (which used to
+    // return success from an alternative location)
     CScript swscript;
     swscript << OP_0 << std::vector<uint8_t>(20);
     CHECK_VERIFYSCRIPT(CScript() << ToByteVector(swscript),