RoastMyOpsec

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.

OptionWhen it winsWatch-outTakeaway
Permissions-Policy unload=()You want no unload handlers; protect bfcacheA legacy library still depends on unloadDefault for brochure HTML once tags are cleaned up
beforeunload listenerA form with unsaved user data on that routeSite-wide trap or analytics on every pageUX prompt, not a Permissions-Policy token
pagehide / visibilitychangeBeacons and teardown without blocking bfcacheTreating them as clickjacking or CSPPreferred lifecycle hooks for public sites
Permissions-Policy device deniesMarketing pages that never need camera or geoEnabling payment on the blog for one checkout widgetStill 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 now

FAQ

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.

Sources

Related guides