diff --git a/CHANGES.rst b/CHANGES.rst
index bdf8c845b024bede54a5409dce231618bf0792e5..c4e3043a31dca4e8d4a0e80ab390e8d38f1933d6 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -50,6 +50,9 @@ Bugs fixed
 * abs(signed int) now returns a signed rather than unsigned int.
   (Github issue #1837)
 
+* Compile time evaluations of (partially) constant f-strings could show incorrect
+  results.
+
 Other changes
 -------------
 
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py
index f0ea3f9802b0852413d9aee1f29491ed4fea5222..f2dcdbbc09b7996fd91d13e8f2e6d97319a3ed85 100644
--- a/Cython/Compiler/ExprNodes.py
+++ b/Cython/Compiler/ExprNodes.py
@@ -3126,7 +3126,7 @@ class FormattedValueNode(ExprNode):
             self.format_spec = self.format_spec.analyse_types(env).coerce_to_pyobject(env)
         if self.c_format_spec is None:
             self.value = self.value.coerce_to_pyobject(env)
-            if not self.format_spec and not self.conversion_char:
+            if not self.format_spec and (not self.conversion_char or self.conversion_char == 's'):
                 if self.value.type is unicode_type and not self.value.may_be_none():
                     # value is definitely a unicode string and we don't format it any special
                     return self.value
diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py
index 109c49794d3720f83e802559fc9fbac4cb332a32..479165f744f39694e5ed21047ad2e24c7dffad8a 100644
--- a/Cython/Compiler/Optimize.py
+++ b/Cython/Compiler/Optimize.py
@@ -3959,10 +3959,21 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
 
     def visit_FormattedValueNode(self, node):
         self.visitchildren(node)
+        conversion_char = node.conversion_char or 's'
         if isinstance(node.format_spec, ExprNodes.UnicodeNode) and not node.format_spec.value:
             node.format_spec = None
-        if node.format_spec is None and node.conversion_char is None and isinstance(node.value, ExprNodes.UnicodeNode):
-            return node.value
+        if node.format_spec is None and isinstance(node.value, ExprNodes.IntNode):
+            value = EncodedString(node.value.value)
+            if value.isdigit():
+                return ExprNodes.UnicodeNode(node.value.pos, value=value, constant_result=value)
+        if node.format_spec is None and conversion_char == 's':
+            value = None
+            if isinstance(node.value, ExprNodes.UnicodeNode):
+                value = node.value.value
+            elif isinstance(node.value, ExprNodes.StringNode):
+                value = node.value.unicode_value
+            if value is not None:
+                return ExprNodes.UnicodeNode(node.value.pos, value=value, constant_result=value)
         return node
 
     def visit_JoinedStrNode(self, node):
@@ -3980,7 +3991,8 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
                 substrings = list(substrings)
                 unode = substrings[0]
                 if len(substrings) > 1:
-                    unode.value = EncodedString(u''.join(value.value for value in substrings))
+                    value = EncodedString(u''.join(value.value for value in substrings))
+                    unode = ExprNodes.UnicodeNode(unode.pos, value=value, constant_result=value)
                 # ignore empty Unicode strings
                 if unode.value:
                     values.append(unode)
@@ -3988,7 +4000,8 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
                 values.extend(substrings)
 
         if not values:
-            node = ExprNodes.UnicodeNode(node.pos, value=EncodedString(''))
+            value = EncodedString('')
+            node = ExprNodes.UnicodeNode(node.pos, value=value, constant_result=value)
         elif len(values) == 1:
             node = values[0]
         elif len(values) == 2:
diff --git a/tests/run/fstring.pyx b/tests/run/fstring.pyx
index b01662f91552e285961c1bde54541705bb30eb63..09f5ec496c077d901b8a4a5f1b619d7489e2f321 100644
--- a/tests/run/fstring.pyx
+++ b/tests/run/fstring.pyx
@@ -5,6 +5,8 @@
 # Cython specific PEP 498 tests in addition to test_fstring.pyx from CPython
 ####
 
+cimport cython
+
 import sys
 IS_PYPY = hasattr(sys, 'pypy_version_info')
 
@@ -18,13 +20,39 @@ max_long = LONG_MAX
 min_long = LONG_MIN
 
 
+@cython.test_fail_if_path_exists(
+    "//FormattedValueNode",
+    "//JoinedStrNode",
+    "//AddNode",
+)
 def escaping():
     """
     >>> escaping()
     """
     assert f'{{{{{"abc"}}}}}{{}}{{' == '{{abc}}{}{'
+    s = f'{{{{{"abc"}}}}}{{}}{{'
+    assert s == '{{abc}}{}{', s
+
     assert f'\x7b}}' == '{}'
+    s = f'\x7b}}'
+    assert s == '{}', s
+
     assert f'{"{{}}"}' == '{{}}'
+    s = f'{"{{}}"}'
+    assert s == '{{}}', s
+
+
+@cython.test_fail_if_path_exists(
+    "//FormattedValueNode",
+    "//JoinedStrNode",
+    "//AddNode",
+)
+def nested_constant():
+    """
+    >>> nested_constant()
+    'xyabc123321'
+    """
+    return f"""{f'''xy{f"abc{123}{'321'}"!s}'''}"""
 
 
 def format2(ab, cd):