Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ServiceWorker doesn't update index.html when headers (e.g. CSP) change #37390

Open
wibimaster opened this issue Jun 2, 2020 · 19 comments
Open
Labels
area: service-worker Issues related to the @angular/service-worker package freq1: low P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent state: confirmed type: use-case workaround2: non-obvious
Milestone

Comments

@wibimaster
Copy link

wibimaster commented Jun 2, 2020

🐞 bug report

Affected Package

The issue is caused by package @angular/service-worker:9.1.7

Is this a regression?

Nope, seems to always been there

Description

Supposition: It seems that the service worker use body response only to generate its hash, corresponding to versioned resources ; update some headers server side doesn't update these hashes and resources are served with old headers.

🔬 Minimal Reproduction

When deploying Angular application with service-worker (default configuration):

  • Load the main page in a browser
  • Service worker do its job and cache resources (including index.html)
  • Change global resources header (in my case, nginx configuration, like add_header X-OneThing "hello there"
  • Let the service worker try to do a cache bust
  • Take a look on the answer : no update detected
  • Refresh the page : the new header isn't applied (but the ngsw.json HTTP call got it, of course)

Sorry but cannot do a stackblitz with that :/

🔥 Exception or Error

None

🌍 Your Environment

Angular Version:


> ng "version"


     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / △ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/
    

Angular CLI: 9.1.6
Node: 12.14.1
OS: linux x64

Angular: 9.1.7
... animations, common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router
Ivy Workspace: Yes

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.901.6
@angular-devkit/build-angular     0.901.6
@angular-devkit/build-optimizer   0.901.6
@angular-devkit/build-webpack     0.901.6
@angular-devkit/core              9.1.6
@angular-devkit/schematics        9.1.6
@angular/cli                      9.1.6
@angular/platform-server          9.1.9
@angular/service-worker           9.1.9
@ngtools/webpack                  9.1.6
@schematics/angular               9.1.6
@schematics/update                0.901.6
rxjs                              6.5.5
typescript                        3.8.3
webpack                           4.42.0
@pkozlowski-opensource pkozlowski-opensource added the area: service-worker Issues related to the @angular/service-worker package label Jun 2, 2020
@ngbot ngbot bot added this to the needsTriage milestone Jun 2, 2020
@gkalpak
Copy link
Member

gkalpak commented Jun 2, 2020

Thx for reporting this, @wibimaster. Can you please clarify what you mean by "Let the service worker trying to do a cache burst"?

@gkalpak gkalpak added needs reproduction This issue needs a reproduction in order for the team to investigate further triage #1 labels Jun 2, 2020
@wibimaster
Copy link
Author

@gkalpak
Sure, I mean the HTTP calls to: /ngsw.json?ngsw-cache-bust=0.440594490857368

I write a loop following the documentation to call this every five seconds, for testing purpose ;)

const appIsStable$ = this.appRef.isStable.pipe(first(isStable => isStable === true));
const msInterval$ = interval(5 * 1000);
const everyMsIntervalOnceAppIsStable$ = concat(appIsStable$, msInterval$);
everyMsIntervalOnceAppIsStable$.subscribe(() => this.swUpdate.checkForUpdate();

@gkalpak
Copy link
Member

gkalpak commented Jun 2, 2020

I see. In that case, the SW will not re-request the files and get the new headers. Since we are talking about hashed asset requests (i.e. requests for files that exist at build time, not some dynamically generated API request), the SW only cares about their content. Among other reasons, it is because it uses the content hash to ensure that a specific file version corresponds to an app version (as specified in the corresponding ngsw.json).

What is your usecase? Why do you care about headers of static assets?

@wibimaster
Copy link
Author

My specific use case is about CSP headers ; if I change them because of a bug, too strict, or whatever, the application is locked in a bad state...

Not all static assets are concerned : mainly "index.html" because it loads main javascript files.
A simple typo into CSP headers and JS are not loaded and the application will be broken if it's cached :/

@gkalpak gkalpak added freq1: low severity3: broken workaround2: non-obvious type: use-case and removed needs reproduction This issue needs a reproduction in order for the team to investigate further labels Jun 5, 2020
@gkalpak gkalpak changed the title Service worker doesn't use headers to generate hashes ServiceWorker doesn't update index.html when headers (e.g. CSP) change Jun 5, 2020
@gkalpak
Copy link
Member

gkalpak commented Jun 5, 2020

Oh, I see. That sounds like a valid usecase indeed.

If it's only about index.html, we could change the ServiceWorker to always fetch ngsw.json > index from the server. If more files/URLs are affected, then I am not sure what would be a good solution/API.

In the meantime, a work-around for index.html is to update its content in any way to make its hash different (e.g. add the current revision SHA in a comment or something).

@dsm0880
Copy link

dsm0880 commented Sep 21, 2020

@gkalpak Just want to bring this back up as a possible thing to address. We are hitting the same issue, I'm guessing as CSP becomes more standard this issue is going to become more problematic. There is another ticket #37631 which is closely related. Overall focusing on proper CSP support and documentation may be a good thing.

@gkalpak
Copy link
Member

gkalpak commented Sep 22, 2020

Can someone confirm that this is only about ngsw.json > index or whether other resources are affected as well?
BTW, #38565 will add the ability to send all navigation requests through to the network, which offers another workaround for this issue (althought personally, I would most likely use the workaround described in #37390 (comment)).

Also, does anyone happen to know how other SW implementation (such as Workbox) handle this (if at all)?

@ngbot ngbot bot modified the milestones: needsTriage, Backlog Sep 22, 2020
@anhhnguyen206
Copy link

anhhnguyen206 commented Sep 22, 2020

@gkalpak I'm having a same issue as well. My use case is similar. I have an Angular web app that uses Angular Service Worker. I then have the needs to add some CSP headers (to allow our web to download some images from a new source) to our web server configuration (in our case it's IIS). However the service worker won't pick up the newly added CSP headers unless we manually unregister the service worker or clear our browser cache (which we unfortunately cannot ask our end users to do). This creates a broken state because our application needs to download some additional images but were restricted in the old CSP headers. If I bypass the service worker by checking the "Bypass Service Worker" checkbox in Chrome Dev Tools -> Application -> Service Worker, then everything works.

My current workaround is during build process I copy the ngsw-worker.js into ngsw-worker-v2.js and copy safety-worker.js into ngsw-worker.js. And in our app.module.ts we just register "./ngsw-worker-v2.js". This would allow the browsers to unregister old service workers and register new service workers which at that point refresh all the CSP headers. However it is a workaround so it's not ideal I think.

@gkalpak
Copy link
Member

gkalpak commented Sep 22, 2020

@anhhnguyen206, does the workaround mentioned in #37390 (comment) work for you?

@anhhnguyen206
Copy link

@gkalpak I tried that and it didn't work. I confirmed the hash for index.html is also different.

@dsm0880
Copy link

dsm0880 commented Sep 22, 2020

@gkalpak and @anhhnguyen206 I have a theory but it might be due to my misunderstanding of how service workers function. The CSP headers need to be located on the file that makes the calls correct? If so, one concern I have is the ngsw-worker.js file itself. If the service worker makes a fetch then it will violate the connect-src policy if the headers returned on the ngsw-worker.js file are old. So you ask yourself when will the ngsw-worker.js update? I don't know :) I'm guessing that code does not change often and it is not part of the ngsw.json manifest. Could this be part of the issue?

@gkalpak
Copy link
Member

gkalpak commented Sep 22, 2020

@anhhnguyen206:
I suspect your problem is different then. I suggest you try to create a minimal reproduction and open a new issue if it turns out to be a problem with Angular. See here for some useful tips on debugging the Angular SW.

(It is also possible that you have not configured the SW correctly wrt CSP headers. See below for some references that might help 👇)

@dsm0880:
There are indeed some nuances that one should be aware of regarding SW and CSP headers. The browser does indeed not take headers into account when updating the SW script itself. However, the browsers are guaranteed to not use a cached SW script that is more than 24 hours old (see also #35491 (comment) for more details and links to other resources). So, your users should get your updates no later than 24 hours after you deploy them.

Also #31330 has some info (and links to more resources) explaining how CSP headers must be configured for the SW script intself (in order to be able to serve CSP-restricted resources from the page).

I'm afraid, in order to be able to make any progress on this, we'll need:

  • A clear description of the problem and which assets are affected.
  • A minimal reproduction with exact steps to perform to see the problem.

@Splaktar
Copy link
Member

Sorry but cannot do a stackblitz with that :/

In those cases, it's often best to create a GitHub repository and put the reproduction steps and details in the README.

@jelbourn jelbourn added P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent and removed severity3: broken labels Oct 1, 2020
@ahimik
Copy link

ahimik commented Feb 18, 2022

Are there are any updates on this?

I also experienced similar issue.

In my case I generate random CSP dynamically at server level for each index.html request.
When I first time open the page everything works fine up until I reset browser's cache.
Once the cache is refreshed the new index.html page gets requested by the browser with the new CSP nonce in headers and cookie value.
If I refresh the page again, service worker retrieves outdated index.html from cache with the different headers values so I receive CSP nonce mismatch error.

It would be nice if service worker could update cached index.html on each request being made by browser.

@gkalpak
Copy link
Member

gkalpak commented Feb 18, 2022

I haven't seen any updates since my last comment #37390 (comment) 😉

If you want to hit the server on each navigation request, you can use the navigationRequestStrategy configuration option, @ahimik.

@Masinac
Copy link

Masinac commented Dec 21, 2022

I'm currently experiencing the same problem.

@nthornton2010
Copy link

Same problem here too

@jspivack
Copy link

Definitely still an issue

@pmachauxOrigin8Cares
Copy link

Still an issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: service-worker Issues related to the @angular/service-worker package freq1: low P3 An issue that is relevant to core functions, but does not impede progress. Important, but not urgent state: confirmed type: use-case workaround2: non-obvious
Projects
None yet
Development

No branches or pull requests