Safety
Permissions-Policy unload vs beforeunload
January 29, 2025
Permissions-Policy unload vs beforeunload is a browser-lifecycle split, not two spellings of one header: the unload feature gates the window unload event (handlers that break back-forward cache), while beforeunload is a different event for unsaved-form prompts and is not a Permissions-Policy directive. Marketing sites should disable unused device APIs and should not rely on unload for analytics beacons — use pagehide, visibilitychange, or fetch keepalive instead. RoastMyOpsec grades that a Permissions-Policy header exists on the primary document; it does not parse unload=(), fire unload, or score GPC.
The practical difference
unload as a policy-controlled feature lets you set Permissions-Policy: unload=() (deny) or unload=self if you still have a same-origin exception. Chrome has been deprecating unload because those handlers make bfcache ineligible. Third-party tags that still listen for unload are why a document policy belongs next to your tracker diet.
beforeunload is for 'you have unsaved changes.' It is not camera, geolocation, or unload. Abusing it for analytics or to trap tab-close is hostile UX and still not a substitute for Permissions-Policy on device APIs. pagehide and visibilitychange are the usual replacements for teardown work.
How to choose based on what you shipped
Brochure, blog, docs: ship a tight Permissions-Policy that turns off camera, microphone, geolocation, and payment unless a named route needs them. Add unload=() when you want to opt out of unload handlers immediately rather than waiting on browser deprecation. Checkout with a multi-step form may keep beforeunload on that route only.
Do not copy unload=(*) site-wide to silence a console warning from a tag manager. Remove the handler or sandbox the iframe. Feature-Policy is the old header name; send Permissions-Policy on current stacks.
| Option | When it wins | Watch-out | Takeaway |
|---|---|---|---|
| Permissions-Policy unload=() | You want no unload handlers; protect bfcache | A legacy library still depends on unload | Default for brochure HTML once tags are cleaned up |
| beforeunload listener | A form with unsaved user data on that route | Site-wide trap or analytics on every page | UX prompt, not a Permissions-Policy token |
| pagehide / visibilitychange | Beacons and teardown without blocking bfcache | Treating them as clickjacking or CSP | Preferred lifecycle hooks for public sites |
| Permissions-Policy device denies | Marketing pages that never need camera or geo | Enabling payment on the blog for one checkout widget | Still the header RoastMyOpsec actually looks for |
When beforeunload still wins
beforeunload still wins on an editor or checkout form where losing the draft is worse than a prompt. It does not win on a homepage, a blog post, or as a replacement for sendBeacon. unload=() still wins when you have retired unload listeners and want the policy to keep them retired.
Neither control replaces CSP or framing. A Permissions-Policy that only mentions unload and still allows camera is not a marketing-site policy.
Common mistakes
The first mistake is using unload to fire analytics and then wondering why bfcache never hits.
The second mistake is treating beforeunload as a Permissions-Policy feature you can set in the header.
The third mistake is a missing Permissions-Policy entirely while arguing about unload syntax.
What a URL roast can prove
The free header pack flags a missing Permissions-Policy. It does not parse individual directives such as unload, camera, or geolocation, does not score Global Privacy Control, and does not dispatch unload or beforeunload. Device-API tightness is still your job after the header exists. The same roast still checks CSP quality, HSTS, framing, Referrer-Policy, nosniff, HTTPS scheme, session-like cookies, paths, and public JS secrets. Header and URL read only — no exploit payloads, no HSTS preload submit.
Free audit the URL you own
RoastMyOpsec is a defensive public-surface roast: headers, cookies, sensitive paths, and more — no exploit payloads. Start with the free audit, then open the vault if the blurred findings look expensive.
Free audit nowFAQ
- Is beforeunload the same as Permissions-Policy unload?
- No. unload in Permissions-Policy gates the unload event. beforeunload is a separate event for unsaved-changes prompts. You cannot set beforeunload in Permissions-Policy.
- Does RoastMyOpsec require unload=()?
- No. It checks that Permissions-Policy is present. It does not score the unload feature or GPC.
- Should a marketing site keep unload handlers?
- Usually no. They hurt bfcache and are being deprecated. Use pagehide or visibilitychange for beacons, and beforeunload only where a form draft matters.