# Chứng chỉ push Apple: .p8 hay .p12 với Expo

> Để gửi thông báo đẩy, Apple có khóa .p8 không hết hạn, dùng cho mọi app, hoặc chứng chỉ .p12 hạn một năm. Với Expo, EAS quản lý khóa giúp bạn.

Trang gốc: [Chứng chỉ push Apple: .p8 hay .p12 với Expo](https://devoria.fr/vi/blog/chung-chi-push-apple-p8-p12-expo)
Tác giả: [Mehdi Azizi](https://devoria.fr/vi/mehdi-azizi)
Ngày đăng: 2026-11-04
Lần sửa nội dung gần nhất: 2026-11-04


Chứng chỉ push của Apple cho phép một máy chủ gửi thông báo đến app của bạn qua APNs, dịch vụ thông báo của Apple. Nó có hai dạng: chứng chỉ .p12, có hiệu lực một năm và gắn với một app duy nhất, hoặc khóa .p8, không hết hạn và dùng được cho mọi app trong tài khoản của bạn. **Với Expo, bạn không cần chứng chỉ: EAS đề nghị tạo khóa .p8 ở lần build đầu tiên, và dịch vụ push của Expo dùng khóa đó thay bạn.**

Bài hướng dẫn về [chứng chỉ developer và provisioning profile](https://devoria.fr/vi/blog/ios-developer-certificate-provisioning-profile-expo) chỉ nhắc đến khóa push vài dòng. Ở đây mình đi đến cùng: chọn giữa hai định dạng, hai môi trường sandbox và production, việc gia hạn, và những lỗi làm mất thông báo trên TestFlight.

Mình phát hành app bằng Expo và EAS. Solys và TradaZen khai báo plugin `expo-notifications` trong cấu hình: chính plugin này chuẩn bị cho app nhận thông báo.

## Vì sao Apple yêu cầu chứng chỉ hoặc khóa

Một thông báo push xuất phát từ máy chủ, của bạn hoặc của Expo, máy chủ đó giao nó cho APNs, rồi APNs đưa đến iPhone. Vì vậy APNs phải kiểm tra máy chủ đó có quyền gửi cho app của bạn không. Apple có hai cách để chứng minh.

- **Kết nối bằng token, với khóa .p8.** [Theo Apple](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns), cách này nhanh hơn, dùng được từ nhiều máy chủ, và một khóa bao phủ toàn bộ hoặc một phần app của team bạn. Máy chủ dùng khóa để ký một token, phải tạo lại ít nhất mỗi giờ.
- **Kết nối bằng chứng chỉ, với file .p12.** [Apple nói rõ](https://developer.apple.com/documentation/usernotifications/establishing-a-certificate-based-connection-to-apns) chứng chỉ này gắn với một app duy nhất và có hiệu lực một năm. Nhiều app thì nhiều chứng chỉ.

[Trang của Apple về các mốc hết hạn](https://developer.apple.com/support/expiration/) nhắc lại: khóa ký token APNs không bao giờ hết hạn.

## Bảng so sánh .p8 / .p12

| Tiêu chí      | Khóa .p8                | Chứng chỉ .p12               |
| ------------- | ----------------------- | ---------------------------- |
| Thời hạn      | Không hết hạn           | Một năm                      |
| Phạm vi       | Mọi app của bạn         | Một app duy nhất             |
| Môi trường    | Chọn khi tạo            | Sandbox và production        |
| Gia hạn       | Không, trừ khi thu hồi  | Mỗi năm                      |
| Tải xuống     | Chỉ một lần             | Xuất từ máy Mac              |
| Số lượng      | Hai khóa mỗi môi trường | Một cho mỗi app              |
| Dùng với Expo | EAS tạo và quản lý      | Không có trong tài liệu Expo |

Ba lưu ý. File .p8 [chỉ tải xuống được một lần](https://developer.apple.com/help/account/keys/create-a-private-key): Apple không giữ nó trong tài khoản của bạn. Còn .p12 thì [được xuất từ Keychain của máy Mac](https://developer.apple.com/help/account/capabilities/communicate-with-apns-using-a-tls-certificate) đã tạo chứng chỉ. Và giới hạn hai khóa mỗi môi trường áp dụng cho khóa cấp team. Với khóa giới hạn cho một số app, Apple cho phép đến 200 khóa mỗi môi trường.

[Trang của Expo về thông tin xác thực](https://docs.expo.dev/app-signing/app-credentials/) chỉ nói về khóa push, và tính tối đa hai khóa mỗi tài khoản, không phân biệt môi trường hay khóa theo app. **Với một app Expo, khóa .p8 là lựa chọn đúng.**

## Sandbox và production: hai môi trường

APNs có hai máy chủ: development, còn gọi là sandbox, và production. [Apple](https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns) dành máy chủ đầu cho việc test và máy chủ sau cho app đã phát hành. App biết dùng máy chủ nào nhờ entitlement `aps-environment`. [Theo Apple](https://developer.apple.com/documentation/bundleresources/entitlements/aps-environment), giá trị của nó theo profile ký: `development` với profile development, `production` với profile distribution và cho TestFlight.

**Vì vậy một build TestFlight nói chuyện với production, y hệt App Store.**

Về phía Expo, có hai chi tiết. [Tài liệu expo-notifications](https://docs.expo.dev/versions/latest/sdk/notifications/) cho biết entitlement luôn được ghi là `development` trong project, rồi chuyển thành `production` trong archive của build release. Và khi được tạo, token Expo gắn với môi trường của app, được nhận diện tự động: bạn không phải chọn gì khi gửi.

Nếu tự tạo khóa, Apple sẽ hỏi môi trường và phạm vi của nó: cả team hay một số app. Tài liệu cho developer của Apple giờ mô tả khóa team chỉ dùng cho sandbox hoặc production, còn khóa cũ dùng được ở mọi nơi vẫn được chấp nhận. Trong khi đó, [trang trợ giúp tài khoản](https://developer.apple.com/help/account/capabilities/communicate-with-apns-using-authentication-tokens) vẫn khẳng định khóa dùng cho cả hai môi trường. Apple tự mâu thuẫn: hãy nhớ môi trường bạn chọn khi tạo. **Khóa chỉ dành cho sandbox sẽ không dùng được cho TestFlight lẫn App Store.**

## Expo quản lý khóa push như thế nào

Theo [hướng dẫn cấu hình của Expo](https://docs.expo.dev/push-notifications/push-notifications-setup/), cập nhật ngày 28/7/2026, build development đầu tiên sẽ đề nghị bạn cấu hình push notification và tạo khóa APNs. Hãy trả lời có cho cả hai. Nếu không dùng EAS Build, chạy `eas credentials`. Bạn cần một tài khoản Apple Developer trả phí.

Sau đó:

- **EAS lưu khóa và gắn nó với app của bạn.** Theo Expo, một khóa dùng được cho bao nhiêu app tùy bạn, và tải lên khóa mới không làm thay đổi token Expo Push của người dùng.
- **Máy chủ của bạn không gọi APNs.** Nó gửi đến dịch vụ push của Expo, dịch vụ này chuyển tiếp cho APNs. [Expo yêu cầu bạn đọc receipt](https://docs.expo.dev/push-notifications/sending-notifications/) khoảng 15 phút sau khi gửi: lỗi hiện ở đó.
- **Plugin thêm entitlement.** Khai báo `expo-notifications` trong `plugins`:

```json
{
  "expo": {
    "plugins": ["expo-notifications"]
  }
}
```

Hai cái bẫy. [FAQ của Expo](https://docs.expo.dev/push-notifications/faq/) nói Expo Go không còn hỗ trợ push notification từ SDK 53, trong khi trang expo-notifications chỉ nhắc đến Android. Dù thế nào, Expo khuyên dùng build development: hãy test bằng nó. Và xóa khóa bằng `eas credentials` chỉ gỡ nó khỏi máy chủ của Expo, không gỡ khỏi tài khoản Apple. Muốn giải phóng một suất, hãy thu hồi khóa trong portal của Apple. Như mọi secret, file .p8 không bao giờ được nằm trong repo của bạn: quy tắc giống hệt với [API key của app Expo](https://devoria.fr/vi/blog/khoa-api-bi-mat-app-expo).

## Gia hạn: thứ gì thật sự hết hạn

**Khóa .p8 không có gì phải gia hạn.** Nó chỉ ngừng hoạt động khi bạn thu hồi. Nếu nghi khóa bị lộ, [Apple khuyên](https://developer.apple.com/help/account/capabilities/communicate-with-apns-using-authentication-tokens) tạo khóa mới, chuyển sang dùng nó, rồi mới thu hồi khóa cũ. Để thay khóa với Expo, FAQ đưa ra các bước: `eas credentials`, iOS, build profile của bạn, rồi gỡ khóa push và tạo khóa mới.

**Chứng chỉ .p12 hết hạn mỗi năm.** Apple yêu cầu cập nhật nó trước hạn và theo dõi ngày hết hạn trong Keychain. Khi đã hết hạn hoặc bị thu hồi, [bạn không thể gửi thông báo](https://developer.apple.com/help/account/certificates/certificates-overview) đến app nữa. Apple không mô tả quy trình gia hạn riêng: bạn tạo chứng chỉ mới từ identifier của app, cài lên máy chủ trước hạn, rồi gỡ chứng chỉ cũ. **Cách gia hạn tốt nhất vẫn là chuyển sang khóa.**

> Bạn quản lý iPhone bằng Intune hay một công cụ quản lý thiết bị khác? Đó không phải cùng một chứng chỉ. Chứng chỉ push quản lý thiết bị được tạo trên Apple Push Certificates Portal và gia hạn mỗi năm. [Apple khuyên](https://support.apple.com/guide/deployment/dep2de55389a) ghi lại tài khoản Apple đã dùng: bạn sẽ cần nó khi gia hạn.

## Các lỗi thường gặp và cách sửa

### Chạy được ở development, không chạy trên TestFlight

1. **Bạn gửi đến sai token.** Build TestFlight đăng ký ở production và nhận token mới. Token từ build development không dùng được: Apple trả về `BadDeviceToken` khi [token không khớp với môi trường](https://developer.apple.com/documentation/usernotifications/handling-notification-responses-from-apns). Hãy lấy token từ chính app TestFlight.
2. **App production chưa có thông tin xác thực push.** Đây là chẩn đoán trong FAQ của Expo. Kiểm tra trang credentials của project trên expo.dev, hoặc `eas credentials`.
3. **Khóa của bạn chỉ dành cho sandbox.** Thay bằng khóa dùng được ở production.
4. **iPhone không nhận được token.** Nếu bạn cài build TestFlight đè lên build Xcode, Expo gợi ý khởi động lại thiết bị.

### "No valid aps-environment entitlement string found"

Theo FAQ của Expo, lỗi này báo rằng chưa có khóa push nào được cấu hình cho project iOS của bạn. Kiểm tra trang credentials, rồi build lại: lúc đó EAS sẽ đề nghị tạo khóa. Kiểm tra thêm `expo-notifications` đã có trong `plugins` chưa.

### ITMS-90078: Missing Push Notification Entitlement

Apple gửi email này sau khi bạn nộp build: app có vẻ đăng ký với APNs, nhưng chữ ký của nó không chứa entitlement `aps-environment`. [Ghi chú của Expo về SDK 51](https://github.com/expo/fyi/blob/main/apns-entitlement-sdk-51.md) giải thích điều này. Từ SDK đó, entitlement chỉ được thêm nếu `expo-notifications` được cài. Cảnh báo đến từ hai bước kiểm tra của Apple. Bước thứ nhất so sánh capability của app identifier với entitlement của app. Bước thứ hai phát hiện trong binary một phương thức đăng ký thông báo, mà code của Expo có trong mọi app.

**Đây là cảnh báo, không phải từ chối**, Expo nói rõ. Nếu bạn dùng thông báo, thêm plugin rồi build lại: [EAS đồng bộ capability](https://docs.expo.dev/build-reference/ios-capabilities/) của app identifier ở mỗi lần build. Nếu không, gỡ capability Push Notifications khỏi identifier trong portal của Apple. Cảnh báo có thể vẫn còn, nhưng không gây hậu quả gì.

### InvalidCredentials hoặc mọi app im lặng cùng lúc

`InvalidCredentials` trong receipt nghĩa là thông tin xác thực push của bạn không còn hợp lệ, thường vì khóa đã bị thu hồi. Mọi app dùng khóa đó im lặng cùng lúc. Cách sửa của Expo: `eas credentials`, rồi tạo khóa mới. Nếu receipt ghi `InvalidProviderToken`, Expo khuyên build lại và tạo lại cả khóa push lẫn profile.

## Trước khi bật thông báo

Để test thông báo trên iPhone trước khi phát hành, làm theo [TestFlight để test app](https://devoria.fr/vi/blog/testflight-kiem-thu-app-iphone). Nếu bạn gửi thông báo im lặng để đánh thức app, hãy đọc lại [guideline 2.5.4 về chế độ chạy nền](https://devoria.fr/vi/blog/guideline-2-5-4-apple-tu-choi-che-do-chay-nen). Toàn bộ quy trình, không cần Mac, có trong [phát hành app iOS không cần Mac](https://devoria.fr/vi/blog/phat-hanh-app-ios-khong-can-mac-expo-eas), còn các bước phát hành nằm ở trang [đưa ứng dụng lên App Store](https://devoria.fr/vi/expertise/dang-ung-dung-len-app-store).

Trước lần gửi đầu tiên, hãy làm [bài chẩn đoán miễn phí](https://devoria.fr/vi/diagnostic): nó cho bạn biết điều gì đang chặn project và gửi bạn checklist chống bị Apple từ chối của mình. Để xây app, gắn gói đăng ký và phát hành, [iOS & Android Kit](https://devoria.fr/vi/kits/ios) đi theo lộ trình bốn bước với code khởi đầu từ các app của mình, theo nhịp của bạn.

## Câu hỏi thường gặp

### Chứng chỉ push Apple có hết hạn không?

Chứng chỉ .p12 hết hạn sau một năm, và thông báo sẽ ngừng nếu không được thay. Khóa .p8 không bao giờ hết hạn: nó chỉ ngừng khi bạn thu hồi. Đó là lý do chính nên chọn khóa.

### Mỗi app có cần một chứng chỉ push riêng không?

Với chứng chỉ .p12 thì có: mỗi chứng chỉ gắn với một app duy nhất. Khóa .p8 dùng được cho mọi app trong tài khoản. Vì vậy với Expo, một khóa có thể bao phủ toàn bộ danh mục app của bạn.

### Gia hạn chứng chỉ push Apple như thế nào?

Tạo chứng chỉ mới từ identifier của app, với một yêu cầu ký (CSR) mới, cài lên máy chủ, rồi gỡ chứng chỉ cũ. Làm việc này trước hạn. Đơn giản nhất vẫn là chuyển sang khóa .p8, loại không cần gia hạn.

### Mình làm mất file .p8, phải làm sao?

Apple chỉ cho tải xuống một lần. Nếu EAS đã tạo khóa, Expo giữ nó và thông báo vẫn chạy. Nếu không, tạo khóa mới, thêm nó bằng `eas credentials`, rồi thu hồi khóa cũ trong portal của Apple. Token Expo Push của người dùng không thay đổi.

### Có cần build lại sau khi đổi khóa push không?

Thường là không. Khóa dùng cho máy chủ gửi, không phải cho app đã cài, và Expo cho biết khóa mới không làm thay đổi token. Ngoại lệ: nếu receipt báo `InvalidProviderToken`, Expo khuyên build lại với khóa mới và profile mới.


## Dự án của bạn

[Review Kit](https://devoria.fr/vi/kits/app-review): Checklist duyệt App Store của mình, các lần Apple từ chối đã sửa, checklist Google Play và ba skill Claude Code. 1.399.000 ₫.

[iOS & Android Kit](https://devoria.fr/vi/kits/ios): Tạo app iPhone và Android với Claude Code: code khởi đầu từ các app của mình, lộ trình đến tận store và tất cả các kit. 2.799.000 ₫.

[Chẩn đoán: điều gì ngăn bạn có app iPhone](https://devoria.fr/vi/diagnostic): 7 câu hỏi, 60 giây: bạn biết điều gì thật sự cản bạn đăng app iPhone, lộ trình 4 bước và kit phù hợp với bạn.
