Thanks for the detailed write-up and links—great question. You can keep your service worker (SW) under Anvil’s theme assets (e.g., theme/assets/fb-service-worker.js, served at /_/theme/fb-service-worker.js) and still receive Firebase Cloud Messaging (FCM) push events. The important part is explicitly registering that SW and passing its registration to Firebase via messaging.useServiceWorker(...).
Browser compatibility note: In my testing, Chrome handles this setup reliably and works as expected. However, Edge and some other browsers have shown poor results—registration may succeed but FCM push delivery does not consistently work. If you’re running into issues, test first in Chrome to confirm the integration works before troubleshooting elsewhere.
Update: v1.0.4 released
Added a concise User Guide and improved docs with structured MARK sections.
No behavior changes—just clearer instructions and examples.
See the project README and CHANGELOG in the repo for details.
By default, many examples assume Firebase will auto-load /firebase-messaging-sw.js at the root. If you move the SW and do not tell Firebase about the new location/registration, it will not be used. In Anvil, explicitly registering your SW and calling messaging.useServiceWorker(swReg) solves this.
About Service-Worker-Allowed
That header can extend scope beyond the SW’s path, but Anvil doesn’t expose custom headers for theme assets.
You do not need it for FCM push in this setup. Push events are delivered to the SW registration even if it doesn’t control your top-level page.
Quick checklist
SW is “activated” in DevTools > Application > Service Workers, source /_/theme/fb-service-worker.js.
No 404 on registering the SW.
Firebase initialized before useServiceWorker.
Token retrieval succeeds (watch console).
Background handler exists in your SW (either FCM v8’s background handler or a custom push handler calling showNotification(...)).
I will note that you should not need to create/add a fb-service-worker.js to your app if you are using the anvil-firebase as a dependent. It should fetch it from the dependency and just work.
HI Chad, I will give it some testing later, but I am a bit confused still with service workers. The documentation explicitly says that they will only affect pages in the same directory as the service worker or in a subdirectory. Anvil index.html however sits at the root whereas the service worker is under _/theme/ so I speculate it should affect only pages served under this theme location.
Here is a quote from the documentation:
A service worker can’t have a scope broader than its own location, unless the server specifies a broader maximum scope in a header on the service worker script. Use the scope option when you need a narrower scope than the default.
The following code, if included in example.com/index.html, at the root of a site, would only apply to resources under example.com/product.
if ("serviceWorker" in navigator) {
// declaring scope manually
navigator.serviceWorker.register("./sw.js", { scope: "/product/" }).then(
Do you think this might be the reason why apps dont receive notifications when closed? (on windows it edge can still run which is the default setting precisely for pwa’s)
Based on my experience, you’re mostly correct—and unfortunately, I’ve uncovered a key limitation when trying to integrate Firebase Cloud Messaging (FCM) with Anvil. The root of the issue seems to be that Anvil doesn’t currently allow placing fb-service-worker.js in the root directory, which is essential for FCM to function properly.
I spent most of yesterday digging into this, trying to improve performance and find a workaround. What I discovered is that FCM behaves inconsistently across browsers. Chrome worked reliably, but Edge was a complete no-go—notifications simply wouldn’t register or trigger. I also tested in Firefox, which showed partial functionality but still had quirks.
The problem appears to stem from how service workers are scoped. Since Anvil hosts files in subdirectories, the service worker ends up being registered outside the expected root scope, which breaks FCM’s ability to handle push events correctly. Without root-level access, the browser can’t associate the service worker with the entire app, which is a requirement for Firebase messaging.
So, unless Anvil adds support for root-level file placement—or provides a workaround for service worker scoping—this limitation will likely persist.
I hate to be the bearer of bad news if Chrome isn’t a viable option for your use case, but for now, it’s the only browser I’ve found that handles the current setup reliably. Hopefully, Anvil will address this in a future update. Until then, this might be the best we can do—or at least the best I’ve been able to figure out .
Hi Chad, thanks for giving it a thorough investigation.
I think the solution might be changing the http headers to include Service-Worker-Allowed: /
as described in: Increase scope of service worker via HTTP-Header
or I was thinking if using the routing module and make it pretend all forms should open in the _/theme/ directory. I tried looking into the documentation but I have no idea if the routing makes the browser believe the page is actually within a different directory.
That might be a better question for one of the Anvil developers, especially when it comes to how routing affects perceived directory structure in the browser. From what I understand, though, it doesn’t seem likely that routing alone would trick the browser into thinking the page is actually served from /_/theme/.
The idea of setting the Service-Worker-Allowed: / header sounds promising. If that header expands the scope as described, it could be the key to resolving this. Definitely worth testing further.
Let me know if you uncover anything else—I’m curious to see where this leads!
I dont know how we could do it with the dependency, maybe one would have to manually upload the service worker JS script file, but that is just fine I am very inexperienced in these things, but I guess this is it Let’s roll