If you host an Angular Progressive Web App (PWA) on a web server, you will frequently have URLs that are handled by the Angular router to navigate to specific pages within the app.
However, without the service worker registered, navigation requests are not intercepted and sent to the PWA. In other words, if a user has never ran the PWA yet in his or her browser, and he or she tries to navigate to a specific page in the PWA (e.g. https://mywebapp.abc/foo/bar), your web server will likely return a 404 error. We would like to serve the PWA first, then relay the /foo/bar
path to the Angular router.
Nginx config solution
If you have access and can configure the static serving of files on your web server, you will want to use the try_files
directive:
location / { try_files $uri $uri/ /index.html; }
Using this directive, Nginx will attempt to serve the static file based on the URI path (e.g. /images/foo.png will load correctly), but in all other instances where it cannot find a static file to serve if it is instead a PWA Angular route (e.g. /foo/bar), it will serve index.html of your PWA. This will eliminate all 404 errors and will serve up the PWA entry page, retaining the “404” URL in the browser (/foo/bar) so the Angular router can handle it.
The downside to this is that all legitimate 404 errors now always serve the PWA’s index.html, which may not be desirable. Say your server was actually missing /images/foo.png, then it will instead serve index.html in its place rather than returning a 404.
404 page solution
Another possible solution is to create a custom 404 page containing the following Javascript that accomplishes three things:
- Checks if there is a
ngsw-worker.js
to register. - Loads the PWA in an iframe, so that the service worker is installed.
- Once the iframe is done loading, refresh the page with the intended URL. The PWA router will now pick up the navigation request to
/foo/bar
and transition to the correct page after load.
var request = new XMLHttpRequest(); request.open('GET', '/ngsw-worker.js', true); request.onreadystatechange = function(){ if (request.readyState === 4){ if (request.status === 200) { var iframe = document.createElement('iframe'); document.body.append(iframe); iframe.style.setProperty('position', 'fixed'); iframe.style.setProperty('left', '0'); iframe.style.setProperty('top', '0'); iframe.style.setProperty('width', '100%'); iframe.style.setProperty('height', '100%'); iframe.style.setProperty('border', '0'); iframe.style.setProperty('background-color', '#f7f7f7'); iframe.src = '/'; iframe.onload = function() { location.reload(); }; } } }; request.send();
As of the time of this writing, this alternate solution does not work with FireFox but works well in Chrome.
“404 page solution”
Thank for this interesting Post. Where should I put this code”404 page solution”?
If you are able to customize the template for your 404 page, you’ll want to put it inside