Web Push API encryption and sending library for Go with VAPID support.
The client-based API sends encrypted Web Push notifications from a Go server to Push API subscriptions. It covers RFC 8188 / RFC 8291 payload encryption, RFC 8292 VAPID authentication, RFC 8030 delivery headers, multi-record payloads, send-side receipt metadata, and batch sending.
The library does not implement live receipt monitoring. It only supports requesting receipts and reusing returned receipt-subscription metadata.
go get github.com/marknefedov/go-webpush/v2- Generate VAPID keys once and store them securely.
package main
import (
"os"
webpush "github.com/marknefedov/go-webpush/v2"
)
func main() {
keys, err := webpush.GenerateVAPIDKeys()
if err != nil {
panic(err)
}
pem, err := keys.ExportVAPIDPrivateKeyPEM()
if err != nil {
panic(err)
}
_ = os.WriteFile("vapid_private.pem", pem, 0o600)
}- Create a reusable client and send a notification.
package main
import (
"context"
"encoding/json"
"log"
"net/http"
"os"
"time"
webpush "github.com/marknefedov/go-webpush/v2"
)
func main() {
var sub webpush.Subscription
if err := json.Unmarshal([]byte(`{...}`), &sub); err != nil {
log.Fatal(err)
}
keys, err := webpush.LoadVAPIDPrivateKeyPEM(mustRead("vapid_private.pem"))
if err != nil {
log.Fatal(err)
}
client := webpush.NewClient(webpush.Config{
HTTPClient: &http.Client{Timeout: 10 * time.Second},
})
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
result, err := client.Send(ctx, []byte("Hello from Go!"), &sub, webpush.SendOptions{
Subject: "user@example.com",
VAPIDKeys: keys,
TTL: 60,
RequestReceipt: true,
})
if err != nil {
log.Fatal(err)
}
defer result.Response.Body.Close()
log.Println("push status:", result.StatusCode, "records:", result.RecordCount, "receipt:", result.ReceiptSubscription)
}
func mustRead(p string) []byte {
b, _ := os.ReadFile(p)
return b
}- Send a no-payload notification when you only need a wake-up or receipt request.
result, err := client.Send(ctx, nil, &sub, webpush.SendOptions{
Subject: "user@example.com",
VAPIDKeys: keys,
RequestReceipt: true,
ReceiptSubscription: "https://app.example/receipts",
})
if err != nil {
log.Fatal(err)
}
log.Println("no payload:", result.NoPayload)- Send the same payload to many subscriptions with batch helpers.
attempts := client.SendBatch(ctx, []byte("Hello everyone!"), subs, webpush.SendOptions{
Subject: "user@example.com",
VAPIDKeys: keys,
TTL: 60,
})
for _, attempt := range attempts {
if attempt.Err != nil {
log.Println("failed:", attempt.Subscription.Endpoint, attempt.Err)
continue
}
log.Println("ok:", attempt.Subscription.Endpoint, attempt.Result.StatusCode)
}SendNotificationremains available as a thin compatibility wrapper, but the client API is the recommended entry point.Topic,Urgency, andTTLare validated before a request is sent.RequestReceiptandReceiptSubscriptiononly request and propagate receipt metadata. They do not enable live receipt monitoring.
The example/ directory contains a small CLI sender and service worker for the client API.