ESPWebPush is an async-first Web Push sender for ESP32 firmware. It handles VAPID JWT signing, RFC 8291 aes128gcm payload encryption, and HTTP delivery so devices can notify browsers without custom glue code.
ArduinoJson v7+ is required for the structured payload API.
- RFC 8292 VAPID JWT signing with
mailto:orhttps://subjects. - Accepts standard unpadded base64url VAPID keys generated by Node/web-push tooling.
- RFC 8291 / RFC 8188
aes128gcmWeb Push encryption. - Async queue + worker task via native FreeRTOS APIs.
- Bounded shutdown via
requestStop(),join(timeoutMs), anddeinit(timeoutMs). - Sync
send()API plus asyncsend()overloads that returnWebPushEnqueueResult. - Strict
PushPayloadvalidation for browser notification fields. - Payload-size guard with the RFC-safe default limit of 3993 bytes.
- Small per-origin JWT cache to avoid re-signing every message.
- Configurable queue length, memory caps, retries, timeouts, and worker task settings.
- Optional TLS transport controls for certificate bundle, global CA store, and common-name verification behavior.
#include <Arduino.h>
#include <ESPWebPush.h>
ESPWebPush webPush;
void setup() {
Serial.begin(115200);
WebPushVapidConfig vapid;
vapid.subject = "mailto:notify@example.com";
vapid.publicKeyBase64 = "BAvapidPublicKeyBase64Url...";
vapid.privateKeyBase64 = "vapidPrivateKeyBase64Url...";
WebPushConfig cfg;
cfg.queueLength = 16;
cfg.queueMemory = WebPushQueueMemory::Psram;
cfg.worker.stackSizeBytes = 16 * 1024;
cfg.worker.priority = 3;
cfg.worker.name = "webpush";
cfg.maxPayloadBytes = 3993;
cfg.networkValidator = []() { return true; };
webPush.init(vapid, cfg);
}
void loop() {}WebPushSubscription subscription;
subscription.endpoint = "https://fcm.googleapis.com/fcm/send/...";
subscription.p256dh = "BME...";
subscription.auth = "nsa...";
PushPayload payload;
payload.title = "Hello";
payload.body = "ESP32";
payload.tag = "demo";
payload.icon = "https://example.com/icon.png";WebPushEnqueueResult enqueue = webPush.send(subscription, payload, [](WebPushResult result) {
if (!result.ok()) {
ESP_LOGE("WEBPUSH", "Push failed: %s (status %d)",
result.message, result.statusCode);
return;
}
ESP_LOGI("WEBPUSH", "Push OK (status %d)", result.statusCode);
});
if (!enqueue.queued()) {
ESP_LOGW("WEBPUSH", "Enqueue failed: %s", enqueue.message);
}Async preflight failures are returned through WebPushEnqueueResult. The callback only runs for messages that were actually queued.
JsonDocument doc;
doc["title"] = "Hello";
doc["body"] = "ESP32";
doc["tag"] = "demo";
WebPushResult result = webPush.send(subscription, doc);WebPushResult result = webPush.send(subscription, payload);
if (!result.ok()) {
ESP_LOGW("WEBPUSH", "Sync push failed: %s", result.message);
}PushMessage msg;
msg.subscription = subscription;
msg.payload = "{\"title\":\"Hello\",\"body\":\"ESP32\"}";
// Raw payload strings remain supported, but they are not schema-validated.
WebPushResult result = webPush.send(msg);if (webPush.isInitialized()) {
WebPushJoinStatus stopStatus = webPush.deinit();
if (stopStatus == WebPushJoinStatus::Timeout) {
ESP_LOGW("WEBPUSH", "Worker did not stop within the timeout");
}
}requestStop() marks shutdown and wakes the worker without blocking. join(timeoutMs) waits for the worker to exit and finalizes shutdown when the stop completes in time. deinit(timeoutMs) is the convenience wrapper that performs both in one call.
WebPushConfig lets you tune the worker and queue:
queueLength- number of queued messages.queueMemory-Internal,Psram, orAny.worker- stack size, priority, core id, and task name.requestTimeoutMs- HTTP timeout.ttlSeconds- Web Push TTL header.maxRetries,retryBaseDelayMs,retryMaxDelayMs- retry/backoff controls.maxPayloadBytes- plaintext payload size guard. The default is 3993 bytes; use0to disable.useTlsCertBundle- attachesp_crt_bundle_attachwhen the build provides the ESP x509 bundle.useGlobalCaStore- forwarduse_global_ca_storetoesp_http_client.skipTlsCommonNameCheck- forwardskip_cert_common_name_checktoesp_http_client.networkValidator- optional callback for application-defined network readiness checks.
The default transport behavior is certificate-bundle-backed (useTlsCertBundle = true), which matches common browser push providers such as FCM without extra app wiring.
- System time is required for VAPID JWT expiration.
- Web Push endpoints require TLS-capable
esp_http_client. - Only
aes128gcmis generated. Legacyaesgcmis intentionally not supported in v2. subjectmust start withmailto:orhttps://.- VAPID keys should be standard base64url strings without PEM wrapping; unpadded Node/web-push output is supported.
- The configured VAPID public key must match the private key.
bool init(const WebPushVapidConfig&, const WebPushConfig& = {})WebPushEnqueueResult send(const PushMessage&, WebPushResultCB cb)WebPushResult send(const PushMessage&)WebPushEnqueueResult send(const WebPushSubscription&, const PushPayload&, WebPushResultCB cb)WebPushResult send(const WebPushSubscription&, const PushPayload&)WebPushEnqueueResult send(const WebPushSubscription&, const JsonDocument&, WebPushResultCB cb)WebPushResult send(const WebPushSubscription&, const JsonDocument&)WebPushEnqueueResult send(const WebPushSubscription&, JsonVariantConst, WebPushResultCB cb)WebPushResult send(const WebPushSubscription&, JsonVariantConst)void requestStop()WebPushJoinStatus join(uint32_t timeoutMs)void setNetworkValidator(WebPushNetworkValidator)WebPushJoinStatus deinit(uint32_t timeoutMs = 10000)/bool isInitialized() constconst char *errorToString(WebPushError)
- ESP32-class targets only.
- Arduino and ESP-IDF frameworks are supported.
- Requires C++17, ArduinoJson v7+, and mbedTLS.
- Do not call from ISR context.
- On-device Unity tests live in
test/test_esp_webPush. - CI builds Arduino examples and includes an ESP-IDF compile smoke build.
This repository follows the firmware formatting baseline from esptoolkit-template:
.clang-formatis the source of truth for C/C++/INO layout..editorconfigenforces tabs (tab_width = 4), LF endings, and final newline.- Format tracked firmware sources with
bash scripts/format_cpp.sh.
MIT - see LICENSE.md.
- Check out other libraries: https://github.com/orgs/ESPToolKit/repositories
- Support the project: https://ko-fi.com/esptoolkit
- Visit the website: https://www.esptoolkit.hu/