diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index f8cae78b909a00..f7b4cdcda826c0 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -1078,6 +1078,14 @@ sys.monitoring
 * Two new events are added: :monitoring-event:`BRANCH_LEFT` and
   :monitoring-event:`BRANCH_RIGHT`. The ``BRANCH`` event is deprecated.
 
+
+sysconfig
+---------
+
+* Add ``ABIFLAGS`` key to :func:`sysconfig.get_config_vars` on Windows.
+  (Contributed by Xuehai Pan in :gh:`131799`.)
+
+
 threading
 ---------
 
@@ -1085,6 +1093,7 @@ threading
   to :attr:`threading.Thread.name`.
   (Contributed by Victor Stinner in :gh:`59705`.)
 
+
 tkinter
 -------
 
diff --git a/Lib/sysconfig/__init__.py b/Lib/sysconfig/__init__.py
index 18e6b8d25e5b56..dad715eb087387 100644
--- a/Lib/sysconfig/__init__.py
+++ b/Lib/sysconfig/__init__.py
@@ -401,9 +401,20 @@ def _init_non_posix(vars):
     vars['BINLIBDEST'] = get_path('platstdlib')
     vars['INCLUDEPY'] = get_path('include')
 
-    # Add EXT_SUFFIX, SOABI, and Py_GIL_DISABLED
+    # Add EXT_SUFFIX, SOABI, Py_DEBUG, and Py_GIL_DISABLED
     vars.update(_sysconfig.config_vars())
 
+    # NOTE: ABIFLAGS is only an emulated value. It is not present during build
+    #       on Windows. sys.abiflags is absent on Windows and vars['abiflags']
+    #       is already widely used to calculate paths, so it should remain an
+    #       empty string.
+    vars['ABIFLAGS'] = ''.join(
+        (
+            't' if vars['Py_GIL_DISABLED'] else '',
+            '_d' if vars['Py_DEBUG'] else '',
+        ),
+    )
+
     vars['LIBDIR'] = _safe_realpath(os.path.join(get_config_var('installed_base'), 'libs'))
     if hasattr(sys, 'dllhandle'):
         dllhandle = _winapi.GetModuleFileName(sys.dllhandle)
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index f1cfff68ad6105..44e6bdf5f00712 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -724,6 +724,8 @@ def test_attributes(self):
         self.assertIn(sys.float_repr_style, ('short', 'legacy'))
         if not sys.platform.startswith('win'):
             self.assertIsInstance(sys.abiflags, str)
+        else:
+            self.assertFalse(hasattr(sys, 'abiflags'))
 
     def test_thread_info(self):
         info = sys.thread_info
diff --git a/Lib/test/test_sysconfig.py b/Lib/test/test_sysconfig.py
index cc11eade2e3426..53e55383bf9c72 100644
--- a/Lib/test/test_sysconfig.py
+++ b/Lib/test/test_sysconfig.py
@@ -9,6 +9,7 @@
 import textwrap
 from copy import copy
 
+from test import support
 from test.support import (
     captured_stdout,
     is_android,
@@ -455,20 +456,20 @@ def test_library(self):
         library = sysconfig.get_config_var('LIBRARY')
         ldlibrary = sysconfig.get_config_var('LDLIBRARY')
         major, minor = sys.version_info[:2]
-        if sys.platform == 'win32':
-            self.assertTrue(library.startswith(f'python{major}{minor}'))
-            self.assertTrue(library.endswith('.dll'))
+        abiflags = sysconfig.get_config_var('ABIFLAGS')
+        if sys.platform.startswith('win'):
+            self.assertEqual(library, f'python{major}{minor}{abiflags}.dll')
             self.assertEqual(library, ldlibrary)
         elif is_apple_mobile:
             framework = sysconfig.get_config_var('PYTHONFRAMEWORK')
             self.assertEqual(ldlibrary, f"{framework}.framework/{framework}")
         else:
-            self.assertTrue(library.startswith(f'libpython{major}.{minor}'))
-            self.assertTrue(library.endswith('.a'))
+            self.assertStartsWith(library, f'libpython{major}.{minor}')
+            self.assertEndsWith(library, '.a')
             if sys.platform == 'darwin' and sys._framework:
                 self.skipTest('gh-110824: skip LDLIBRARY test for framework build')
             else:
-                self.assertTrue(ldlibrary.startswith(f'libpython{major}.{minor}'))
+                self.assertStartsWith(ldlibrary, f'libpython{major}.{minor}')
 
     @unittest.skipUnless(sys.platform == "darwin", "test only relevant on MacOSX")
     @requires_subprocess()
@@ -592,6 +593,63 @@ def test_osx_ext_suffix(self):
         suffix = sysconfig.get_config_var('EXT_SUFFIX')
         self.assertTrue(suffix.endswith('-darwin.so'), suffix)
 
+    def test_always_set_py_debug(self):
+        self.assertIn('Py_DEBUG', sysconfig.get_config_vars())
+        Py_DEBUG = sysconfig.get_config_var('Py_DEBUG')
+        self.assertIn(Py_DEBUG, (0, 1))
+        self.assertEqual(Py_DEBUG, support.Py_DEBUG)
+
+    def test_always_set_py_gil_disabled(self):
+        self.assertIn('Py_GIL_DISABLED', sysconfig.get_config_vars())
+        Py_GIL_DISABLED = sysconfig.get_config_var('Py_GIL_DISABLED')
+        self.assertIn(Py_GIL_DISABLED, (0, 1))
+        self.assertEqual(Py_GIL_DISABLED, support.Py_GIL_DISABLED)
+
+    def test_abiflags(self):
+        # If this test fails on some platforms, maintainers should update the
+        # test to make it pass, rather than changing the definition of ABIFLAGS.
+        self.assertIn('abiflags', sysconfig.get_config_vars())
+        self.assertIn('ABIFLAGS', sysconfig.get_config_vars())
+        abiflags = sysconfig.get_config_var('abiflags')
+        ABIFLAGS = sysconfig.get_config_var('ABIFLAGS')
+        self.assertIsInstance(abiflags, str)
+        self.assertIsInstance(ABIFLAGS, str)
+        self.assertIn(abiflags, ABIFLAGS)
+        if os.name == 'nt':
+            self.assertEqual(abiflags, '')
+
+        if not sys.platform.startswith('win'):
+            valid_abiflags = ('', 't', 'd', 'td')
+        else:
+            # Windows uses '_d' rather than 'd'; see also test_abi_debug below
+            valid_abiflags = ('', 't', '_d', 't_d')
+
+        self.assertIn(ABIFLAGS, valid_abiflags)
+
+    def test_abi_debug(self):
+        ABIFLAGS = sysconfig.get_config_var('ABIFLAGS')
+        if support.Py_DEBUG:
+            self.assertIn('d', ABIFLAGS)
+        else:
+            self.assertNotIn('d', ABIFLAGS)
+
+        # The 'd' flag should always be the last one on Windows.
+        # On Windows, the debug flag is used differently with a underscore prefix.
+        # For example, `python{X}.{Y}td` on Unix and `python{X}.{Y}t_d.exe` on Windows.
+        if support.Py_DEBUG and sys.platform.startswith('win'):
+            self.assertEndsWith(ABIFLAGS, '_d')
+
+    def test_abi_thread(self):
+        abi_thread = sysconfig.get_config_var('abi_thread')
+        ABIFLAGS = sysconfig.get_config_var('ABIFLAGS')
+        self.assertIsInstance(abi_thread, str)
+        if support.Py_GIL_DISABLED:
+            self.assertEqual(abi_thread, 't')
+            self.assertIn('t', ABIFLAGS)
+        else:
+            self.assertEqual(abi_thread, '')
+            self.assertNotIn('t', ABIFLAGS)
+
     @requires_subprocess()
     def test_makefile_overwrites_config_vars(self):
         script = textwrap.dedent("""
diff --git a/Misc/ACKS b/Misc/ACKS
index 814a530f7b79d4..c3e8530d5b36c2 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1402,6 +1402,7 @@ Todd R. Palmer
 Juan David Ibáñez Palomar
 Nicola Palumbo
 Jan Palus
+Xuehai Pan
 Yongzhi Pan
 Martin Panter
 Mathias Panzenböck
diff --git a/Misc/NEWS.d/next/Windows/2025-03-27-16-22-58.gh-issue-127405.aASs2Z.rst b/Misc/NEWS.d/next/Windows/2025-03-27-16-22-58.gh-issue-127405.aASs2Z.rst
new file mode 100644
index 00000000000000..a6d5985ff4c7b8
--- /dev/null
+++ b/Misc/NEWS.d/next/Windows/2025-03-27-16-22-58.gh-issue-127405.aASs2Z.rst
@@ -0,0 +1 @@
+Add ``ABIFLAGS`` to :func:`sysconfig.get_config_vars` on Windows. Patch by Xuehai Pan.
diff --git a/Modules/_sysconfig.c b/Modules/_sysconfig.c
index c50c5cfabc2f1f..e2d141b5910b6d 100644
--- a/Modules/_sysconfig.c
+++ b/Modules/_sysconfig.c
@@ -67,6 +67,16 @@ _sysconfig_config_vars_impl(PyObject *module)
         return NULL;
     }
 
+#ifdef Py_DEBUG
+    PyObject *py_debug = _PyLong_GetOne();
+#else
+    PyObject *py_debug = _PyLong_GetZero();
+#endif
+    if (PyDict_SetItemString(config, "Py_DEBUG", py_debug) < 0) {
+        Py_DECREF(config);
+        return NULL;
+    }
+
     return config;
 }