Debugging web push failures
Web push debugging usually splits into two distinct phases. First, confirm the server can send a message successfully, which should return a 201 HTTP status code. Second, verify that the browser actually receives and decrypts the message, firing the push event in the service worker. Different symptoms point to different root causes, so working through these stages systematically saves time.
If you are new to service workers, debugging those separately is a good first step. Registration failures or stale files often have nothing to do with push itself. A dedicated guide on debugging service workers covers those cases well.
A useful tip before diving deep: Firefox and Mozilla AutoPush return more descriptive error messages than Chrome. If you are stuck, reproduce the issue in Firefox—the error text often reveals the problem immediately.
Authorization errors
Authorization failures are the most common early hurdle. They almost always stem from misconfigured VAPID (Application Server Keys). The typical fix is supplying an applicationServerKey in the subscribe() call. Any mismatch between the key used on the front end for subscription and the key used on the server to sign the Authorization header results in an authorization error.
Chrome and FCM behavior
Chrome uses FCM as its push service. FCM returns an UnauthorizedRegistration error for several VAPID-related problems:
- A missing
Authorizationheader in the request to FCM. - A mismatch between the key that subscribed the user and the key used to sign the JWT.
- An invalid token expiration—either the JWT has already expired or the expiration exceeds 24 hours.
- A malformed JWT or one containing invalid values.
<html>
<head>
<title>UnauthorizedRegistration</title>
</head>
<body bgcolor="#FFFFFF" text="#000000">
<h1>UnauthorizedRegistration</h1>
<h2>Error 400</h2>
</body>
</html>
If this error appears, try the same request through Firefox for a clearer diagnostic message.
Firefox and Mozilla AutoPush messages
Mozilla AutoPush responds with a 401 Unauthorized error when the Authorization header is missing entirely:
{
"errno": 109,
"message": "Request did not validate missing authorization header",
"code": 401,
"more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes",
"error": "Unauthorized"
}
An expired JWT produces a 401 with an explicit message about the expired token:
{
"code": 401,
"errno": 109,
"error": "Unauthorized",
"more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes",
"message": "Request did not validate Invalid bearer token: Auth expired"
}
Key mismatch—the subscription key differs from the key that signed the Authorization header—surfaces as a 404 Not Found response:
{
"errno": 102,
"message": "Request did not validate invalid token",
"code": 404,
"more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes",
"error": "Not Found"
}
Invalid JWT values, such as an unexpected alg field, generate a distinct AutoPush error:
{
"code": 401,
"errno": 109,
"error": "Unauthorized",
"more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes",
"message": "Request did not validate Invalid Authorization Header"
}
HTTP status codes from push services
A push service can return various non-201 codes for reasons unrelated to authorization. The table below lists the status codes you might encounter and what each indicates in the context of web push:
| Status Code | Description |
|---|---|
| 429 | Too many requests. Your application server has reached a rate limit with a push service. The response from the service should include a 'Retry-After' header to indicate how long before another request can be made. |
| 400 | Invalid request. One of your headers is invalid or poorly formatted. |
| 404 | Not Found. In this case you should delete the PushSubscription from your back end and wait for an opportunity to resubscribe the user. |
| 410 | Gone. The subscription is no longer valid and should be removed from your back end. This can be reproduced by calling `unsubscribe()` on a `PushSubscription`. |
| 413 | Payload size too large. The minimum size payload a push service must support is 4096 bytes (or 4kb). Anything larger can result in this error. |
For a status code not listed here with an unclear response body, consult the Web Push Protocol spec. It references status codes with scenarios where each is appropriate.
Payload decryption failures
Successfully sending a push message—receiving a 201—but never seeing the push event in the service worker usually signals a decryption failure on the browser side.
Firefox reports this clearly in the DevTools console:

To confirm the same problem in Chrome:
- Open
about://gcm-internalsand click "Start Recording".

- Trigger a push message and inspect the "Message Decryption Failure Log".

You will see an AES-GCM decryption failed message in the details column. Two tools can help pinpoint the encryption issue:
- Push Encryption Verifier tool by Peter Beverloo.
- Web Push Data Encryption Test Page by Mozilla.
Connection problems with the push service
If no decryption errors appear but the push event still does not fire, the browser may have lost connection to the push service. In Chrome, use the "Receive Message Log" in about://gcm-internals to see whether messages arrive:

If messages do not arrive promptly, verify that the connection status shows CONNECTED:

When the status is not CONNECTED, deleting the current profile and creating a new one often restores connectivity. If that does not help, proceed to file a bug report.
Submitting a bug report
When all diagnostics fail, the issue may live in the browser's push implementation. File a report with the relevant project:
- Chrome: bugs.chromium.org
- Firefox: bugzilla.mozilla.org
A useful bug report needs specific artifacts. Include each of the following where possible:
- Every browser version tested (e.g., Chrome 50, Chrome 51, Firefox 50, Firefox 51).
- An example
PushSubscriptionthat reproduces the problem. - Complete network request headers sent to the push service.
- Complete response bodies received from those requests.
A minimal reproducible example—source code or a hosted site—substantially accelerates diagnosis and resolution.



