Documentation — Complete Guide

PWA Push
Notifications

Premium Progressive Web App push notifications for WordPress — with rich images, install detection, six admin themes, and seamless WooCommerce integration.

WordPress ≥ 6.0 PHP ≥ 8.1 WooCommerce ≥ 8.0 HTTPS Required VAPID / RFC 8292 Version 1.0.0

01 Overview

PWA Push Notifications by XaniaCode turns any WordPress installation into an installable Progressive Web App capable of sending rich, image-based push notifications to subscribers — even when the browser is closed.

The plugin implements the Web Push Protocol (RFC 8292) with VAPID authentication entirely server-side, requiring no third-party push service or external API account. Subscribers are stored locally in your database, and all cryptography is handled by the bundled xania-webpush library.

Area Capability Notes
Push Notifications Title, body, icon, badge, hero image, click URL Core
PWA Manifest Dynamic, site-aware manifest served via rewrite rule Core
Install Banner Smart banner with 7-day dismissal memory, iOS instructions Core
Subscriber Segmentation All / PWA-installed only / Browser-only Core
VAPID P-256 ECDSA key generation and rotation Core
Admin Themes 6 presets including fully custom color picker UI
WooCommerce Order and stock event notifications per-customer and admin Add-on
Diagnostic 40+ environment, conflict and live HTTP checks Tools
Dead Endpoint GC Automatic removal of expired/invalid push endpoints Core
i18n / RTL Translation-ready, RTL-aware admin UI UI

02 Requirements

Verify your hosting environment meets all of the following before installing:

🔷
WordPress
≥ 6.0 (tested up to 6.9)
🐘
PHP
≥ 8.1
🔒
HTTPS
Valid TLS certificate required
Service workers won't load over HTTP
🛒
WooCommerce (optional)
≥ 8.0 (tested up to 10.7)
🔑
PHP Extensions
openssl, gmp or bcmath
🌐
Browser Support
Chrome, Edge, Firefox, Opera, Safari
⚠ Safari / iOS note Safari on iOS 16.4+ and macOS 13+ supports Web Push from installed PWAs. Older Safari versions will display the install banner but cannot receive push notifications.
ℹ Outbound connectivity Your server must be able to make outbound HTTPS requests to browser push services (e.g., fcm.googleapis.com, updates.push.services.mozilla.com) and to xaniacode.com for license validation.

03 Installation

Manual Upload (Recommended)

  1. 1
    Upload the plugin folder

    Extract the ZIP and upload the pwa-push-notifications folder to your server at:

    /wp-content/plugins/pwa-push-notifications/
  2. 2
    Activate through WordPress admin

    Go to Plugins → Installed Plugins, locate PWA Push Notifications, and click Activate. The plugin creates its subscriber database table automatically on activation.

  3. 3
    Navigate to PWA Push in the sidebar

    A new top-level menu item — PWA Push — will appear in the WordPress admin sidebar.

  4. 4
    Generate VAPID keys

    On the Dashboard screen, click Generate VAPID Keys. This is required before any subscription or notification will work. See Section 04 for details.

  5. 5
    Activate your license (or start the trial)

    The 14-day trial starts automatically the first time you open the plugin's admin pages — no key required. For an active license, see Section 07.

  6. 6
    Visit your site over HTTPS

    Front-end visitors will now see the install/subscribe banner. The service worker is registered and the PWA manifest is served automatically.

What the Plugin Installs

The activation routine creates:

✔ Database table A custom subscribers table ({prefix}xc_pwa_subscribers) with columns for endpoint, auth, p256dh keys, user_id, is_pwa (install detection), and timestamps.
✔ Rewrite rule A WordPress rewrite rule that serves /pwa-manifest.json dynamically — no static file is created or overwritten on your server.
✔ WP-Cron job A daily background job (xc_pwa_daily_license) for license validation. Transient failures do not deactivate the plugin.

Uninstalling

Deleting the plugin via Plugins → Delete triggers the bundled uninstall.php script, which removes the subscribers table and all plugin options from the database, leaving no orphan data behind.

⚠ Warning Uninstalling erases all subscriber data permanently. Export any subscriber records you need to keep before deleting the plugin.

04 VAPID Keys

VAPID (Voluntary Application Server Identification) is the authentication mechanism that proves to browser push services that push requests originate from your server. The plugin implements VAPID per RFC 8292 using P-256 / prime256v1 ECDSA key pairs.

Generating Keys

  1. 1
    Go to PWA Push → Dashboard

    The VAPID status panel at the top of the dashboard shows whether keys are currently set.

  2. 2
    Click "Generate VAPID Keys"

    The plugin generates a new P-256 ECDSA key pair using PHP's openssl extension and stores both the public key (base64url) and private PEM securely in the WordPress options table.

  3. 3
    Set the VAPID subject

    The subject should be either a mailto: address or an https: URL identifying your organisation. This is embedded in VAPID JWT tokens and is visible to browser push services.

    Example subjects
    mailto:admin@example.com
    https://example.com
🔴 Rotating VAPID keys invalidates all subscriptions When you regenerate keys, every existing subscriber endpoint becomes invalid. All current subscribers will need to re-subscribe on their next visit. Only rotate keys if you suspect a key compromise.

How Keys Are Used

The public key is embedded in the front-end JavaScript (pwaPush.publicKey) and passed to the browser's PushManager.subscribe() call as the applicationServerKey. The private key PEM is used server-side to sign JWT tokens attached to each push request.

05 General Settings

Navigate to PWA Push → Settings to configure all plugin options. Settings are grouped into tabs.

Frontend Behaviour

Frontend Behaviour
show_install_promo
Show the install/subscribe banner to visitors.
Default: enabled
auto_prompt
Automatically prompt for notification permission without requiring the user to interact with the banner first. Not recommended — Chrome may penalise sites that auto-prompt, reducing permission-grant rates.
Default: disabled
manifest_color
theme_color and background_color injected into the dynamic PWA manifest and the <meta name="theme-color"> tag in <head>.
Default: #000000

WooCommerce Integration

WooCommerce
wc_enabled
Master switch for WooCommerce notifications. Only visible when WooCommerce is active.
Default: disabled
wc_admin_capability
WordPress capability used to identify admin recipients for store-level events (new orders, failed orders, stock alerts).
Default: manage_woocommerce
wc_events
Array of individual order/stock events to enable. See Section 08 for the full list.
ℹ Saving settings The settings form POSTs to admin-post.php with action pwa_push_save_settings. All input is sanitised with WordPress helpers (sanitize_text_field, esc_url_raw, etc.) before being stored.

06 Appearance

The plugin ships with six admin theme presets selectable under Settings → Appearance. Themes are applied only to the plugin's own admin pages and do not affect the rest of the WordPress admin or the front end.

Inherit
Follows the active WordPress admin colour scheme. Safest choice for existing themes.
Light
Clean white background with grey borders and blue accents.
Dark
Dark grey surface with light text. Reduces eye strain in low-light environments.
Professional
Deep navy with sharper contrast. Suited for agency and enterprise environments.
Art Floral
Soft organic gradients with botanical-inspired accent colours.
Custom
Full colour picker for Background, Surface, Text, Border, Primary, and Accent.

Custom Colour Variables

When the Custom theme is selected, you can set six CSS variables independently via the WordPress colour picker:

Custom Theme Colours
--xc-bg
Page background colour
--xc-surface
Card and panel surface colour
--xc-text
Primary text colour
--xc-border
Border and divider colour
--xc-primary
Buttons, links, active states
--xc-accent
Highlights and badge colours
ℹ Appearance form action Appearance settings are saved separately via the action xc_pwa_save_appearance, allowing you to update colours without touching functional settings.

07 License

The plugin includes a license manager that gates the notification-sending functionality. VAPID key management and subscriber collection work regardless of license status.

Trial Period

A 14-day local trial starts automatically the first time you open any plugin admin page — no license key is required. During the trial you can send notifications and test all features.

Activating a License

  1. 1
    Go to PWA Push → License

    The license screen shows current status, expiry, and trial remaining time.

  2. 2
    Enter your license key and click Activate

    The plugin contacts xaniacode.com/api/v1/license/activate with your key and the current site URL to register the activation.

  3. 3
    Confirmation

    On success the license status updates to Active and the notification composer becomes fully available.

Background Validation

A WP-Cron job runs daily to re-validate the license against the XaniaCode server. Transient server failures do not deactivate the plugin — the last known valid state is preserved until a definitive invalid response is received.

Deactivating a License

Click Deactivate on the License screen. This contacts xaniacode.com/api/v1/license/deactivate and frees the activation slot so you can move the license to another site.

ℹ License storage security License data is stored encrypted in the WordPress options table using AES-256-CBC with HMAC-SHA256 message authentication. Encryption keys are derived from your WordPress secret salts.

08 Sending Notifications

Notifications can be sent manually from the Dashboard or programmatically via the PHP API. An active license or trial is required to send.

Manual Send (Dashboard)

The notification composer on the Dashboard page accepts the following fields:

FieldRequiredDescription
TitleYesNotification title displayed by the OS notification system
BodyYesMain notification message text
Click URLNoURL opened when the user clicks the notification. Defaults to home_url()
Icon URLNoSmall icon shown alongside the notification. Defaults to the site icon
Hero Image URLNoLarge image displayed below the notification body (Chrome/Android)
Badge URLNoMonochrome icon used by Android to replace the browser icon in the status bar
TargetNoAudience segment: all, pwa (installed app), or browser

Subscriber Segmentation

The Target field lets you send to subsets of your subscriber list:

Target Values
all
Send to every active subscriber regardless of how they subscribed.
pwa
Send only to subscribers who have installed the site as a PWA (standalone display mode or iOS standalone).
browser
Send only to subscribers who are on browser-only (non-installed) mode.

Dead Endpoint Cleanup

When the push service returns HTTP 404 or 410 for a subscriber endpoint (meaning the subscription has expired or been revoked), the plugin automatically removes that subscriber from the database. This keeps your subscriber list clean without any manual intervention.

09 Subscribers

Navigate to PWA Push → Subscribers to view and manage all active push subscriptions.

Subscriber Data

ColumnDescription
EndpointThe push service URL unique to this subscription (partial, truncated for display)
Is PWAWhether the subscriber subscribed from an installed PWA (standalone mode)
User IDLinked WordPress user ID, if the visitor was logged in when subscribing
Created AtTimestamp of when the subscription was created

Front-End Opt-In Flow

The plugin follows browser best-practice for permission requests:

  1. 1
    Install banner appears

    A non-intrusive banner slides in offering to install the app and/or subscribe to notifications. The banner is dismissed and not shown again for 7 days if the user clicks "Not now".

  2. 2
    User gesture triggers permission request

    Clicking "Install" or "Subscribe" counts as a user gesture, satisfying Chrome's requirement that permission prompts be triggered by user interaction.

  3. 3
    Browser shows the native permission prompt

    If the user grants permission, the endpoint and keys are sent to the server via an AJAX call secured with a WordPress nonce.

ℹ iOS specifics Safari on iOS cannot show a standard install banner. The plugin detects iOS and shows custom instructions: "Tap the Share button, then choose Add to Home Screen." Push notifications on iOS 16.4+ only work from the installed PWA, not from Safari in-browser.

10 WooCommerce Integration

When WooCommerce (≥ 8.0) is active and you enable the integration under Settings → WooCommerce, the plugin hooks into order lifecycle and stock events to dispatch push notifications automatically.

Supported Events

New Order
→ Admin
Fires when a brand-new order is placed. An idempotency guard prevents duplicate notifications if both woocommerce_new_order and woocommerce_checkout_order_processed fire together.
Order Processing
→ Customer
Fires when the order status changes to "processing" (payment received).
Order Completed
→ Customer
Fires when the order is marked "completed".
Order Cancelled
→ Customer
Fires when the order is cancelled.
Order Refunded
→ Customer
Fires when the order is refunded.
Order Failed
→ Admin
Fires when payment fails and the order moves to "failed".
Low Stock
→ Admin
Fires when a product reaches the low-stock threshold.
Out of Stock
→ Admin
Fires when a product's stock reaches zero.

Guest Checkout Auto-Linking

When a guest completes checkout, the plugin checks whether their billing email matches a registered WordPress user. If a match is found within a 60-minute window and the IP addresses align, the subscription is automatically linked to that user account — ensuring customer-targeted notifications reach returning guests who then log in.

Admin Capability

Admin notifications are dispatched to all subscribers who have the capability set in wc_admin_capability. The default is manage_woocommerce, but you can change this to manage_options or edit_shop_orders depending on who should receive store alerts.

⚠ HPOS compatibility The WooCommerce module uses HPOS-aware order URL helpers (get_edit_order_url, get_view_order_url) so notification click URLs work correctly whether or not High Performance Order Storage is enabled.

11 Diagnostic

Navigate to PWA Push → Diagnostic to run a comprehensive suite of environment and health checks. This page is essential for troubleshooting why push notifications may not be working.

Check Groups

🖥️
Environment — PHP version, OpenSSL, GMP/bcmath extensions, HTTPS, WordPress version
40+ checks
🔌
Plugin State — VAPID keys present, license status, settings sanity
pass / warn / fail
🗄️
Database — Subscriber table existence, column schema, subscriber counts
live check
🔐
Security — AJAX nonce setup, VAPID subject format, encryption availability
live check
⚔️
Conflicts — Detects competing PWA/push plugins (OneSignal, Super PWA, WebPushr, PWA for WP…)
may warn
⚙️
Service Worker — Live HTTP probe of /sw.js checking Content-Type and response integrity
live HTTP
🌐
Frontend — Live probe of /pwa-manifest.json for correct MIME type (application/manifest+json)
live HTTP

Caching Conflicts

The diagnostic also detects common caching and optimisation plugins that might cache sw.js with the wrong headers or an incorrect Cache-Control value. Detected plugins include: WP Rocket, LiteSpeed Cache, W3 Total Cache, WP Super Cache, and others.

Exporting a Diagnostic Report

Click Export Diagnostic Report on the Diagnostic page to download a plain-text summary of all check results. Include this file when submitting a support ticket to XaniaCode.

Example report excerpt
=== XaniaCode PWA Push Notifications — Diagnostic Report ===
Generated: 2026-04-29 14:32:01
Plugin version: 1.0.0

--- ENVIRONMENT ---
✅  PHP version — 8.2.12
✅  OpenSSL extension — loaded
✅  HTTPS — secure context detected
⚠️  GMP extension — not loaded (bcmath fallback active)

--- CONFLICTS ---
✅  No competing PWA plugins detected
⚠️  WP Rocket active — ensure sw.js is excluded from caching

12 Developer API

The plugin exposes a clean PHP API on the XC_PWA_Notifications class for sending notifications from custom code — themes, other plugins, or WP-CLI scripts.

Accessing the Instance

$plugin        = XC_PWA_Plugin::instance();
$notifications = $plugin->notifications;
$subscribers   = $plugin->subscribers;

dispatch() — Send to a list of subscribers

The lowest-level dispatch method. Pass an array of subscriber rows from the database and a payload array:

$result = $notifications->dispatch(
    $subscribers,   // array of subscriber rows
    [
        'title' => 'Hello from my plugin',
        'body'  => 'Something interesting happened.',
        'icon'  => 'https://example.com/icon.png',
        'url'   => 'https://example.com/event/',
        'image' => 'https://example.com/hero.jpg', // optional
        'badge' => 'https://example.com/badge.png', // optional
    ]
);

// $result shape:
// [
//   'sent'     => int,
//   'failed'   => int,
//   'failures' => [ ['id'=>int, 'status'=>int, 'error'=>string], ... ],
//   'skipped'  => bool,  // true when license is inactive
// ]

send_to_user() — Send to a specific WordPress user

$notifications->send_to_user( $user_id, [
    'title' => 'Your report is ready',
    'body'  => 'Click to download.',
    'url'   => admin_url( 'admin.php?page=my-reports' ),
] );

send_to_capability() — Send to all users with a capability

$notifications->send_to_capability( 'manage_options', [
    'title' => 'Admin alert',
    'body'  => 'Disk usage has exceeded 90%.',
    'url'   => admin_url(),
] );
⚠ License gate applies to the PHP API All three methods check whether the license is active before dispatching. If the license has expired and the trial has ended, dispatch() returns skipped: true and no network requests are made.

Custom WP-CLI Integration Example

send-push.php — drop in mu-plugins or a custom command
// Send a notification from a custom WP-CLI command.
WP_CLI::add_command( 'pwa notify', function( $args ) {
    $plugin = XC_PWA_Plugin::instance();
    $subs   = $plugin->subscribers->get_by_target( 'all' );

    $result = $plugin->notifications->dispatch( $subs, [
        'title' => $args[0] ?? 'Test',
        'body'  => $args[1] ?? 'Hello from CLI',
        'url'   => home_url(),
    ] );

    WP_CLI::success( "Sent: {$result['sent']}  Failed: {$result['failed']}" );
} );

13 FAQ

Does the plugin work without HTTPS?
No. All modern browsers require a secure context (HTTPS or localhost) before they allow service worker registration or push subscription. This is a browser platform requirement, not a plugin restriction. You must have a valid TLS certificate on your domain.
Why don't notifications work in trial mode?
Notification sending requires either an active paid license or an active 14-day trial. The trial starts automatically the first time you open any PWA Push admin page — no key required. If the trial has expired you will need a license key to send notifications.
Will visitors be automatically prompted for permission?
By default, no. Chrome and other browsers penalise sites that fire the permission prompt without a user gesture. The plugin shows a polite install banner; clicking Install or Subscribe triggers the permission prompt as part of the user gesture, which significantly improves grant rates. You can opt into auto-prompting under Settings → Frontend Behaviour if you prefer, but it is not recommended.
I regenerated VAPID keys — why did all my subscribers disappear?
Subscribers are stored against a specific VAPID public key. When you generate new keys, all existing browser subscriptions become cryptographically invalid — the browser push services will reject requests signed with the new keys for old endpoints. Your existing subscribers will automatically re-subscribe on their next visit to your site, after which they will appear in the subscriber list again.
Why is sw.js being cached incorrectly?
Caching plugins sometimes aggressively cache sw.js with a long Cache-Control max-age, which means browsers won't pick up service worker updates. You should exclude /wp-content/plugins/pwa-push-notifications/sw.js from any page or file caching rules. The Diagnostic page will flag known offending caching plugins.
Can I send notifications from my own plugin or theme code?
Yes. The plugin exposes a public PHP API via XC_PWA_Plugin::instance()->notifications. See Section 12 — Developer API for usage examples including dispatch(), send_to_user(), and send_to_capability().
Does the plugin work with Cloudflare or other reverse proxies?
Generally yes, but ensure that sw.js and /pwa-manifest.json are excluded from Cloudflare's edge caching. Set a Page Rule to Cache Level: Bypass for those paths. Also verify that Cloudflare's Rocket Loader does not defer the pwa-push.js script (add the data-cfasync="false" attribute via a filter if needed).
Does this plugin replace or conflict with other PWA plugins?
It can conflict. The Diagnostic tool detects common competing plugins: Super PWA, OneSignal, PWA for WP, WebPushr, and others. Running two PWA plugins that both register a service worker on the same scope can cause unpredictable behaviour. It is recommended to use only one PWA solution at a time.

14 Security

The plugin was designed with security as a first-class concern. Key measures:

MeasureImplementation
License storage AES-256-CBC encryption + HMAC-SHA256 message authentication (encrypt-then-MAC). Keys derived from WordPress secret salts.
AJAX nonce protection All AJAX endpoints verified with check_ajax_referer() using separate nonces for admin (pwa_admin_nonce) and frontend (pwa_push_nonce).
Input sanitisation All $_POST values sanitised with WordPress helpers: sanitize_text_field, sanitize_textarea_field, esc_url_raw.
Capability checks Every admin AJAX action and POST handler verifies current_user_can('manage_options') before proceeding.
VAPID subject validation The VAPID subject is validated against a mailto: or https: pattern before being stored.
License resilience Transient failures reaching the license server do not deactivate the plugin, preventing false invalidation from temporary network issues.
No external CDN All assets (JS, CSS, vendor library) are self-hosted within the plugin. No external CDN connections are made during page load.
ℹ Responsible disclosure If you discover a security vulnerability, please report it directly to XaniaCode rather than filing a public issue. Contact information is available at xaniacode.com.