diff --git a/package-lock.json b/package-lock.json
index f733050b21..6c61f501db 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -30,7 +30,6 @@
         "graphql-relay": "0.10.0",
         "graphql-tag": "2.12.6",
         "intersect": "1.0.1",
-        "ip-range-check": "0.2.0",
         "jsonwebtoken": "9.0.0",
         "jwks-rsa": "2.1.5",
         "ldapjs": "2.3.3",
@@ -9303,22 +9302,6 @@
       "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
       "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
     },
-    "node_modules/ip-range-check": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/ip-range-check/-/ip-range-check-0.2.0.tgz",
-      "integrity": "sha512-oaM3l/3gHbLlt/tCWLvt0mj1qUaI+STuRFnUvARGCujK9vvU61+2JsDpmkMzR4VsJhuFXWWgeKKVnwwoFfzCqw==",
-      "dependencies": {
-        "ipaddr.js": "^1.0.1"
-      }
-    },
-    "node_modules/ipaddr.js": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
-      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
-      "engines": {
-        "node": ">= 0.10"
-      }
-    },
     "node_modules/is-accessor-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -16837,6 +16820,14 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/proxy-addr/node_modules/ipaddr.js": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
     "node_modules/ps-node": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/ps-node/-/ps-node-0.1.6.tgz",
@@ -27668,19 +27659,6 @@
       "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
       "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
     },
-    "ip-range-check": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/ip-range-check/-/ip-range-check-0.2.0.tgz",
-      "integrity": "sha512-oaM3l/3gHbLlt/tCWLvt0mj1qUaI+STuRFnUvARGCujK9vvU61+2JsDpmkMzR4VsJhuFXWWgeKKVnwwoFfzCqw==",
-      "requires": {
-        "ipaddr.js": "^1.0.1"
-      }
-    },
-    "ipaddr.js": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
-      "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
-    },
     "is-accessor-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -33335,6 +33313,13 @@
       "requires": {
         "forwarded": "0.2.0",
         "ipaddr.js": "1.9.1"
+      },
+      "dependencies": {
+        "ipaddr.js": {
+          "version": "1.9.1",
+          "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+          "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
+        }
       }
     },
     "ps-node": {
diff --git a/package.json b/package.json
index 6f7d8cd6aa..871a0a95a2 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,6 @@
     "graphql-relay": "0.10.0",
     "graphql-tag": "2.12.6",
     "intersect": "1.0.1",
-    "ip-range-check": "0.2.0",
     "jsonwebtoken": "9.0.0",
     "jwks-rsa": "2.1.5",
     "ldapjs": "2.3.3",
diff --git a/spec/Middlewares.spec.js b/spec/Middlewares.spec.js
index 636e7809f9..571aa4c564 100644
--- a/spec/Middlewares.spec.js
+++ b/spec/Middlewares.spec.js
@@ -27,6 +27,7 @@ describe('middlewares', () => {
     expect(fakeReq.headers['content-type']).toEqual(undefined);
     const contentType = 'image/jpeg';
     fakeReq.body._ContentType = contentType;
+    fakeReq.ip = '127.0.0.1';
     middlewares.handleParseHeaders(fakeReq, fakeRes, () => {
       expect(fakeReq.headers['content-type']).toEqual(contentType);
       expect(fakeReq.body._ContentType).toEqual(undefined);
@@ -151,7 +152,92 @@ describe('middlewares', () => {
     );
   });
 
-  it('should not succeed if the ip does not belong to masterKeyIps list', async () => {
+  it('should match address', () => {
+    const ipv6 = '2001:0db8:85a3:0000:0000:8a2e:0370:7334';
+    const anotherIpv6 = '::ffff:101.10.0.1';
+    const ipv4 = '192.168.0.101';
+    const localhostV6 = '::1';
+    const localhostV62 = '::ffff:127.0.0.1';
+    const localhostV4 = '127.0.0.1';
+
+    const v6 = [ipv6, anotherIpv6];
+    v6.forEach(ip => {
+      expect(middlewares.checkIpRanges(ip, ['::/0'])).toBe(true);
+      expect(middlewares.checkIpRanges(ip, ['::'])).toBe(true);
+      expect(middlewares.checkIpRanges(ip, ['0.0.0.0'])).toBe(false);
+      expect(middlewares.checkIpRanges(ip, ['123.123.123.123'])).toBe(false);
+    });
+
+    expect(middlewares.checkIpRanges(ipv6, [anotherIpv6])).toBe(false);
+    expect(middlewares.checkIpRanges(ipv6, [ipv6])).toBe(true);
+    expect(middlewares.checkIpRanges(ipv6, ['2001:db8:85a3:0:0:8a2e:0:0/100'])).toBe(true);
+
+    expect(middlewares.checkIpRanges(ipv4, ['::'])).toBe(false);
+    expect(middlewares.checkIpRanges(ipv4, ['::/0'])).toBe(true);
+    expect(middlewares.checkIpRanges(ipv4, ['0.0.0.0'])).toBe(true);
+    expect(middlewares.checkIpRanges(ipv4, ['123.123.123.123'])).toBe(false);
+    expect(middlewares.checkIpRanges(ipv4, [ipv4])).toBe(true);
+    expect(middlewares.checkIpRanges(ipv4, ['192.168.0.0/24'])).toBe(true);
+
+    expect(middlewares.checkIpRanges(localhostV4, ['::1'])).toBe(false);
+    expect(middlewares.checkIpRanges(localhostV6, ['::1'])).toBe(true);
+    // ::ffff:127.0.0.1 is a padded ipv4 address but not ::1
+    expect(middlewares.checkIpRanges(localhostV62, ['::1'])).toBe(false);
+    // ::ffff:127.0.0.1 is a padded ipv4 address and is a match for  127.0.0.1
+    expect(middlewares.checkIpRanges(localhostV62, ['127.0.0.1'])).toBe(true);
+  });
+
+  it('can allow all with masterKeyIPs', async () => {
+    const combinations = [
+      {
+        masterKeyIps: ['::/0'],
+        ips: ['::ffff:192.168.0.101', '192.168.0.101'],
+        id: 'allowAllIpV6',
+      },
+      {
+        masterKeyIps: ['0.0.0.0'],
+        ips: ['192.168.0.101'],
+        id: 'allowAllIpV4',
+      },
+    ];
+    for (const combination of combinations) {
+      AppCache.put(combination.id, {
+        masterKey: 'masterKey',
+        masterKeyIps: combination.masterKeyIps,
+      });
+      await new Promise(resolve => setTimeout(resolve, 10));
+      for (const ip of combination.ips) {
+        fakeReq = {
+          originalUrl: 'http://example.com/parse/',
+          url: 'http://example.com/',
+          body: {
+            _ApplicationId: combination.id,
+          },
+          headers: {},
+          get: key => {
+            return fakeReq.headers[key.toLowerCase()];
+          },
+        };
+        fakeReq.ip = ip;
+        fakeReq.headers['x-parse-master-key'] = 'masterKey';
+        await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
+        expect(fakeReq.auth.isMaster).toBe(true);
+      }
+    }
+  });
+
+  it('can allow localhost with masterKeyIPs', async () => {
+    AppCache.put(fakeReq.body._ApplicationId, {
+      masterKey: 'masterKey',
+      masterKeyIps: ['::'],
+    });
+    fakeReq.ip = '::ffff:127.0.0.1';
+    fakeReq.headers['x-parse-master-key'] = 'masterKey';
+    await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
+    expect(fakeReq.auth.isMaster).toBe(true);
+  });
+
+  it('should not succeed if the ip does not belong to masterKeyIps list (ipv4)', async () => {
     AppCache.put(fakeReq.body._ApplicationId, {
       masterKey: 'masterKey',
       masterKeyIps: ['10.0.0.1'],
@@ -162,6 +248,17 @@ describe('middlewares', () => {
     expect(fakeReq.auth.isMaster).toBe(false);
   });
 
+  it('should not succeed if the ip does not belong to masterKeyIps list (ipv6)', async () => {
+    AppCache.put(fakeReq.body._ApplicationId, {
+      masterKey: 'masterKey',
+      masterKeyIps: ['::1'],
+    });
+    fakeReq.ip = '::ffff:101.10.0.1';
+    fakeReq.headers['x-parse-master-key'] = 'masterKey';
+    await new Promise(resolve => middlewares.handleParseHeaders(fakeReq, fakeRes, resolve));
+    expect(fakeReq.auth.isMaster).toBe(false);
+  });
+
   it('should not succeed if the ip does not belong to maintenanceKeyIps list', async () => {
     const logger = require('../lib/logger').logger;
     spyOn(logger, 'error').and.callFake(() => {});
diff --git a/spec/index.spec.js b/spec/index.spec.js
index 66654aaec4..6c20f4d487 100644
--- a/spec/index.spec.js
+++ b/spec/index.spec.js
@@ -450,13 +450,12 @@ describe('server', () => {
     reconfigureServer({ revokeSessionOnPasswordReset: 'non-bool' }).catch(done);
   });
 
-  it('fails if you provides invalid ip in masterKeyIps', done => {
-    reconfigureServer({ masterKeyIps: ['invalidIp', '1.2.3.4'] }).catch(error => {
-      expect(error).toEqual(
-        'The Parse Server option "masterKeyIps" contains an invalid IP address "invalidIp".'
-      );
-      done();
-    });
+  it('fails if you provides invalid ip in masterKeyIps', async () => {
+    await expectAsync(
+      reconfigureServer({ masterKeyIps: ['1.2.3.4/0', 'invalidIp'] })
+    ).toBeRejectedWith(
+      'The Parse Server option "masterKeyIps" contains an invalid IP address "invalidIp".'
+    );
   });
 
   it('should succeed if you provide valid ip in masterKeyIps', done => {
diff --git a/src/middlewares.js b/src/middlewares.js
index a7e309b0cc..a467ec7a4d 100644
--- a/src/middlewares.js
+++ b/src/middlewares.js
@@ -10,9 +10,9 @@ import PostgresStorageAdapter from './Adapters/Storage/Postgres/PostgresStorageA
 import rateLimit from 'express-rate-limit';
 import { RateLimitOptions } from './Options/Definitions';
 import { pathToRegexp } from 'path-to-regexp';
-import ipRangeCheck from 'ip-range-check';
 import RedisStore from 'rate-limit-redis';
 import { createClient } from 'redis';
+import { BlockList, isIPv4 } from 'net';
 
 export const DEFAULT_ALLOWED_HEADERS =
   'X-Parse-Master-Key, X-Parse-REST-API-Key, X-Parse-Javascript-Key, X-Parse-Application-Id, X-Parse-Client-Version, X-Parse-Session-Token, X-Requested-With, X-Parse-Revocable-Session, X-Parse-Request-Id, Content-Type, Pragma, Cache-Control';
@@ -23,6 +23,27 @@ const getMountForRequest = function (req) {
   return req.protocol + '://' + req.get('host') + mountPath;
 };
 
+export const checkIpRanges = (ip, ranges = []) => {
+  const getType = address => (isIPv4(address) ? 'ipv4' : 'ipv6');
+  const clientType = getType(ip);
+  const blocklist = new BlockList();
+  for (const range of ranges) {
+    if ((range === '::/0' || range === '::') && clientType === 'ipv6') {
+      return true;
+    }
+    if (range === '0.0.0.0' && clientType === 'ipv4') {
+      return true;
+    }
+    const [addr, prefix] = range.split('/');
+    if (prefix) {
+      blocklist.addSubnet(addr, Number(prefix), getType(addr));
+    } else {
+      blocklist.addAddress(addr, getType(addr));
+    }
+  }
+  return blocklist.check(ip, clientType);
+};
+
 // Checks that the request is authorized for this app and checks user
 // auth too.
 // The bodyparser should run before this middleware.
@@ -183,7 +204,7 @@ export function handleParseHeaders(req, res, next) {
   const isMaintenance =
     req.config.maintenanceKey && info.maintenanceKey === req.config.maintenanceKey;
   if (isMaintenance) {
-    if (ipRangeCheck(clientIp, req.config.maintenanceKeyIps || [])) {
+    if (checkIpRanges(clientIp, req.config.maintenanceKeyIps)) {
       req.auth = new auth.Auth({
         config: req.config,
         installationId: info.installationId,
@@ -199,7 +220,7 @@ export function handleParseHeaders(req, res, next) {
   }
 
   let isMaster = info.masterKey === req.config.masterKey;
-  if (isMaster && !ipRangeCheck(clientIp, req.config.masterKeyIps || [])) {
+  if (isMaster && !checkIpRanges(clientIp, req.config.masterKeyIps)) {
     const log = req.config?.loggerController || defaultLogger;
     log.error(
       `Request using master key rejected as the request IP address '${clientIp}' is not set in Parse Server option 'masterKeyIps'.`