New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

node-webpush

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-webpush

A dependency-free Web Push implementation for Node.js

latest
Source
npmnpm
Version
1.1.1
Version published
Weekly downloads
20
1900%
Maintainers
1
Weekly downloads
 
Created
Source

Node WebPush (Node.js + TypeScript)

npm version License CI Codecov

A dependency-free Web Push implementation for Node.js (TypeScript-first).

This library focuses on standards-compliant payload encryption + VAPID authentication, and produces request details that can be sent with Node’s built-in fetch().

Features

✅ Standards & compatibility

  • RFC 8188: HTTP Encrypted Content Encoding (record-based framing, per-record nonce derivation).
  • RFC 8291: Web Push message encryption (ECDH + auth secret, “WebPush: info” key schedule, aes128gcm).
  • RFC 8292: VAPID (JWT ES256) authentication headers.
  • Supports both encodings:
    • aes128gcm (recommended): modern Web Push encoding (RFC 8291 + RFC 8188).
    • aesgcm (legacy): kept for interoperability with older endpoints.

🔐 Encryption

  • Pure Node.js crypto (no external libs).
  • RFC8188 record framing:
    • delimiter rules (0x01 for non-last, 0x02 for last)
    • optional final-record padding
    • nonce = baseNonce XOR SEQ per record

🪪 VAPID

  • Native ES256 JWT signing using Node’s crypto (JWK-based key objects).
  • Key generation (VAPID.GenerateKeys()).
  • Full validation helpers (VAPID.Validate.*).

🧰 Request building

  • generateRequest() produces { endpoint, init } for fetch(endpoint, init).
  • Sets required headers:
    • TTL, Urgency, optional Topic
    • Content-Encoding, Content-Type, Content-Length
    • Authorization (VAPID or GCM/FCM key when applicable)

⚠️ GCM / FCM edge cases

  • Detects legacy GCM endpoints:
    • Uses Authorization: key=<apiKey> (VAPID not supported on legacy GCM).
  • Supports FCM endpoints:
    • Uses VAPID by default when configured.
    • Can fall back to Authorization: key=<apiKey> if VAPID is disabled and a key is provided.

Installation

npm install node-webpush

TypeScript is supported out of the box (the package emits .d.ts).

Quick start

1) Create a WebPush instance

import {WebPush} from "node-webpush";

const webpush = new WebPush({
    vapid: {
        subject: "mailto:admin@example.com",
        publicKey: process.env.VAPID_PUBLIC_KEY!,
        privateKey: process.env.VAPID_PRIVATE_KEY!,
    },
    // Optional: used for legacy GCM/FCM key-based auth fallback
    gcm: {apiKey: process.env.GCM_API_KEY ?? null},
});

2) Send a notification

const subscription = {
    endpoint: "https://push-service.example/...",
    keys: {
        p256dh: "<base64url>",
        auth: "<base64url>",
    },
};

const res = await webpush.notify(subscription, "Hello from WebPush!", {
    TTL: 60,
});

console.log("Status:", res.status);

Generate VAPID keys

import {VAPID} from "node-webpush";

const keys = VAPID.GenerateKeys();
console.log(keys.publicKey);
console.log(keys.privateKey);

You typically store these as environment variables:

  • VAPID_PUBLIC_KEY
  • VAPID_PRIVATE_KEY

API Reference (high level)

new WebPush(config)

type WebPushConfig = {
    vapid: {
        publicKey: string;
        privateKey: string;
        subject: string | URL; // must be https: or mailto:
    };
    gcm?: { apiKey?: string | null };
};

Constructing WebPush validates:

  • VAPID subject format (https: or mailto:)
  • VAPID key sizes and base64url encoding
  • GCM/FCM key if provided (must be non-empty)

webpush.generateRequest(subscription, payload?, options?)

Returns the request parameters to call fetch() yourself.

const {endpoint, init} = webpush.generateRequest(subscription, "payload", {
    TTL: 60,
});

const res = await fetch(endpoint, init);

This is useful if you want to:

  • inspect headers
  • plug into your own HTTP stack
  • retry logic / circuit breakers
  • log request metadata

webpush.notify(subscription, payload?, options?)

Sends the request using fetch().

const res = await webpush.notify(subscription, "hello");

default it return the response even if not successful. It can also throw an error if the push service returns a non-2xx response. This can be enabled by:

import {WebPushError} from "./webpush";

try {
  const res = await webpush.notify(subscription, "hello", {
    throwOnInvalidResponse: true //Add this to the options
  });
} catch (error: WebPushError){
    console.error(error);
    const responseObject  = error.response; //<<-- The resulting response object can still be accessed
}

Throws WebPushError when the push service returns a non-2xx response.

This also contains the response but can be handled in the try-catch logic

Options

type GenerateRequestOptions = {
    headers?: Record<string, string>;

    TTL?: number; // seconds
    urgency?: "very-low" | "low" | "normal" | "high";
    topic?: string; // base64url <= 32 chars

    contentEncoding?: "aes128gcm" | "aesgcm";

    // RFC8188 knobs (primarily for advanced use/testing)
    rs?: number; // default 4096, must be >= 18
    allowMultipleRecords?: boolean; // default false (Web Push wants single record)
    finalRecordPadding?: number; // default 0

    // Override authentication behavior:
    vapidDetails?: WebPushConfig["vapid"] | null;
    gcmAPIKey?: string | null;
};

Notes

  • aes128gcm is recommended for Web Push.
  • For Web Push interoperability, leave allowMultipleRecords at false (default).
  • topic must use URL-safe base64 characters and be <= 32 chars.

Choosing auth method (VAPID vs key)

This library follows typical push-service rules:

  • Legacy GCM endpoint (https://android.googleapis.com/gcm/send...)
  • Uses Authorization: key=<gcmAPIKey>
  • VAPID is ignored (not supported)
  • Everything else
  • If vapidDetails is present: uses VAPID
  • Else if endpoint is FCM and a key is present: uses Authorization: key=<gcmAPIKey>

If you want to disable VAPID for a call:

await webpush.notify(subscription, "hello", {
    vapidDetails: null,
    gcmAPIKey: process.env.GCM_API_KEY!,
});

Minimal example with manual fetch

import {WebPush} from "node-webpush";

const webpush = new WebPush({
    vapid: {
        subject: "https://example.com/contact",
        publicKey: process.env.VAPID_PUBLIC_KEY!,
        privateKey: process.env.VAPID_PRIVATE_KEY!,
    },
});

const {endpoint, init} = webpush.generateRequest(subscription, "ping", {
    TTL: 120,
    urgency: "high",
});

console.log(init.headers); // inspect headers

const res = await fetch(endpoint, init);
console.log(res.status);

Error handling

import {WebPush, WebPushError} from "node-webpush";

try {
    await webpush.notify(subscription, "hello");
} catch (e) {
    if (e instanceof WebPushError) {
        console.error("Push service rejected request:", e.response.status);
        console.error("Response body:", await e.response.text());
    } else {
        console.error("Unexpected error:", e);
    }
}

Runtime requirements

  • Node.js with global fetch (Node 18+ recommended).
  • TypeScript target: ES2020 works.

Security notes

  • Keep your VAPID private key secret.
  • Always validate subscriptions server-side before storing or using them.
  • Avoid sending sensitive data in payloads; push payloads can be stored/forwarded by push services.

License

Apache 2.0 See LICENSE

Keywords

webpush

FAQs

Package last updated on 06 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts