diff --git a/composer.json b/composer.json
index b9b34b9..da1bdf5 100644
--- a/composer.json
+++ b/composer.json
@@ -35,7 +35,7 @@
     "ext-openssl": "*",
     "guzzlehttp/guzzle": "^7.4.5",
     "web-token/jwt-library": "^3.3.0",
-    "spomky-labs/base64url": "^2.0.4"
+    "paragonie/constant_time_encoding": "^2.6"
   },
   "suggest": {
     "ext-bcmath": "Optional for performance.",
@@ -51,4 +51,4 @@
       "Minishlink\\WebPush\\": "src"
     }
   }
-}
\ No newline at end of file
+}
diff --git a/src/Encryption.php b/src/Encryption.php
index e6bb708..86868dc 100644
--- a/src/Encryption.php
+++ b/src/Encryption.php
@@ -13,10 +13,10 @@
 
 namespace Minishlink\WebPush;
 
-use Base64Url\Base64Url;
 use Jose\Component\Core\JWK;
 use Jose\Component\Core\Util\Ecc\PrivateKey;
 use Jose\Component\Core\Util\ECKey;
+use ParagonIE\ConstantTime\Base64UrlSafe;
 
 class Encryption
 {
@@ -66,8 +66,8 @@ public static function encrypt(string $payload, string $userPublicKey, string $u
      */
     public static function deterministicEncrypt(string $payload, string $userPublicKey, string $userAuthToken, string $contentEncoding, array $localKeyObject, string $salt): array
     {
-        $userPublicKey = Base64Url::decode($userPublicKey);
-        $userAuthToken = Base64Url::decode($userAuthToken);
+        $userPublicKey = Base64UrlSafe::decodeNoPadding($userPublicKey);
+        $userAuthToken = Base64UrlSafe::decodeNoPadding($userAuthToken);
 
         // get local key pair
         if (count($localKeyObject) === 1) {
@@ -81,9 +81,9 @@ public static function deterministicEncrypt(string $payload, string $userPublicK
             $localJwk = new JWK([
                 'kty' => 'EC',
                 'crv' => 'P-256',
-                'd' => Base64Url::encode($localPrivateKeyObject->getSecret()->toBytes(false)),
-                'x' => Base64Url::encode($localPublicKeyObject[0]),
-                'y' => Base64Url::encode($localPublicKeyObject[1]),
+                'd' => Base64UrlSafe::encodeUnpadded($localPrivateKeyObject->getSecret()->toBytes(false)),
+                'x' => Base64UrlSafe::encodeUnpadded($localPublicKeyObject[0]),
+                'y' => Base64UrlSafe::encodeUnpadded($localPublicKeyObject[1]),
             ]);
         }
         if (!$localPublicKey) {
@@ -95,8 +95,8 @@ public static function deterministicEncrypt(string $payload, string $userPublicK
         $userJwk = new JWK([
             'kty' => 'EC',
             'crv' => 'P-256',
-            'x' => Base64Url::encode($userPublicKeyObjectX),
-            'y' => Base64Url::encode($userPublicKeyObjectY),
+            'x' => Base64UrlSafe::encodeUnpadded($userPublicKeyObjectX),
+            'y' => Base64UrlSafe::encodeUnpadded($userPublicKeyObjectY),
         ]);
 
         // get shared secret from user public key and local private key
@@ -252,9 +252,9 @@ private static function createLocalKeyObject(): array
             new JWK([
                 'kty' => 'EC',
                 'crv' => 'P-256',
-                'x' => Base64Url::encode(self::addNullPadding($details['ec']['x'])),
-                'y' => Base64Url::encode(self::addNullPadding($details['ec']['y'])),
-                'd' => Base64Url::encode(self::addNullPadding($details['ec']['d'])),
+                'x' => Base64UrlSafe::encodeUnpadded(self::addNullPadding($details['ec']['x'])),
+                'y' => Base64UrlSafe::encodeUnpadded(self::addNullPadding($details['ec']['y'])),
+                'd' => Base64UrlSafe::encodeUnpadded(self::addNullPadding($details['ec']['d'])),
             ]),
         ];
     }
diff --git a/src/Utils.php b/src/Utils.php
index 887acb0..c34572e 100644
--- a/src/Utils.php
+++ b/src/Utils.php
@@ -13,9 +13,9 @@
 
 namespace Minishlink\WebPush;
 
-use Base64Url\Base64Url;
 use Jose\Component\Core\JWK;
 use Jose\Component\Core\Util\Ecc\PublicKey;
+use ParagonIE\ConstantTime\Base64UrlSafe;
 
 class Utils
 {
@@ -37,8 +37,8 @@ public static function serializePublicKey(PublicKey $publicKey): string
     public static function serializePublicKeyFromJWK(JWK $jwk): string
     {
         $hexString = '04';
-        $hexString .= str_pad(bin2hex(Base64Url::decode($jwk->get('x'))), 64, '0', STR_PAD_LEFT);
-        $hexString .= str_pad(bin2hex(Base64Url::decode($jwk->get('y'))), 64, '0', STR_PAD_LEFT);
+        $hexString .= str_pad(bin2hex(Base64UrlSafe::decodeNoPadding($jwk->get('x'))), 64, '0', STR_PAD_LEFT);
+        $hexString .= str_pad(bin2hex(Base64UrlSafe::decodeNoPadding($jwk->get('y'))), 64, '0', STR_PAD_LEFT);
 
         return $hexString;
     }
diff --git a/src/VAPID.php b/src/VAPID.php
index 5a40cd9..4ba3de9 100644
--- a/src/VAPID.php
+++ b/src/VAPID.php
@@ -13,13 +13,13 @@
 
 namespace Minishlink\WebPush;
 
-use Base64Url\Base64Url;
 use Jose\Component\Core\AlgorithmManager;
 use Jose\Component\Core\JWK;
 use Jose\Component\KeyManagement\JWKFactory;
 use Jose\Component\Signature\Algorithm\ES256;
 use Jose\Component\Signature\JWSBuilder;
 use Jose\Component\Signature\Serializer\CompactSerializer;
+use ParagonIE\ConstantTime\Base64UrlSafe;
 
 class VAPID
 {
@@ -54,14 +54,14 @@ public static function validate(array $vapid): array
                 throw new \ErrorException('Failed to convert VAPID public key from hexadecimal to binary');
             }
             $vapid['publicKey'] = base64_encode($binaryPublicKey);
-            $vapid['privateKey'] = base64_encode(str_pad(Base64Url::decode($jwk->get('d')), 2 * self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
+            $vapid['privateKey'] = base64_encode(str_pad(Base64UrlSafe::decodeNoPadding($jwk->get('d')), 2 * self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
         }
 
         if (!isset($vapid['publicKey'])) {
             throw new \ErrorException('[VAPID] You must provide a public key.');
         }
 
-        $publicKey = Base64Url::decode($vapid['publicKey']);
+        $publicKey = Base64UrlSafe::decodeNoPadding($vapid['publicKey']);
 
         if (Utils::safeStrlen($publicKey) !== self::PUBLIC_KEY_LENGTH) {
             throw new \ErrorException('[VAPID] Public key should be 65 bytes long when decoded.');
@@ -71,7 +71,7 @@ public static function validate(array $vapid): array
             throw new \ErrorException('[VAPID] You must provide a private key.');
         }
 
-        $privateKey = Base64Url::decode($vapid['privateKey']);
+        $privateKey = Base64UrlSafe::decodeNoPadding($vapid['privateKey']);
 
         if (Utils::safeStrlen($privateKey) !== self::PRIVATE_KEY_LENGTH) {
             throw new \ErrorException('[VAPID] Private key should be 32 bytes long when decoded.');
@@ -122,9 +122,9 @@ public static function getVapidHeaders(string $audience, string $subject, string
         $jwk = new JWK([
             'kty' => 'EC',
             'crv' => 'P-256',
-            'x' => Base64Url::encode($x),
-            'y' => Base64Url::encode($y),
-            'd' => Base64Url::encode($privateKey),
+            'x' => Base64UrlSafe::encodeUnpadded($x),
+            'y' => Base64UrlSafe::encodeUnpadded($y),
+            'd' => Base64UrlSafe::encodeUnpadded($privateKey),
         ]);
 
         $jwsCompactSerializer = new CompactSerializer();
@@ -136,7 +136,7 @@ public static function getVapidHeaders(string $audience, string $subject, string
             ->build();
 
         $jwt = $jwsCompactSerializer->serialize($jws, 0);
-        $encodedPublicKey = Base64Url::encode($publicKey);
+        $encodedPublicKey = Base64UrlSafe::encodeUnpadded($publicKey);
 
         if ($contentEncoding === "aesgcm") {
             return [
@@ -169,14 +169,14 @@ public static function createVapidKeys(): array
             throw new \ErrorException('Failed to convert VAPID public key from hexadecimal to binary');
         }
 
-        $binaryPrivateKey = hex2bin(str_pad(bin2hex(Base64Url::decode($jwk->get('d'))), 2 * self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
+        $binaryPrivateKey = hex2bin(str_pad(bin2hex(Base64UrlSafe::decodeNoPadding($jwk->get('d'))), 2 * self::PRIVATE_KEY_LENGTH, '0', STR_PAD_LEFT));
         if (!$binaryPrivateKey) {
             throw new \ErrorException('Failed to convert VAPID private key from hexadecimal to binary');
         }
 
         return [
-            'publicKey'  => Base64Url::encode($binaryPublicKey),
-            'privateKey' => Base64Url::encode($binaryPrivateKey),
+            'publicKey'  => Base64UrlSafe::encodeUnpadded($binaryPublicKey),
+            'privateKey' => Base64UrlSafe::encodeUnpadded($binaryPrivateKey),
         ];
     }
 }
diff --git a/src/WebPush.php b/src/WebPush.php
index 68de138..4f20faa 100644
--- a/src/WebPush.php
+++ b/src/WebPush.php
@@ -13,10 +13,10 @@
 
 namespace Minishlink\WebPush;
 
-use Base64Url\Base64Url;
 use GuzzleHttp\Client;
 use GuzzleHttp\Exception\RequestException;
 use GuzzleHttp\Psr7\Request;
+use ParagonIE\ConstantTime\Base64UrlSafe;
 use Psr\Http\Message\ResponseInterface;
 
 class WebPush
@@ -208,8 +208,8 @@ protected function prepare(array $notifications): array
                 ];
 
                 if ($contentEncoding === "aesgcm") {
-                    $headers['Encryption'] = 'salt='.Base64Url::encode($salt);
-                    $headers['Crypto-Key'] = 'dh='.Base64Url::encode($localPublicKey);
+                    $headers['Encryption'] = 'salt='.Base64UrlSafe::encodeUnpadded($salt);
+                    $headers['Crypto-Key'] = 'dh='.Base64UrlSafe::encodeUnpadded($localPublicKey);
                 }
 
                 $encryptionContentCodingHeader = Encryption::getContentCodingHeader($salt, $localPublicKey, $contentEncoding);
diff --git a/tests/EncryptionTest.php b/tests/EncryptionTest.php
index 68e1ca9..c7af076 100644
--- a/tests/EncryptionTest.php
+++ b/tests/EncryptionTest.php
@@ -8,10 +8,10 @@
  * file that was distributed with this source code.
  */
 
-use Base64Url\Base64Url;
 use Jose\Component\Core\JWK;
 use Minishlink\WebPush\Encryption;
 use Minishlink\WebPush\Utils;
+use ParagonIE\ConstantTime\Base64UrlSafe;
 use PHPUnit\Framework\Attributes\DataProvider;
 
 /**
@@ -23,30 +23,30 @@ public function testDeterministicEncrypt(): void
     {
         $contentEncoding = "aes128gcm";
         $plaintext = 'When I grow up, I want to be a watermelon';
-        $this->assertEquals('V2hlbiBJIGdyb3cgdXAsIEkgd2FudCB0byBiZSBhIHdhdGVybWVsb24', Base64Url::encode($plaintext));
+        $this->assertEquals('V2hlbiBJIGdyb3cgdXAsIEkgd2FudCB0byBiZSBhIHdhdGVybWVsb24', Base64UrlSafe::encodeUnpadded($plaintext));
 
         $payload = Encryption::padPayload($plaintext, 0, $contentEncoding);
-        $this->assertEquals('V2hlbiBJIGdyb3cgdXAsIEkgd2FudCB0byBiZSBhIHdhdGVybWVsb24C', Base64Url::encode($payload));
+        $this->assertEquals('V2hlbiBJIGdyb3cgdXAsIEkgd2FudCB0byBiZSBhIHdhdGVybWVsb24C', Base64UrlSafe::encodeUnpadded($payload));
 
         $userPublicKey = 'BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4';
         $userAuthToken = 'BTBZMqHH6r4Tts7J_aSIgg';
 
-        $localPublicKey = Base64Url::decode('BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8');
-        $salt = Base64Url::decode('DGv6ra1nlYgDCS1FRnbzlw');
+        $localPublicKey = Base64UrlSafe::decodeNoPadding('BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8');
+        $salt = Base64UrlSafe::decodeNoPadding('DGv6ra1nlYgDCS1FRnbzlw');
 
         [$localPublicKeyObjectX, $localPublicKeyObjectY] = Utils::unserializePublicKey($localPublicKey);
         $localJwk = new JWK([
             'kty' => 'EC',
             'crv' => 'P-256',
             'd' => 'yfWPiYE-n46HLnH0KqZOF1fJJU3MYrct3AELtAQ-oRw',
-            'x' => Base64Url::encode($localPublicKeyObjectX),
-            'y' => Base64Url::encode($localPublicKeyObjectY),
+            'x' => Base64UrlSafe::encodeUnpadded($localPublicKeyObjectX),
+            'y' => Base64UrlSafe::encodeUnpadded($localPublicKeyObjectY),
         ]);
 
         $expected = [
             'localPublicKey' => $localPublicKey,
             'salt' => $salt,
-            'cipherText' => Base64Url::decode('8pfeW0KbunFT06SuDKoJH9Ql87S1QUrd irN6GcG7sFz1y1sqLgVi1VhjVkHsUoEsbI_0LpXMuGvnzQ'),
+            'cipherText' => Base64UrlSafe::decodeNoPadding('8pfeW0KbunFT06SuDKoJH9Ql87S1QUrdirN6GcG7sFz1y1sqLgVi1VhjVkHsUoEsbI_0LpXMuGvnzQ'),
         ];
 
         $result = Encryption::deterministicEncrypt(
@@ -59,17 +59,17 @@ public function testDeterministicEncrypt(): void
         );
 
         $this->assertEquals(Utils::safeStrlen($expected['cipherText']), Utils::safeStrlen($result['cipherText']));
-        $this->assertEquals(Base64Url::encode($expected['cipherText']), Base64Url::encode($result['cipherText']));
+        $this->assertEquals(Base64UrlSafe::encodeUnpadded($expected['cipherText']), Base64UrlSafe::encodeUnpadded($result['cipherText']));
         $this->assertEquals($expected, $result);
     }
 
     public function testGetContentCodingHeader(): void
     {
-        $localPublicKey = Base64Url::decode('BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8');
-        $salt = Base64Url::decode('DGv6ra1nlYgDCS1FRnbzlw');
+        $localPublicKey = Base64UrlSafe::decodeNoPadding('BP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8');
+        $salt = Base64UrlSafe::decodeNoPadding('DGv6ra1nlYgDCS1FRnbzlw');
 
         $result = Encryption::getContentCodingHeader($salt, $localPublicKey, "aes128gcm");
-        $expected = Base64Url::decode('DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8');
+        $expected = Base64UrlSafe::decodeNoPadding('DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A8');
 
         $this->assertEquals(Utils::safeStrlen($expected), Utils::safeStrlen($result));
         $this->assertEquals($expected, $result);