diff --git a/.eslintrc.js b/.eslintrc.js
index f5100fd3cc91b8..be332590ba7260 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -349,6 +349,7 @@ module.exports = {
     BigInt: 'readable',
     BigInt64Array: 'readable',
     BigUint64Array: 'readable',
+    DOMException: 'readable',
     Event: 'readable',
     EventTarget: 'readable',
     MessageChannel: 'readable',
diff --git a/doc/api/globals.md b/doc/api/globals.md
index 8ce4ff1942be27..2794009ffbd89a 100644
--- a/doc/api/globals.md
+++ b/doc/api/globals.md
@@ -380,6 +380,15 @@ added: v0.0.1
 
 [`setTimeout`][] is described in the [timers][] section.
 
+## `DOMException`
+<!-- YAML
+added: REPLACEME
+-->
+
+<!-- type=global -->
+
+The WHATWG `DOMException` class. See [`DOMException`][] for more details.
+
 ## `TextDecoder`
 <!-- YAML
 added: v11.0.0
@@ -430,6 +439,7 @@ The object that acts as the namespace for all W3C
 [Mozilla Developer Network][webassembly-mdn] for usage and compatibility.
 
 [`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
+[`DOMException`]: https://developer.mozilla.org/en-US/docs/Web/API/DOMException
 [`EventTarget` and `Event` API]: events.md#event-target-and-event-api
 [`MessageChannel`]: worker_threads.md#worker_threads_class_messagechannel
 [`MessageEvent`]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/MessageEvent
diff --git a/lib/.eslintrc.yaml b/lib/.eslintrc.yaml
index 38ca7683247b8c..49013e1f8ee8b5 100644
--- a/lib/.eslintrc.yaml
+++ b/lib/.eslintrc.yaml
@@ -37,6 +37,8 @@ rules:
       message: "Use `const { Atomics } = globalThis;` instead of the global."
     - name: Buffer
       message: "Use `const { Buffer } = require('buffer');` instead of the global."
+    - name: DOMException
+      message: "Use lazy function `const { lazyDOMException } = require('internal/util');` instead of the global."
     - name: Event
       message: "Use `const { Event } = require('internal/event_target');` instead of the global."
     - name: EventTarget
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
index 58f7396990dddb..ab63dd0604ea41 100644
--- a/lib/internal/bootstrap/node.js
+++ b/lib/internal/bootstrap/node.js
@@ -52,7 +52,7 @@ const {
   globalThis,
 } = primordials;
 const config = internalBinding('config');
-const { deprecate } = require('internal/util');
+const { deprecate, lazyDOMExceptionClass } = require('internal/util');
 
 setupProcessObject();
 
@@ -201,6 +201,12 @@ if (!config.noBrowserGlobals) {
   exposeInterface(globalThis, 'URL', URL);
   // https://url.spec.whatwg.org/#urlsearchparams
   exposeInterface(globalThis, 'URLSearchParams', URLSearchParams);
+  exposeGetterAndSetter(globalThis,
+                        'DOMException',
+                        lazyDOMExceptionClass,
+                        (value) => {
+                          exposeInterface(globalThis, 'DOMException', value);
+                        });
 
   const {
     TextEncoder, TextDecoder
@@ -483,6 +489,15 @@ function exposeInterface(target, name, interfaceObject) {
   });
 }
 
+function exposeGetterAndSetter(target, name, getter, setter = undefined) {
+  ObjectDefineProperty(target, name, {
+    enumerable: false,
+    configurable: true,
+    get: getter,
+    set: setter,
+  });
+}
+
 // https://heycam.github.io/webidl/#define-the-operations
 function defineOperation(target, name, method) {
   ObjectDefineProperty(target, name, {
diff --git a/lib/internal/util.js b/lib/internal/util.js
index 9158fc8e52431e..4fdf5171cb9976 100644
--- a/lib/internal/util.js
+++ b/lib/internal/util.js
@@ -442,11 +442,15 @@ function createDeferredPromise() {
   return { promise, resolve, reject };
 }
 
-let DOMException;
+let _DOMException;
+const lazyDOMExceptionClass = () => {
+  _DOMException ??= internalBinding('messaging').DOMException;
+  return _DOMException;
+};
+
 const lazyDOMException = hideStackFrames((message, name) => {
-  if (DOMException === undefined)
-    DOMException = internalBinding('messaging').DOMException;
-  return new DOMException(message, name);
+  _DOMException ??= internalBinding('messaging').DOMException;
+  return new _DOMException(message, name);
 });
 
 function structuredClone(value) {
@@ -481,6 +485,7 @@ module.exports = {
   isInsideNodeModules,
   join,
   lazyDOMException,
+  lazyDOMExceptionClass,
   normalizeEncoding,
   once,
   promisify,
diff --git a/test/parallel/test-global-domexception.js b/test/parallel/test-global-domexception.js
new file mode 100644
index 00000000000000..d19b5a5e4f259d
--- /dev/null
+++ b/test/parallel/test-global-domexception.js
@@ -0,0 +1,11 @@
+'use strict';
+
+require('../common');
+
+const assert = require('assert');
+
+assert.strictEqual(typeof DOMException, 'function');
+
+assert.throws(() => {
+  atob('我要抛错!');
+}, DOMException);
diff --git a/test/wpt/test-atob.js b/test/wpt/test-atob.js
index 51fa27e36d3f2f..8dc9c20303a7dc 100644
--- a/test/wpt/test-atob.js
+++ b/test/wpt/test-atob.js
@@ -12,8 +12,6 @@ runner.setFlags(['--expose-internals']);
 runner.setInitScript(`
   const { internalBinding } = require('internal/test/binding');
   const { atob, btoa } = require('buffer');
-  const { DOMException } = internalBinding('messaging');
-  global.DOMException = DOMException;
 `);
 
 runner.runJsTests();
diff --git a/test/wpt/test-url.js b/test/wpt/test-url.js
index 4652bfd880cd76..c1cb3e4f850c4e 100644
--- a/test/wpt/test-url.js
+++ b/test/wpt/test-url.js
@@ -5,14 +5,4 @@ const { WPTRunner } = require('../common/wpt');
 
 const runner = new WPTRunner('url');
 
-// Needed to access to DOMException.
-runner.setFlags(['--expose-internals']);
-
-// DOMException is needed by urlsearchparams-constructor.any.js
-runner.setInitScript(`
-  const { internalBinding } = require('internal/test/binding');
-  const { DOMException } = internalBinding('messaging');
-  global.DOMException = DOMException;
-`);
-
 runner.runJsTests();