Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Web Push Laravel Package

minishlink/web-push

WebPush is a PHP library implementing the Web Push protocol (RFC 8030) for sending push notifications to browser endpoints. It handles Subscription objects, payload encryption, VAPID authentication, and delivery to service workers. Requires PHP 8.2+ with curl, openssl, and mbstring.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. minishlink/web-push is a single-purpose protocol library, not a framework component: it encrypts payloads per RFC 8291/8030, signs VAPID JWTs per RFC 8292, and dispatches HTTP requests to browser vendor push services (FCM, Mozilla autopush, WNS, Apple). It has no Laravel service provider, no config publish, no facades, and no persistence layer. Architecturally that is a feature for us — it stays at the edge of the domain, and everything opinionated (subscription storage, retries, channel routing, telemetry) is ours to own. Fit is strong if we treat it as a transport primitive behind a thin Laravel wrapper; it is a poor fit if stakeholders expect a batteries-included push product.

Integration feasibility. High. Composer install, PHP 8.2+, and standard extensions (curl, openssl with EC, mbstring, gmp/bcmath recommended for crypto performance). Guzzle is already in our transitive dependency graph via Laravel's HTTP client, and the library supports injecting a Guzzle client, so proxy, TLS, and timeout policy can be centralized. The API surface is small and stable: Subscription::create() from the browser's PushSubscription.toJSON() output, queueNotification() + flush() for batches, sendOneNotification() for single sends, and MessageSentReport objects for outcomes including isSubscriptionExpired(). A competent mid-level engineer can produce a working spike in under a day.

Technical risk.

  • VAPID key permanence. Changing the key pair invalidates every stored subscription. This is a de facto irreversible secret with disaster-recovery and rotation-policy implications; it must live in a secrets manager, not .env on a single node.
  • PHP version coupling. The README publishes a strict major-version-to-PHP-version matrix (v9 for 8.1, latest for 8.2+). If our platform lags on PHP, we pin older majors and inherit their maintenance posture. This should be checked against our upgrade calendar before commitment.
  • Payload ceiling and privacy. Practical payload limit is ~3052 bytes; payloads are encrypted but still transit third-party infrastructure. No PII or secrets in payloads — an architectural constraint, not a bug.
  • Synchronous-by-default HTTP. flush() blocks on Guzzle. Calling it in a request cycle is a latent latency incident; queue-only is mandatory.
  • Single-pass generator. flush() returns a \Generator that can be iterated exactly once. Naive retry or "log then summarize" code silently loses results.
  • Dependency signal. "0 dependents" in the supplied metadata is almost certainly a Packagist indexing artifact for a library this widely used, but we should verify real-world adoption ourselves rather than cite the number in a business case.
  • Vendor behavior drift. Safari's documented sensitivity to missing urgency, and the contentEncoding transition from aesgcm to aes128gcm, are reminders that browser vendors — not the library — set the compatibility bar.

Key questions.

  1. Which PHP and Laravel versions must we support at launch, and does that force a pinned older major?
  2. Is there an existing push vendor (OneSignal, FCM web push, Pusher Beams) in the estate, and is this a build-or-buy decision we are consciously making?
  3. Who owns VAPID key custody, backup, and the (non-)rotation policy?
  4. Does the frontend team own the service worker and pushManager.subscribe() flow, and is that work funded and scheduled?
  5. What delivery telemetry is required by the business — sends, successes, expiry-driven prunes, per-browser breakdown?
  6. Do we need multi-channel orchestration (email/SMS/web push) with per-user preferences and quiet hours, or is this a single-channel MVP?
  7. What is the expected subscriber count and peak send rate, and what does that imply for queue workers and vendor rate limits?

Integration Approach

Stack fit. Web push complements, and does not replace, APNs/FCM mobile push: it targets browsers and installed PWAs. It slots naturally into Laravel's notification ecosystem as a custom channel (vonage, mail, database are the familiar analogues), backed by a queued job per batch. Recommended shape:

  • config/webpush.php — VAPID subject, key references resolved from secrets, default TTL/urgency/topic/batch size, timeouts.
  • A service provider binding a configured WebPush instance as a singleton (or a small factory keyed by tenant, if multi-tenant).
  • push_subscriptions table: user_id, endpoint (unique, hashed index), public_key, auth_token, content_encoding, user_agent, last_success_at, last_failure_reason, disabled_at.
  • A WebPushChannel + WebPushMessage value object so product code says $user->notify(new InvoiceReady($invoice)).
  • A SendWebPushBatch job that flushes in bounded batches and persists MessageSentReport outcomes.

Migration path. Greenfield for most teams, so "migration" is really three parallel tracks: (a) backend library adoption, (b) subscription capture endpoints and frontend service worker, (c) key provisioning and secret distribution. If replacing a vendor, run both in shadow for one release cycle — capture subscriptions in both formats where possible, compare delivery rates, then cut over. Note that subscriptions are vendor-agnostic (endpoint + keys), so export/import from a vendor is feasible but VAPID key continuity is not: subscribers must re-subscribe if we introduce our own key pair, which argues for launching with our key from day one.

Compatibility. Enforce PHP 8.2+ in composer.json platform config; require ext-curl, ext-openssl, ext-mbstring, and prefer ext-gmp/ext-bcmath for encryption throughput. Store contentEncoding per subscription rather than assuming a global default, since browsers differ and older subscriptions may carry aesgcm. Pin a major version and treat its PHP matrix as an input to platform upgrade planning. Validate against the four major push services explicitly; a green CI badge is not evidence of browser-vendor behavior.

Sequencing.

  1. Week 1 — spike. Send to a real endpoint via a tinker/artisan command; confirm key generation, payload encryption, and report parsing on our PHP runtime.
  2. Week 1–2 — foundation. Config + provider binding, subscription schema and CRUD endpoints, key provisioning into the secrets manager, composer.json platform constraints.
  3. Week 2–3 — delivery path. Queued batch job, WebPushChannel, outcome persistence, expired-subscription pruning.
  4. Week 3 — frontend contract. Service worker registration, permission UX, subscribe/unsubscribe lifecycle, browser matrix test plan.
  5. Week 4 — hardening. Load test at projected volume, failure-mode drills (vendor 429/5xx, timeouts, malformed subs), dashboards and alerts.
  6. Week 5 — pilot. One low-risk notification type to a small cohort, then expand.

Rough envelope: 3–5 engineer-weeks backend, plus separate frontend effort.

Operational Impact

Maintenance. Low code-maintenance, moderate operational-maintenance. The library is MIT, actively released, and its surface area is small enough that upgrade diffs are readable. Real cost sits in the edges: PHP upgrade cadence (version matrix), subscription table hygiene, and browser vendor behavior changes. Budget a recurring slice — roughly a day per quarter for dependency and vendor-compat review — plus a defined owner for the VAPID key.

Support. No commercial support; community issues and docs only. The README is genuinely good and covers TTL, urgency, topic, batching, and payload sizing — the questions teams actually get wrong. Escalation path for a production incident is "read the code," which is viable here because the codebase is small. Set that expectation explicitly with stakeholders before go-live; this is not a vendor with an SLA.

Scaling. Throughput is bounded by two things: encryption CPU and push-service rate limits. Mitigations are all configuration, not code — batchSize (default 1000, lowered to protect memory), setReuseVAPIDHeaders(true) to avoid re-signing JWTs across a flush, and horizontal queue-worker concurrency. Sending is stateless per notification, so scaling is embarrassingly parallel; the hard constraint is per-vendor throttle behavior (429s), which needs backoff, not brute force. At hundreds of thousands of subscribers, the pruning job and the endpoint index matter more than the send path.

Failure modes to instrument.

  • isSubscriptionExpired() → soft-delete the row; never retry. Stale subscriptions are the dominant long-run data quality issue.
  • 5xx / timeouts from push services → retry with jittered backoff; the library does not retry for you.
  • 429 / throttling → concurrency reduction and per-vendor queue partitioning.
  • Single-pass Generator misuse → silently missing telemetry. Mitigate by materializing reports inside the job and persisting before any other logic.
  • Payload over ~3052 bytes → encrypt/validation error. Enforce a size guard in the message value object, fail fast at enqueue time rather than in the worker.
  • Missing urgency → reported Safari failures. Set a default explicitly.
  • Bad or mismatched VAPID subject → silent rejection by vendors. Validate at boot, fail the health check.
  • Worker memory exhaustion on huge flushes → batch cap plus memory_limit alerting.

Ramp-up. Developer ramp-up is short — a half-day to first successful send using the README example and the linked sample project; API shape (queue → flush → reports) maps cleanly onto concepts Laravel engineers already hold. Operational ramp-up is the longer pole: teams need to internalize that (a) VAPID keys are effectively immutable, (b) all sends belong on the queue, (c) subscription expiry is normal and must be handled, and (d) delivery is best-effort with no read receipt. One internal runbook covering key custody, pruning cadence, throttle response, and browser-compat regression testing will do more for reliability than any additional code.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin