APNs .p8 Key vs .p12 Certificate: Which Push Credential to Use
Use an APNs .p8 key for new projects: it never expires and covers every app in your team. A .p12 push certificate is per app and expires yearly.
For APNs, use a .p8 key rather than a .p12 certificate in new projects: the .p8 key never expires and one key can send to every app on your team. A .p12 push certificate covers one app, expires after a year, and is only worth it when you integrate with an older system that supports certificates only. Switching from p12 to p8 only touches the server, with no new app release. Below you’ll find how to create each, how to configure your server or push service, how to migrate, and failure causes by APNs response. Apple rules are as of October 2026.
If you’re adding push for the first time and haven’t got the whole flow working yet, start with iOS push notifications with APNs, from p8 key to your first push, which goes from enabling the capability to the first notification on your iPhone. This article focuses on choosing and switching credentials.
What’s the difference between an APNs .p8 key and a .p12 certificate?
| .p8 authentication key (token) | .p12 push certificate | |
|---|---|---|
| Where to create it | Keys page in Certificates, Identifiers & Profiles | Push Notifications > Configure on the App ID in the Identifiers page, or the Certificates page |
| What you get | AuthKey_<Key ID>.p8, downloadable only once |
A .cer, which you combine with the CSR’s private key into a .p12 |
| Validity | Doesn’t expire, can be revoked | One year, replace it before it expires |
| Scope | A Team Scoped key covers every app on the team, including future ones. A Topic Specific key covers only the apps you select | One certificate is tied to one App ID |
| Environment | Chosen when you create the key. Apple recommends one key each for development and production | The current “Sandbox & Production” type works in both environments |
| Server work | Every request carries a JWT signed with the .p8 (ES256), refreshed every 20 to 60 minutes | Present the certificate when opening the TLS connection, no token in requests |
| Multiple apps | One key and one connection pool | One certificate per app, each with its own connections |
| If it leaks | Create a new key, switch over, then revoke the old one | Revoke the certificate, and APNs rejects connections made with it |
| Required role | Account Holder or Admin | Account Holder or Admin |
The rules in the table come from Apple’s Establishing a token-based connection to APNs, Establishing a certificate-based connection to APNs and the help page Communicate with APNs using authentication tokens. The certificate documentation says “Provider certificates are valid for a year”, and once expired you can’t send notifications with them. See what happens when a certificate expires or is revoked.
Key environments
Apple’s current documentation states that new team keys are limited to either Sandbox or Production, with at most two team keys per environment. Older keys that work in both environments stay valid, but Apple recommends splitting them by environment. Topic Specific keys are limited to 200 per environment, each associated with up to 400 topics.
Once keys are split by environment, sign JWTs for the development environment (api.sandbox.push.apple.com) with the development key and for production (api.push.apple.com) with the production key. If you mix them up, APNs returns 403 BadEnvironmentKeyIdInToken.
Which to choose
| Your situation | Choose |
|---|---|
| New project with your own push service | .p8 team keys, one for development and one for production |
| Several apps on one team | A .p8 team key that covers all of them |
| A service that should only send to certain apps | A .p8 Topic Specific key with just those apps selected |
| A third-party push service that supports both | .p8, so you skip the yearly certificate renewal |
| An older system or service that supports certificates only | .p12, with the expiration date in your calendar |
How do you create a .p8 authentication key?
- On the Keys page in Certificates, Identifiers & Profiles, click “+”, enter a name and check Apple Push Notification service (APNs).
- Click Configure next to it and choose the environment and key type (Team Scoped or Topic Specific) using the options on the page. For Topic Specific, also select the apps to associate.
- Confirm and click Download. The
.p8file can be downloaded only once. A grayed-out Download button means you already downloaded it, and if you lose the file you have to revoke the key and create a new one. - Write down the Key ID (the 10 characters under the name on the key’s details page) and the Team ID (the 10 characters under Membership details on the developer website).
Apple’s Create a private key and Get a key identifier are the authoritative steps. The App ID also needs the Push Notifications capability checked, as described in the capability section of the push tutorial. With a .p8 key you can ignore the Configure button next to Push Notifications.
A .p8 key grants push access for your whole team. Keep it in your server’s secret store, never commit it to the repository, and never ship it inside the app.
Create a .p12 push certificate
A certificate starts with a CSR (certificate signing request). After Apple issues the .cer, combine it with the CSR’s private key into a .p12. On Windows and Linux you can do this with OpenSSL, the same way as when you get a development certificate.
First generate the private key and CSR.
1openssl req -new -newkey rsa:2048 -nodes -keyout push.key -out push.csr -subj "/emailAddress=you@example.com/CN=MyApp Push/C=US"
Then request the certificate in the developer portal.
- On the Identifiers page, open the app’s App ID, check Push Notifications and click Configure.
- Under Production SSL Certificate, click Create Certificate, upload
push.csrand click Continue. - Click Download to get the
.cerfile. The steps below assume it’s namedaps.cer.
Finally build the .p12. Give the export password to your server or push service.
1openssl x509 -in aps.cer -inform DER -out aps.pem
2openssl pkcs12 -export -inkey push.key -in aps.pem -out push.p12
Check which app the certificate is bound to and when it expires.
1openssl x509 -in aps.pem -noout -subject -enddate
The Bundle ID appears in subject, and notAfter is the expiration date. Apple’s Communicate with APNs using a TLS certificate has the authoritative steps. That page exports from Keychain Access on a Mac, but the idea is the same.
Push certificates are for servers only. When you build with KXBuild on Windows or Linux, don’t put them in the keychain folder. That folder holds only the development and distribution certificates that sign the app. See Certificates and signing.
How do you configure your server or a third-party push service?
Whether you run your own service or use Firebase or another third-party push service, you prepare the same set of materials. With a third-party service, upload them in its dashboard and follow its documentation for the exact screens.
| With .p8, provide | With .p12, provide |
|---|---|
The .p8 file |
The .p12 file |
| Key ID | The password you set when exporting the .p12 |
| Team ID | The certificate’s environment (some services want development and production uploaded separately) |
| Bundle ID (the push topic) | Bundle ID (most services read it from the certificate) |
| Environment, development or production |
These three settings are the easiest to get wrong.
- The Bundle ID must match exactly. The
apns-topicrequest header is the app’s Bundle ID. If it doesn’t match the build, APNs returnsDeviceTokenNotForTopic. If your Ad Hoc test build uses a different Bundle ID (such ascom.example.myapp.test), configure the backend with the test build’s ID. See iOS Ad Hoc and App Store builds from one project with xcconfig. - The environment must match the build. Debug builds signed with a Development profile use the development environment. TestFlight, App Store and Ad Hoc builds use production. Device tokens from one environment don’t work in the other.
- The device token must be current. Have the app report its token to your backend on every launch. Tokens can change after the app is deleted and reinstalled or moved to a new phone.
If you run your own service, keep a few more points in mind, all from Apple’s Sending notification requests to APNs and the token documentation.
- Connect over HTTP/2 with TLS 1.2 or later on port 443. If your firewall only allows push traffic, you can use port 2197 instead.
- A JWT whose
iatis more than an hour old is rejected withExpiredProviderToken. Replacing the JWT on the same connection more often than every 20 minutes returnsTooManyProviderTokenUpdates. Run a scheduled job that re-signs every 20 to 60 minutes. - One connection serves one team. If you send push for several developer accounts, use separate connection pools.
How do you migrate from p12 to p8?
Migration only touches the server. You don’t need to release a new app version or collect device tokens again. Tokens are generated by the phone per app and environment, independent of whether the server authenticates with a key or a certificate.
- Create the key. Create a .p8 as described above. Creating a key doesn’t affect the certificate in use, and both can be valid at the same time.
- Verify in development first. Add a .p8 configuration to your server or push service, send to the development environment using a device token from a Debug build, and confirm you get 200 and the phone receives it.
- Switch production. Move production to the .p8, verify once with a token from a TestFlight build, then watch the delivery success rate for real users for a day or two.
- Use new connections. If you run your own service, close the old certificate-based connections after switching and send over new ones. Apple’s documentation states that a connection is bound to the team and key used on its first push, and switching to an unrelated key later returns an error.
- Retire the old certificate. Once you’ve confirmed no service still uses it, revoke it or let it expire. After revocation APNs rejects every connection made with it, so confirm before you act.
If you use a third-party service, upload the .p8 in its dashboard, fill in the Key ID, Team ID and Bundle ID, switch over as its documentation describes, and send one test notification to a test device in each environment.
How do you troubleshoot APNs failures by response code?
APNs returns a status code for every request, and failures include {"reason":"..."} in the response body. The reason strings and meanings below follow Apple’s Handling notification responses from APNs. The last column maps each one to the configuration to check.
| Status | reason | Meaning | Common causes and fixes |
|---|---|---|---|
| 400 | BadDeviceToken |
Invalid device token | Token and environment don’t match, such as a Debug build’s token sent to the production address or the other way round, or the token was copied incompletely |
| 400 | DeviceTokenNotForTopic |
Token doesn’t belong to the topic | apns-topic differs from the app’s Bundle ID, common when a test build changes the Bundle ID |
| 400 | TopicDisallowed |
Pushing to this topic isn’t allowed | Check that the topic belongs to this team, the App ID has Push Notifications enabled, and the Topic Specific key includes this app |
| 400 | BadTopic, MissingTopic |
Topic invalid or missing | Add the apns-topic header with the Bundle ID as its value |
| 400 | InvalidPushType |
Invalid apns-push-type |
Use alert for regular notifications and background for silent push |
| 403 | InvalidProviderToken |
JWT invalid or signature check failed | Wrong Key ID or Team ID, the .p8 belongs to another team, or the signature isn’t in the raw 64-byte format |
| 403 | ExpiredProviderToken |
JWT expired | The JWT is more than an hour old or the server clock is off. Sync the clock and re-sign on a schedule |
| 403 | BadEnvironmentKeyIdInToken |
Key doesn’t match the environment | A development key was used against production or the other way round |
| 403 | UnrelatedKeyIdInToken |
Key is unrelated to the one used on this connection’s first push | Open a new connection after switching keys |
| 403 | MissingProviderToken |
Neither a certificate nor a JWT | Add the authorization: bearer <JWT> header |
| 403 | BadCertificate |
Invalid certificate | The .p12 is corrupt, the password is wrong, or the certificate expired or was revoked |
| 403 | BadCertificateEnvironment |
Certificate doesn’t match the environment | The certificate and the address (development or production) don’t match. Use the certificate for that environment or change the address |
| 410 | Unregistered |
Token is no longer active for this topic | Usually the app was deleted from the phone. Stop sending to this token unless the app reports it again |
| 410 | ExpiredToken |
Device token expired | Wait for the app to report a new token on its next launch |
| 413 | PayloadTooLarge |
Payload too large | Keep regular notifications at 4 KB or less, and download large images in a notification service extension |
| 429 | TooManyProviderTokenUpdates |
JWT updated too often | Reuse each JWT for 20 to 60 minutes instead of re-signing for every request |
| 429 | TooManyRequests |
Too many requests to the same token | Lower the rate and retry after a delay |
Apple says not to retry BadDeviceToken, DeviceTokenNotForTopic, Forbidden, ExpiredToken, Unregistered or PayloadTooLarge unchanged. Fix the configuration or get a new token first. You can retry 5xx errors after 15 minutes with backoff. Too many 4xx errors can make APNs slow you down or close your connection.
What if APNs returns 200 but nothing arrives, or there’s no token?
A 200 only means APNs accepted the request. When the phone doesn’t receive anything, or the app can’t get a device token at all, the aps-environment entitlement in the app’s signature is usually the problem.
| Symptom | Cause | Fix |
|---|---|---|
Registration fails with no valid "aps-environment" entitlement string found for application |
The signature has no aps-environment |
Check Push Notifications on the App ID, regenerate the profile, and add the key to the project’s entitlements |
| Build says the profile doesn’t include Push Notifications | The profile was generated before the capability was enabled | Edit the profile, save it without changes, then download and replace it |
| Upload to App Store Connect reports ITMS-90078 | The code registers for push but the signature has no push entitlement | See ITMS-90078 |
| Debug builds receive push, TestFlight builds don’t | The backend still uses the development address or a development token | Production builds need the production address and the new token they report |
| Push worked with the certificate but stopped after switching to .p8 | The key’s environment is wrong, or the sender still uses the old connection | Check the returned reason against the table above |
The value of aps-environment comes from the provisioning profile. Apple’s aps-environment documentation states that development profiles use development (also called sandbox), while distribution profiles and TestFlight use production. When you build with KXBuild on Windows or Linux, write development in the entitlements file and the value is taken from the profile used at build time.
See the entitlements section of the push tutorial. For profile-related build errors, see 16 common iOS code signing and .ipa install errors. Once the credential, environment and signature all match, the APNs side of push is complete.
FAQ
Does an APNs .p8 key expire?
No. Apple states that APNs signing keys don’t expire, though they can be revoked. When you need to replace a key, create the new one first and revoke the old one after your server has switched.
Can one .p8 key send push to several apps?
Yes. A team key covers every app on the team, including apps you create later, and the apns-topic header picks the app for each request. To cover only some apps, create a Topic Specific key and select those apps.
What happens when a .p12 push certificate expires?
Your server can no longer send push with it. The app keeps running but receives no notifications. Push certificates are valid for one year, so replace yours before it expires, or take the chance to switch to a .p8 key.
Do I need a new app release or new device tokens when switching from p12 to p8?
Neither. Device tokens are generated by the phone per app and environment and don’t depend on the credential your server uses. Just switch the credential on your server or push service.
Should I upload a p8 or a p12 to a third-party push service?
Upload the .p8 if the service supports it, along with the Key ID, Team ID and Bundle ID, and you won’t need to renew a certificate every year. If the service only supports certificates, upload the .p12 and its export password, and configure development and production separately.