Fix Notification Request on Page Load
Nothing says "desperate" like a notification prompt the instant someone opens your site. Users don't know you yet, don't trust you, and will click Block without thinking. Now your notification features are dead for that user forever.
What's Happening
Your code calls Notification.requestPermission() during page load - before the user has expressed any interest in receiving notifications. The browser shows a permission prompt, the user blocks it reflexively, and that decision persists until they manually dig through browser settings to reverse it.
Chrome has started hiding these prompts entirely for sites that abuse them. Your notification request might not even be shown anymore if your site has a poor grant rate.
SEO Impact 2025
"Searcher Engagement" is now a top-tier ranking factor (weighted at 12%). Intrusive interstitials—like immediate permission prompts—cause "pogo-sticking" (users immediately bouncing back to search results). Google's algorithm detects this pattern and demotes sites that frustrate users within seconds of loading.
Diagnose
DevTools Console
Check the Lighthouse audit details for the exact source location. To find it manually:
- Open DevTools > Sources
- Search all files (
Cmd/Ctrl+Shift+F) forNotification.requestPermissionorpushManager.subscribe - Check if calls are inside event handlers (good) or running automatically (bad)
Patterns That Trigger This
// Bad: Runs immediately
Notification.requestPermission()
// Bad: Runs on page load
document.addEventListener('DOMContentLoaded', () => {
Notification.requestPermission()
})
// Bad: In module top-level
if (Notification.permission === 'default') {
Notification.requestPermission()
}
// Bad: Auto-runs after slight delay
setTimeout(() => Notification.requestPermission(), 3000)
Fix
1. Request After User Action
Move the permission request inside a user-triggered event:
Before (bad):
// Runs on page load
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
subscribeToNotifications()
}
})
After (good):
document.querySelector('#enable-notifications').addEventListener('click', () => {
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
subscribeToNotifications()
updateButtonState('enabled')
}
else {
updateButtonState('denied')
}
})
})
The user clicks a button that says "Enable notifications," the browser prompt appears, and context is clear.
2. Explain Value Before Asking
Show users what they'll get before triggering the browser prompt:
const notifyBtn = document.querySelector('#notify-toggle')
notifyBtn.addEventListener('click', async () => {
const permission = Notification.permission
if (permission === 'granted') {
// Already enabled, maybe show settings
showNotificationSettings()
}
else if (permission === 'denied') {
// Blocked - explain how to unblock
showUnblockInstructions()
}
else {
// Show our own explainer first
showNotificationExplainer()
}
})
function showNotificationExplainer() {
const modal = document.querySelector('#notification-explainer')
modal.innerHTML = `
<h3>Get notified when:</h3>
<ul>
<li>Your order ships</li>
<li>Someone replies to your comment</li>
<li>There's a flash sale</li>
</ul>
<button id="confirm-notify">Enable Notifications</button>
<button id="skip-notify">Not Now</button>
`
modal.showModal()
modal.querySelector('#confirm-notify').addEventListener('click', () => {
modal.close()
Notification.requestPermission().then(handlePermissionResult)
}, { once: true })
}
3. Wait for Engagement
Request after the user has shown investment in your site:
// Only show notification option after user has engaged
let userEngaged = false
function checkEngagement() {
// User has been on site 2+ minutes, or completed an action
const timeOnSite = Date.now() - pageLoadTime
const hasInteracted = purchaseComplete || commentPosted || signedUp
if (timeOnSite > 120000 || hasInteracted) {
userEngaged = true
showNotificationPrompt()
}
}
function showNotificationPrompt() {
if (Notification.permission !== 'default')
return
// Show soft prompt, not browser prompt
const banner = document.querySelector('#notification-banner')
banner.classList.remove('hidden')
banner.querySelector('#enable-btn').addEventListener('click', () => {
Notification.requestPermission()
banner.classList.add('hidden')
}, { once: true })
}
Verify the Fix
- Clear site data (DevTools > Application > Clear site data)
- Reload the page
- No permission prompt should appear automatically
- Navigate around, scroll, read content - still no prompt
- Click the notification enable button
- Now the browser prompt appears
Run Lighthouse - the "Requests the notification permission on page load" audit should pass.
Common Mistakes
- Using a short delay -
setTimeout(() => Notification.requestPermission(), 5000)still runs automatically. Five seconds isn't user engagement; it's just slower annoyance. - Checking permission then auto-requesting - "If not denied, request" is still automatic. The user must take action.
- Third-party chat widgets - Some live chat and support widgets request notification permission automatically. Check their configuration for a "request on user interaction" option.
- Service worker auto-subscribe - Some push notification libraries subscribe automatically. Configure them to wait for explicit opt-in.
Related
- Geolocation Permission - Same pattern, different API
- Best Practices Overview
HTTPS
Eliminate insecure HTTP requests and mixed content warnings. Learn to enforce HTTPS across your entire site for security and modern web features.
Paste Inputs
Stop preventing users from pasting into input fields. Blocking paste breaks password managers and frustrates users without improving security.