# Apple 推播憑證：Expo 該用 .p8 還是 .p12

> Apple 推播可用永不過期、適用所有 app 的 .p8 金鑰，或每年到期的 .p12 憑證。用 Expo 時由 EAS 管理金鑰。

原始頁面：[Apple 推播憑證：Expo 該用 .p8 還是 .p12](https://devoria.fr/zh-hant/blog/apple-push-notification-certificate-p8-p12-expo)
作者：[Mehdi Azizi](https://devoria.fr/zh-hant/mehdi-azizi)
發布日期：2026-11-04
內容最後修訂：2026-11-04


Apple 的推播憑證讓伺服器可以透過 APNs（Apple 的推播通知服務）傳送通知給你的 app。它有兩種形式：.p12 憑證，效期一年、只綁定一個 app；或 .p8 金鑰，不會過期，可用於你帳號下的所有 app。**用 Expo 的話，你不需要憑證：EAS 會在第一次 build 時提議幫你建立 .p8 金鑰，Expo 的推播服務會替你使用它。**

[開發者憑證與 provisioning profile](https://devoria.fr/zh-hant/blog/ios-developer-certificate-provisioning-profile-expo) 那篇指南只用幾行帶過推播金鑰。這篇會講到底：兩種格式怎麼選、sandbox 與 production 環境、更新方式，以及讓 TestFlight 收不到通知的常見故障。

我用 Expo 和 EAS 發布 app。Solys 和 TradaZen 都在設定裡宣告了 `expo-notifications` 外掛：就是它讓 app 準備好接收通知。

## 為什麼 Apple 要求憑證或金鑰

推播通知從伺服器（你的或 Expo 的）發出，交給 APNs，再由 APNs 送到 iPhone。所以 APNs 必須確認這台伺服器有權傳訊息給你的 app。Apple 提供兩種證明方式。

- **用 .p8 金鑰的權杖連線。** [根據 Apple](https://developer.apple.com/documentation/usernotifications/establishing-a-token-based-connection-to-apns)，這種方式更快、可以從多台伺服器使用，而且同一把金鑰可涵蓋你團隊的全部或部分 app。你的伺服器用它簽署一個權杖，至少每小時要重簽一次。
- **用 .p12 檔案的憑證連線。** [Apple 說明](https://developer.apple.com/documentation/usernotifications/establishing-a-certificate-based-connection-to-apns)這種憑證只綁定一個 app，效期一年。幾個 app 就要幾張憑證。

[Apple 關於到期的頁面](https://developer.apple.com/support/expiration/)也提到：APNs 權杖簽署金鑰永遠不會過期。

## .p8 / .p12 比較表

| 項目      | .p8 金鑰          | .p12 憑證             |
| --------- | ----------------- | --------------------- |
| 效期      | 不會過期          | 一年                  |
| 範圍      | 你所有的 app      | 單一 app              |
| 環境      | 建立時選擇        | Sandbox 與 production |
| 更新      | 不需要，除非撤銷  | 每年一次              |
| 下載      | 只能一次          | 從 Mac 匯出           |
| 數量      | 每個環境兩把      | 每個 app 一張         |
| 搭配 Expo | 由 EAS 建立與管理 | Expo 文件未提及       |

三點補充。.p8 檔案[只能下載一次](https://developer.apple.com/help/account/keys/create-a-private-key)：Apple 不會把它留在你的帳號裡。.p12 則是[從建立憑證的那台 Mac 的鑰匙圈匯出](https://developer.apple.com/help/account/capabilities/communicate-with-apns-using-a-tls-certificate)。另外，每個環境兩把的上限是針對團隊金鑰。限定特定 app 的金鑰，Apple 允許每個環境最多 200 把。

[Expo 關於憑證的頁面](https://docs.expo.dev/app-signing/app-credentials/)只談推播金鑰，並說每個帳號最多兩把，沒有區分環境，也沒有區分限定 app 的金鑰。**對 Expo app 來說，.p8 金鑰才是正確選擇。**

## Sandbox 與 production：兩個環境

APNs 有兩台伺服器：開發（也叫 sandbox）和 production。[Apple](https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns) 把前者留給測試，後者給已上架的 app。app 透過 `aps-environment` entitlement 知道要用哪一個。[根據 Apple](https://developer.apple.com/documentation/bundleresources/entitlements/aps-environment)，它的值跟著簽署描述檔走：開發描述檔是 `development`，發布描述檔和 TestFlight 則是 `production`。

**所以 TestFlight build 連的是 production，跟 App Store 完全一樣。**

Expo 這邊有兩個細節。[expo-notifications 文件](https://docs.expo.dev/versions/latest/sdk/notifications/)指出，專案裡的 entitlement 一律寫成 `development`，在 release build 的封存檔中才改成 `production`。而 Expo 權杖建立時會綁定 app 的環境（自動偵測）：傳送時你什麼都不用選。

如果你自己建立金鑰，Apple 會問你它的環境和範圍：整個團隊或特定 app。Apple 的開發者文件現在描述的是限定 sandbox 或 production 的團隊金鑰，舊的通用金鑰仍然可用。但[帳號說明頁](https://developer.apple.com/help/account/capabilities/communicate-with-apns-using-authentication-tokens)還是說金鑰兩個環境都能用。Apple 自相矛盾：以你建立時選的環境為準。**只限 sandbox 的金鑰，TestFlight 和 App Store 都用不了。**

## Expo 如何管理推播金鑰

根據 2026 年 7 月 28 日更新的 [Expo 設定指南](https://docs.expo.dev/push-notifications/push-notifications-setup/)，你的第一個開發 build 會問你要不要設定推播通知並產生 APNs 金鑰。兩個都回答是。沒用 EAS Build 的話，執行 `eas credentials`。你需要付費的 Apple Developer 帳號。

接著：

- **EAS 儲存金鑰並綁定你的 app。** 根據 Expo，一把金鑰可用於任意數量的 app，上傳新金鑰也不會改變使用者的 Expo Push 權杖。
- **你的伺服器不直接呼叫 APNs。** 它傳給 Expo 的推播服務，再由 Expo 轉給 APNs。[Expo 要求你讀取回執](https://docs.expo.dev/push-notifications/sending-notifications/)，大約在傳送 15 分鐘後：錯誤會顯示在那裡。
- **外掛負責加上 entitlement。** 在 `plugins` 裡宣告 `expo-notifications`：

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

兩個陷阱。[Expo 的 FAQ](https://docs.expo.dev/push-notifications/faq/) 說從 SDK 53 起 Expo Go 不再支援推播通知，但 expo-notifications 頁面只說 Android。不管哪種情況，Expo 都建議用開發 build：就用它測試。另外，用 `eas credentials` 刪除金鑰只會把它從 Expo 的伺服器移除，不會從你的 Apple 帳號移除。要空出名額，得到 Apple 入口網站撤銷它。跟所有機密一樣，.p8 檔案絕對不能進你的 repo：規則和 [Expo app 的 API 金鑰](https://devoria.fr/zh-hant/blog/expo-api-key-security-supabase)一樣。

## 更新：真正會過期的是什麼

**.p8 金鑰不需要更新。** 只有你撤銷它才會失效。如果你懷疑它外洩，[Apple 建議](https://developer.apple.com/help/account/capabilities/communicate-with-apns-using-authentication-tokens)先建立新金鑰、切換過去，最後才撤銷舊的。在 Expo 中替換金鑰，FAQ 給了步驟：`eas credentials`、iOS、你的 build profile，然後移除推播金鑰並產生新的。

**.p12 憑證每年過期。** Apple 要求在到期前更新，並在鑰匙圈裡留意到期日。一旦過期或被撤銷，[你就無法再傳送通知](https://developer.apple.com/help/account/certificates/certificates-overview)給你的 app。Apple 沒有另外描述更新流程：你從 app 的識別碼建立新憑證，在到期前安裝到伺服器，再移除舊的。**最好的更新方式，還是改用金鑰。**

> 你用 Intune 或其他裝置管理工具管理 iPhone？那不是同一種憑證。裝置管理用的推播憑證要在 Apple Push Certificates Portal 建立，每年更新。[Apple 建議](https://support.apple.com/guide/deployment/dep2de55389a)記下使用的 Apple 帳號：更新時會用到。

## 常見故障與修正

### 開發環境正常，TestFlight 卻不行

1. **你傳到錯的權杖。** TestFlight build 註冊在 production，會拿到新的權杖。來自開發 build 的權杖無效：當[權杖與環境不符](https://developer.apple.com/documentation/usernotifications/handling-notification-responses-from-apns)時，Apple 會回應 `BadDeviceToken`。請從 TestFlight 安裝的 app 本身取得權杖。
2. **production app 沒有推播憑證資料。** 這是 Expo FAQ 的診斷。檢查 expo.dev 上專案的憑證頁面，或執行 `eas credentials`。
3. **你的金鑰只限 sandbox。** 換成可用於 production 的金鑰。
4. **iPhone 拿不到權杖。** 如果你把 TestFlight build 裝在 Xcode build 上面，Expo 建議重新開機。

### 「No valid aps-environment entitlement string found」

根據 Expo FAQ，這個錯誤表示你的 iOS 專案沒有設定推播金鑰。檢查憑證頁面，然後重新 build：EAS 就會提議幫你產生金鑰。也確認 `expo-notifications` 確實在 `plugins` 裡。

### ITMS-90078：Missing Push Notification Entitlement

Apple 會在你上傳後寄這封信：你的 app 看起來會向 APNs 註冊，但簽署中沒有 `aps-environment` entitlement。[Expo 關於 SDK 51 的說明](https://github.com/expo/fyi/blob/main/apns-entitlement-sdk-51.md)解釋了原因。從這個 SDK 起，只有安裝了 `expo-notifications` 才會加入這個 entitlement。這個警告來自 Apple 的兩項檢查。第一項比對 app 識別碼的功能與 app 的 entitlement。第二項在二進位檔中找出註冊通知的方法，而 Expo 的程式碼在所有 app 裡都有這個方法。

**這是警告，不是退件**，Expo 特別說明。如果你有用通知，加上外掛再重新 build：[EAS 每次 build 都會同步](https://docs.expo.dev/build-reference/ios-capabilities/) app 識別碼的功能。如果沒用，就到 Apple 入口網站把識別碼的 Push Notifications 功能移除。警告可能還是會出現，但沒有影響。

### InvalidCredentials 或所有 app 同時沒聲音

回執中出現 `InvalidCredentials` 表示你的推播憑證資料已經失效，通常是因為金鑰被撤銷了。所有使用它的 app 會同時收不到通知。Expo 的修正方式：`eas credentials`，然後產生新金鑰。如果回執寫的是 `InvalidProviderToken`，Expo 建議重新產生推播金鑰和描述檔後重新 build。

## 開啟通知之前

想在上架前用 iPhone 測試通知，請看 [用 TestFlight 測試你的 app](https://devoria.fr/zh-hant/blog/testflight-test-iphone-app-before-review)。如果你會傳靜默通知來喚醒 app，請重讀 [關於背景模式的 2.5.4 準則](https://devoria.fr/zh-hant/blog/guideline-2-5-4-background-mode-rejected)。不用 Mac 的完整流程在 [沒有 Mac 也能發布 iOS app](https://devoria.fr/zh-hant/blog/publish-ios-app-without-mac-expo-eas)，上架步驟則在 [在 App Store 發布 app](https://devoria.fr/zh-hant/expertise/publish-app-on-app-store) 頁面。

第一次發送前，先做[免費診斷](https://devoria.fr/zh-hant/diagnostic)：它會告訴你專案卡在哪裡，並寄給你我的 Apple 防退件檢查清單。要打造 app、串接訂閱並上架，[iOS & Android Kit](https://devoria.fr/zh-hant/kits/ios) 會帶你走完四個步驟，附上我 app 的起始程式碼，按你的步調進行。

## 常見問題

### Apple 推播憑證會過期嗎？

.p12 憑證一年後過期，沒換掉的話你的通知就會停止。.p8 金鑰永遠不會過期：只有你撤銷它才會失效。這是優先選擇金鑰的主要原因。

### 每個 app 都需要一張推播憑證嗎？

用 .p12 憑證的話，是的：每張憑證只綁定一個 app。.p8 金鑰可用於你帳號下的所有 app。所以用 Expo 時，一把金鑰就能涵蓋你所有的 app。

### 如何更新 Apple 推播憑證？

從 app 的識別碼建立新憑證（附上新的憑證簽署請求），安裝到你的伺服器，再移除舊的。務必在到期前完成。最簡單的做法還是改用不需要更新的 .p8 金鑰。

### 我弄丟了 .p8 檔案，怎麼辦？

Apple 只允許下載一次。如果金鑰是 EAS 建立的，Expo 會保存它，你的通知照常運作。否則，建立新金鑰，用 `eas credentials` 加入，再到 Apple 入口網站撤銷舊的。使用者的 Expo Push 權杖不會改變。

### 換了推播金鑰之後需要重新 build 嗎？

一般來說不用。金鑰是給負責傳送的伺服器用的，不是給已安裝的 app，而且 Expo 表示新金鑰不會改變權杖。例外：如果回執顯示 `InvalidProviderToken`，Expo 建議用新金鑰和新描述檔重新 build。


## 你的專案

[Review Kit](https://devoria.fr/zh-hant/kits/app-review)：我的 App Store 審查檢查清單、我被 Apple 退件後的修正、Google Play 檢查清單，以及三個 Claude Code skill。$1,749。

[iOS & Android Kit](https://devoria.fr/zh-hant/kits/ios)：用 Claude Code 打造你的 iPhone 和 Android App：我自己 App 的起始程式碼、上架商店的完整流程，以及所有套件。$3,599。

[診斷：你離自己的 iPhone App 還差什麼](https://devoria.fr/zh-hant/diagnostic)：7 個問題，60 秒：找出真正卡住你發布 iPhone App 的原因，拿到你的 4 步驟路線，以及最適合你的套件。
