Web Push kurulumu
Web Push bir standarttır (RFC 8030 protokol, RFC 8291 şifreleme, RFC 8292 VAPID); Chrome, Edge, Firefox, Safari — ve kurulu PWA'lar için iOS Safari — uygular. Tek uygulama hepsine ulaşır; yalnızca endpoint'teki push servisi host'u değişir.
A Bölümü — VAPID anahtar çifti
Çift sunucunuzu push servislerine tanıtır. Uygulama başına bir kez üretin ve asla değiştirmeyin.
Panel: Kimlik bilgileri › Web Push → konu girin → VAPID anahtarı üret. Public key kartta görünür.

Komut satırı:
bun run generate:keys -- --vapid
# VAPID_PUBLIC_KEY=BNcRd…
# VAPID_PRIVATE_KEY=…
bun run app:create -- --name "Acme" --slug acme \
--vapid-public "BNcRd…" --vapid-private "…" \
--vapid-subject mailto:[email protected]
Konu, trafiğiniz sorun çıkarırsa push servisi işletmecisinin size ulaşabileceği bir mailto: ya da https:// URL'idir.
Her tarayıcı aboneliği oluşturulduğu applicationServerKey'e bağlıdır. Anahtarı değiştirin, mevcut her abonelik sessizce ölür. Panel bu yüzden yapılandırılmış çifti yeniden üretmenize izin vermez.
B Bölümü — Siteniz
1 HTTPS. Service worker ve push güvenli bağlam ister (localhost hariç).
2 Her sayfadan bağlanan, display: standalone ya da fullscreen olan bir web app manifest. Chrome ve Safari siteyi ancak bununla kurulabilir sayar — iOS push'u ancak bununla teslim eder.
{
"name": "Acme Shop",
"short_name": "Acme",
"start_url": "/?source=pwa",
"display": "standalone",
"background_color": "#000000",
"theme_color": "#000000",
"icons": [
{ "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" },
{ "src": "/icons/512-maskable.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }
]
}
<link rel="manifest" href="/manifest.json">
3 Site kökünde bir service worker (/sw.js). Web SDK hazır derlenmiş bir tane taşır; bkz. Web SDK › Service worker. Kapsayacağı scope'tan sunulmalı — /sw.js, /static/js/sw.js değil.
4 API'de origin'inize izin verin: CORS_ORIGINS'e ekleyin (https://www.acme.example). Tarayıcılar /v1/subscriptions ve /v1/e/*'ı doğrudan çağırır.
C Bölümü — Tıklamadan abone olma
Tarayıcının izin istemi kullanıcı hareketiyle tetiklenmelidir. Sayfa yüklenirken çağrılırsa Safari'de sessizce reddedilir, başka yerlerde giderek daha çok kısıtlanır.
import { init } from "@opennotification/web";
const on = init({
endpoint: "https://push.acme.example",
appKey: "pk_live_…",
vapidKey: "BNcRd…", // A bölümündeki public key
});
button.addEventListener("click", async () => {
const result = await on.subscribe("user_123"); // ya da subscribe({ externalId, tags, language })
if (!result.ok) console.warn(result.error.code);
});
iOS Safari — yayınlamadan önce okuyun
iOS 16.4'ten beri Safari web push destekler, ama yalnızca Ana Ekrana eklenmiş bir web uygulaması için. Sıradan bir Safari sekmesinde window.PushManager hiç yoktur ve hiçbir kod bunu değiştiremez.
| Gereksinim | Notlar |
|---|---|
| iOS ≥ 16.4 | Öncesinde hiçbir şey yok. |
display: standalone / fullscreen manifest | Yoksa "Ana Ekrana Ekle" PWA değil yer imi yapar. |
| Kullanıcı Paylaş → Ana Ekrana Ekle'ye dokunur | iOS'ta programatik kurulum ve beforeinstallprompt yok. Nasıl yapılacağını göstermelisiniz. |
Bir dokunma handler'ı içinde Notification.requestPermission() | Başka her şey sessizce reddedilir. |
| Ana Ekran simgesinden açılmış | Aynı site Safari sekmesinde push'u olmayan farklı bir bağlamdır. |
Web SDK bu durumu algılar (capability().reason === "IOS_NEEDS_INSTALL") ve İngilizce ile Türkçe hazır bir kurulum rehberi (mountInstallPrompt) içerir. Rehberi atlamak iOS web push dönüşümünü sıfıra indirir.
iOS'ta desteklenmeyip yok sayılanlar: actions (butonlar), image, silent, renotify, vibrate. Desteklenenler: title, body, icon, badge (Badging API ile uygulama simgesi sayısı), tag, data.
Tarayıcı matrisi
| Tarayıcı | Push | Gereksinim | Notlar |
|---|---|---|---|
| Chrome / Edge / Brave (masaüstü) | ✅ | HTTPS | Tam destek, 2 aksiyon butonu |
| Firefox (masaüstü) | ✅ | HTTPS | Tam destek |
| Safari (macOS 13+) | ✅ | HTTPS | Aksiyon butonu yok |
| Chrome / Samsung Internet / Firefox (Android) | ✅ | HTTPS | Tam destek |
| Safari (iOS 16.4+) | ⚠️ | Kurulu PWA | Sınırlı özellik, yukarıya bakın |
| Chrome / Firefox / Edge (iOS) | ❌ | — | WebKit kabukları; PWA kuramaz |
Yük sınırı ve şifreleme
Her push worker tarafından o aboneliğe özel şifrelenir (RFC 8291 aes128gcm). Push servisleri şifreli gövdeyi 4 KB ile sınırlar; sunucunun kullandığı kayıt boyutuyla kullanılabilir düz metin 3 993 bayt. Uzun gövde + büyük data buna takılır; sihirbaz yazarken bayt sayısını gösterir ve büyük yük gönderilmeden 413/PAYLOAD_TOO_LARGE ile düşer.
Sunucuda hata yönetimi
| Durum | Worker |
|---|---|
201 | Push servisine teslim edildi. |
404, 410 | Abonelik gitmiş. Geçersiz kılınır, tekrar denenmez. |
413 | Çok büyük. O push için kalıcı. |
429 | Retry-After'a uyar, en çok 3 deneme. |
400, 401, 403 | VAPID ya da yük sorunu — konuyu ve anahtarın sitenin abone olduğu anahtarla aynı olduğunu kontrol edin. |
Tarayıcı aboneliği sessizce yenilerse (pushsubscriptionchange) SDK'nın service worker'ı POST /v1/subscriptions/rotate çağırır ve satır kaybolmak yerine güncellenir.