diff --git a/lib/util/schema-utils.spec.ts b/lib/util/schema-utils.spec.ts
index f62fcc4bbffe08a3898a5ae1f455f93d5f8d1f21..9be7ac651c4489a43f87f2c15c90aca5c43e294a 100644
--- a/lib/util/schema-utils.spec.ts
+++ b/lib/util/schema-utils.spec.ts
@@ -1,5 +1,12 @@
 import { z } from 'zod';
-import { Json, Json5, LooseArray, LooseRecord, UtcDate } from './schema-utils';
+import {
+  Json,
+  Json5,
+  LooseArray,
+  LooseRecord,
+  Url,
+  UtcDate,
+} from './schema-utils';
 
 describe('util/schema-utils', () => {
   describe('LooseArray', () => {
@@ -270,4 +277,22 @@ describe('util/schema-utils', () => {
       expect(() => UtcDate.parse('foobar')).toThrow();
     });
   });
+
+  describe('Url', () => {
+    it('parses valid URLs', () => {
+      const urlStr = 'https://www.example.com/foo/bar?baz=qux';
+      const parsedUrl = Url.parse(urlStr);
+      expect(parsedUrl).toMatchObject({
+        protocol: 'https:',
+        hostname: 'www.example.com',
+        pathname: '/foo/bar',
+        search: '?baz=qux',
+      });
+    });
+
+    it('throws an error for invalid URLs', () => {
+      const urlStr = 'invalid-url-string';
+      expect(() => Url.parse(urlStr)).toThrow('Invalid URL');
+    });
+  });
 });
diff --git a/lib/util/schema-utils.ts b/lib/util/schema-utils.ts
index f3dfe108ce7a508901156887e21ceaae6d61d11f..0caa342e39164329121da32abf098b993f9a4a1c 100644
--- a/lib/util/schema-utils.ts
+++ b/lib/util/schema-utils.ts
@@ -223,3 +223,12 @@ export const UtcDate = z
     }
     return date;
   });
+
+export const Url = z.string().transform((str, ctx): URL => {
+  try {
+    return new URL(str);
+  } catch (e) {
+    ctx.addIssue({ code: 'custom', message: 'Invalid URL' });
+    return z.NEVER;
+  }
+});