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

PWA / Service worker breaking production deployments #2440

Closed
ro-savage opened this Issue Jun 1, 2017 · 19 comments

Comments

Projects
None yet
9 participants
@ro-savage
Contributor

ro-savage commented Jun 1, 2017

EDIT: Issue appears to be with Firebase only

Service workers aren't refreshing index.html and therefore new deploys are showing old bundles.

This means production websites or really all deployed sites that use a single url/domain can not be updated.

PR #2441 - Is a hotfix (if needed) that just removes the service worker and updates docs to say they have been disabled temporarily.

@gaearon @Timer @jeffposnick @addyosmani

Steps to reproduce

  • create-react-app sw-test
  • cd sw-test
  • npm install
  • npm run build
  • cd build

Deploy with firebase

  • firebase deploy
  • Visit site https://[project].firebaseapp.com. Confirm working.
  • Change app.js
  • npm run build
  • cd build
  • deploy firebase

Check website

  • Visit site again and no changes shown. DevTools shows SW is providing index.html and old bundles.
  • Force refresh a few times. No difference.
  • Delete service worker.
  • Refresh.
  • See correctly updated site.

Can someone else confirm this is affecting them?

I am hoping I am doing something wrong and this isn't affecting every production deploy.

@ro-savage

This comment has been minimized.

Show comment
Hide comment
@ro-savage

ro-savage Jun 1, 2017

Contributor

Additionally, you can not remove the service worker because if you try to unregister in a new deploy, the user will never see the updated bundle.

Contributor

ro-savage commented Jun 1, 2017

Additionally, you can not remove the service worker because if you try to unregister in a new deploy, the user will never see the updated bundle.

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon
Member

gaearon commented Jun 1, 2017

@ro-savage ro-savage changed the title from CRITICAL: PWA / Service worker not refreshing on deployment in production to CRITICAL: PWA / Service worker breaking production deployments Jun 1, 2017

@bunshar

This comment has been minimized.

Show comment
Hide comment
@bunshar

bunshar Jun 1, 2017

Contributor

Looks like something to do with "now". Works for me - Checked with "surge" and localhost

Contributor

bunshar commented Jun 1, 2017

Looks like something to do with "now". Works for me - Checked with "surge" and localhost

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon
Member

gaearon commented Jun 1, 2017

cc @rauchg

@ro-savage

This comment has been minimized.

Show comment
Hide comment
@ro-savage

ro-savage Jun 1, 2017

Contributor

Thank you @bunshar !

I tried surge on a new project built from scratch. And it working as expected.

Deployed same app on now and its working as expected.

No idea what happened or what I did different.

Happy to close this and assume I managed to screw something up multiple times and over-react.

I will keep an eye on my projects and see if I can narrow down what happened.

Contributor

ro-savage commented Jun 1, 2017

Thank you @bunshar !

I tried surge on a new project built from scratch. And it working as expected.

Deployed same app on now and its working as expected.

No idea what happened or what I did different.

Happy to close this and assume I managed to screw something up multiple times and over-react.

I will keep an eye on my projects and see if I can narrow down what happened.

@ro-savage ro-savage closed this Jun 1, 2017

@ro-savage ro-savage changed the title from CRITICAL: PWA / Service worker breaking production deployments to PWA / Service worker breaking production deployments Jun 1, 2017

@ro-savage

This comment has been minimized.

Show comment
Hide comment
@ro-savage

ro-savage Jun 1, 2017

Contributor

Still running into issues on firebase.

Deploying same newly create app to now, surge and firebase.

now and surge are updating. firebase is not. (newly created project: https://cra-sw.firebaseapp.com/)

Will see if its working tomorrow and will contact firebase and see what they say.

Contributor

ro-savage commented Jun 1, 2017

Still running into issues on firebase.

Deploying same newly create app to now, surge and firebase.

now and surge are updating. firebase is not. (newly created project: https://cra-sw.firebaseapp.com/)

Will see if its working tomorrow and will contact firebase and see what they say.

@ro-savage

This comment has been minimized.

Show comment
Hide comment
@ro-savage

ro-savage Jun 1, 2017

Contributor

Just to prove I am not crazy and this is a thing on firebase.

Deploying changes with serve, now, surge and firebase.
Approximately 3 minutes
https://www.youtube.com/watch?v=oYy-WqkgS1Q

Contributor

ro-savage commented Jun 1, 2017

Just to prove I am not crazy and this is a thing on firebase.

Deploying changes with serve, now, surge and firebase.
Approximately 3 minutes
https://www.youtube.com/watch?v=oYy-WqkgS1Q

@gaearon gaearon reopened this Jun 1, 2017

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jun 1, 2017

Member

Let’s keep it open and wait for @jeffposnick to help with diagnosing this.

Member

gaearon commented Jun 1, 2017

Let’s keep it open and wait for @jeffposnick to help with diagnosing this.

@jeffposnick

This comment has been minimized.

Show comment
Hide comment
@jeffposnick

jeffposnick Jun 1, 2017

Contributor

What you're running into with https://cra-sw.firebaseapp.com/ is described in item 5 of the "Offline-First Considerations", and is due to service-worker.js being served with HTTP caching headers.

screen shot 2017-06-01 at 10 54 07 am

It's unfortunately a common pain point, since many hosting providers will default to a medium-length cache lifetime for assets like .js files. Updating your web app's assets relies on detecting an updating service-worker.js file, and that is complicated when the browser's cache comes into play. (The cache headers are always ignored if the file is over 24 hours old; it there's more historical detail here.)

The service worker specification was changed to cause HTTP caching of service-worker.js to only happen when a developer explicitly opts-in, and Firefox has already implemented that change in FF53+.

Chrome has unfortunately not caught up, and still honors the HTTP caching headers. I was just asking about the status of this internally yesterday, and I'll point the relevant folks to these discussions, given the larger audience that is likely to inadvertently run into this.

Contributor

jeffposnick commented Jun 1, 2017

What you're running into with https://cra-sw.firebaseapp.com/ is described in item 5 of the "Offline-First Considerations", and is due to service-worker.js being served with HTTP caching headers.

screen shot 2017-06-01 at 10 54 07 am

It's unfortunately a common pain point, since many hosting providers will default to a medium-length cache lifetime for assets like .js files. Updating your web app's assets relies on detecting an updating service-worker.js file, and that is complicated when the browser's cache comes into play. (The cache headers are always ignored if the file is over 24 hours old; it there's more historical detail here.)

The service worker specification was changed to cause HTTP caching of service-worker.js to only happen when a developer explicitly opts-in, and Firefox has already implemented that change in FF53+.

Chrome has unfortunately not caught up, and still honors the HTTP caching headers. I was just asking about the status of this internally yesterday, and I'll point the relevant folks to these discussions, given the larger audience that is likely to inadvertently run into this.

@addyosmani

This comment has been minimized.

Show comment
Hide comment
@addyosmani

addyosmani Jun 1, 2017

cc'ing @davideast from Firebase as an FYI regarding the current cache-control max-age above. Jeff's summary is quite accurate but it would be good to get a check-in on the medium-length lifetime for static assets. Checking back in on #2441, it appears this is now only an issue for Firebase hosted content vs other hosts.

Chrome has unfortunately not caught up, and still honors the HTTP caching headers. I was just asking about the status of this internally yesterday, and I'll point the relevant folks to these discussions, given the larger audience that is likely to inadvertently run into this.

I'm going to chase trying to get some resource behind working on this internally.

addyosmani commented Jun 1, 2017

cc'ing @davideast from Firebase as an FYI regarding the current cache-control max-age above. Jeff's summary is quite accurate but it would be good to get a check-in on the medium-length lifetime for static assets. Checking back in on #2441, it appears this is now only an issue for Firebase hosted content vs other hosts.

Chrome has unfortunately not caught up, and still honors the HTTP caching headers. I was just asking about the status of this internally yesterday, and I'll point the relevant folks to these discussions, given the larger audience that is likely to inadvertently run into this.

I'm going to chase trying to get some resource behind working on this internally.

@mbleigh

This comment has been minimized.

Show comment
Hide comment
@mbleigh

mbleigh Jun 1, 2017

For those who come across this issue, here is how you should be able to fix it for Firebase Hosting. Edit your firebase.json and add headers like so:

{
  "hosting": {
    "headers": [
      { "source":"/service-worker.js", "headers": [{"key": "Cache-Control", "value": "no-cache"}] }
    ]
  }
}

mbleigh commented Jun 1, 2017

For those who come across this issue, here is how you should be able to fix it for Firebase Hosting. Edit your firebase.json and add headers like so:

{
  "hosting": {
    "headers": [
      { "source":"/service-worker.js", "headers": [{"key": "Cache-Control", "value": "no-cache"}] }
    ]
  }
}
@antmarot

This comment has been minimized.

Show comment
Hide comment
@antmarot

antmarot Jun 1, 2017

Thanks @mbleigh, it works!
There is a typo in your json though: firebase expects a headers array instead of a header object. Here is the adapted json:

{
  "hosting": {
    "headers": [
      { "source":"/service-worker.js", "headers": [{"key": "Cache-Control", "value": "no-cache"}] }
    ]
  }
}

antmarot commented Jun 1, 2017

Thanks @mbleigh, it works!
There is a typo in your json though: firebase expects a headers array instead of a header object. Here is the adapted json:

{
  "hosting": {
    "headers": [
      { "source":"/service-worker.js", "headers": [{"key": "Cache-Control", "value": "no-cache"}] }
    ]
  }
}
@mbleigh

This comment has been minimized.

Show comment
Hide comment
@mbleigh

mbleigh Jun 1, 2017

Thanks, I updated my snippet to match yours. That's what I get for assuming I remember all of my product's config from the top of my head. 🤦‍♂️

mbleigh commented Jun 1, 2017

Thanks, I updated my snippet to match yours. That's what I get for assuming I remember all of my product's config from the top of my head. 🤦‍♂️

@ro-savage

This comment has been minimized.

Show comment
Hide comment
@ro-savage

ro-savage Jun 2, 2017

Contributor

@jeffposnick - Thanks for clearing things up. That makes a lot more sense.

I had index.html cache-control set to 0 but all .js files are set to 3600 on the test cw-app and 7200 on my production app. Because you know, cache busting.

@mbleigh @antmarot - That configuration works great for firebase. Thanks.

Does this mean that service-worker.js is being modified with every deployment, so that it points at the correct bundles?
If so, does it make sense to cache-bust the service worker as well?


In terms of those steps in the Offline-First Considerations. Force-refresh (and having no-cache on) doesn't seem to fix the issue, maybe Chrome doesn't refresh the service worker?

Looks like maybe it's a Chrome issue?

In fact, I can see chrome saying that service-worker.js is being served from disk cache. Despite cache being disabled and force-refresh. (58.0.3029.110 & Canary 61.0.3117.0)

@addyosmani - Is this deliberate? (Meaning developers need to check 'update on reload' option in Chrome to force no-cache?)

Chrome regular
image

Chrome Canary
image

Can also confirm was not working in Firefox 52 but is now working in Firefox 54.

Contributor

ro-savage commented Jun 2, 2017

@jeffposnick - Thanks for clearing things up. That makes a lot more sense.

I had index.html cache-control set to 0 but all .js files are set to 3600 on the test cw-app and 7200 on my production app. Because you know, cache busting.

@mbleigh @antmarot - That configuration works great for firebase. Thanks.

Does this mean that service-worker.js is being modified with every deployment, so that it points at the correct bundles?
If so, does it make sense to cache-bust the service worker as well?


In terms of those steps in the Offline-First Considerations. Force-refresh (and having no-cache on) doesn't seem to fix the issue, maybe Chrome doesn't refresh the service worker?

Looks like maybe it's a Chrome issue?

In fact, I can see chrome saying that service-worker.js is being served from disk cache. Despite cache being disabled and force-refresh. (58.0.3029.110 & Canary 61.0.3117.0)

@addyosmani - Is this deliberate? (Meaning developers need to check 'update on reload' option in Chrome to force no-cache?)

Chrome regular
image

Chrome Canary
image

Can also confirm was not working in Firefox 52 but is now working in Firefox 54.

@jeffposnick

This comment has been minimized.

Show comment
Hide comment
@jeffposnick

jeffposnick Jun 2, 2017

Contributor

Does this mean that service-worker.js is being modified with every deployment, so that it points at the correct bundles?

Yes, service-worker.js is modified with every deployment. It contains versioning information for all of the URLs it knows about—including URLs like index.html that can't be renamed to include inline hashes (no one will navigate to index.<md5>.html). Someone's written up a very thorough explainer that goes into all the details if you're curious.

If so, does it make sense to cache-bust the service worker as well?

The best practice is to keep the service worker file name constant (i.e. service-worker.js, sw.js or whatnot) instead of including a hash in the file name.

Including a hash in the file name means that you couldn't serve the file that contains the navigator.serviceWorker.register('service-worker.<md5>.js') statement using a cache-first strategy, since if you did serve it cache-first, you'd never pick up the new <md5> value.

Keeping the file name consistent, and relying on the service worker update flow to handle cache updates, means that you can serve all your static content cache-first. But it also means that if your service-worker.js is served with HTTP cache headers enabled, Chrome currently will respect those headers for up to 24 hours, leading to the frustration that you bumped up against in this issue.

Contributor

jeffposnick commented Jun 2, 2017

Does this mean that service-worker.js is being modified with every deployment, so that it points at the correct bundles?

Yes, service-worker.js is modified with every deployment. It contains versioning information for all of the URLs it knows about—including URLs like index.html that can't be renamed to include inline hashes (no one will navigate to index.<md5>.html). Someone's written up a very thorough explainer that goes into all the details if you're curious.

If so, does it make sense to cache-bust the service worker as well?

The best practice is to keep the service worker file name constant (i.e. service-worker.js, sw.js or whatnot) instead of including a hash in the file name.

Including a hash in the file name means that you couldn't serve the file that contains the navigator.serviceWorker.register('service-worker.<md5>.js') statement using a cache-first strategy, since if you did serve it cache-first, you'd never pick up the new <md5> value.

Keeping the file name consistent, and relying on the service worker update flow to handle cache updates, means that you can serve all your static content cache-first. But it also means that if your service-worker.js is served with HTTP cache headers enabled, Chrome currently will respect those headers for up to 24 hours, leading to the frustration that you bumped up against in this issue.

@ro-savage

This comment has been minimized.

Show comment
Hide comment
@ro-savage

ro-savage Jun 3, 2017

Contributor

Thanks for the through explanation @jeffposnick, very useful info.

Contributor

ro-savage commented Jun 3, 2017

Thanks for the through explanation @jeffposnick, very useful info.

@eranhirs

This comment has been minimized.

Show comment
Hide comment
@eranhirs

eranhirs Oct 2, 2017

Thanks for the help! 👍

It's very frustrating to find this solution, as I didn't even know about the existence of service workers. (Which is actually pretty cool that it's bundled so good into CRA).

Please add it to the docs to the Troubleshooting section for other users.

eranhirs commented Oct 2, 2017

Thanks for the help! 👍

It's very frustrating to find this solution, as I didn't even know about the existence of service workers. (Which is actually pretty cool that it's bundled so good into CRA).

Please add it to the docs to the Troubleshooting section for other users.

@Andersos Andersos referenced this issue Oct 13, 2017

Closed

Deploy before testing #110

6 of 6 tasks complete

Andersos added a commit to Andersos/morland-sandvik.com that referenced this issue Nov 18, 2017

@gaearon

This comment has been minimized.

Show comment
Hide comment
@gaearon

gaearon Jan 8, 2018

Member

Closing in favor of #2398.

Member

gaearon commented Jan 8, 2018

Closing in favor of #2398.

@gaearon gaearon closed this Jan 8, 2018

@lusan

This comment has been minimized.

Show comment
Hide comment
@lusan

lusan Feb 1, 2018

Hi everyone, sorry for asking a similar question in a closed issue, thought it is related to it and the solution might be simple.

I am trying to set cache header for sw with nginx, in the configuration like it has been done in firebase, I tried

location /service-worker.js {
    add_header Cache-Control "no-cache";
    proxy_cache_bypass $http_pragma;
    proxy_cache_revalidate on;
    expires off;
    access_log off;
}

However when I load my page, sw registration fails with the message.

A bad HTTP response code (404) was received when fetching the script. registerServiceWorker.js:71 Error during service worker registration: TypeError: Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script.

@gaearon @Timer @jeffposnick @addyosmani @mbleigh

Can someone please help me with this?

lusan commented Feb 1, 2018

Hi everyone, sorry for asking a similar question in a closed issue, thought it is related to it and the solution might be simple.

I am trying to set cache header for sw with nginx, in the configuration like it has been done in firebase, I tried

location /service-worker.js {
    add_header Cache-Control "no-cache";
    proxy_cache_bypass $http_pragma;
    proxy_cache_revalidate on;
    expires off;
    access_log off;
}

However when I load my page, sw registration fails with the message.

A bad HTTP response code (404) was received when fetching the script. registerServiceWorker.js:71 Error during service worker registration: TypeError: Failed to register a ServiceWorker: A bad HTTP response code (404) was received when fetching the script.

@gaearon @Timer @jeffposnick @addyosmani @mbleigh

Can someone please help me with this?

BlakeWilliams added a commit to BlakeWilliams/osquery-site that referenced this issue Feb 13, 2018

Don't cache service worker file
facebook/create-react-app#2440

This should resolve an issue where `index.html` is cached and pointing
at an invalid JavaScript bundle.

@ri7nz ri7nz referenced this issue Apr 3, 2018

Merged

Firebase #5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment