[Suggestion] Allow headers to be specified as part of `navigationFallback` configuration
- Dominant language
- No language data
- Stars
- 346
- Forks
- 67
- PR merge metrics
- No merged PRs in 30d
Description
**Background**
My app is an Angular SPA. As part of a production build, Angular's CLI gives me "versioned" JS/CSS files containing a hash (e.g. `main.[hash].js`) and these files are referenced inside `index.html`.
If the user's browser (or any intermediary CDN) uses a cached `index.html` that is out of date, it may refer to old JS files (e.g. `main.[oldhash].js`) which no longer exist, resulting in them not seeing the latest version of the app. As such, caching needs to be configured to ensure that a cached `index.html` can only be used if it is first validated as being up to date (e.g. by validating the etag)
**Is your feature request related to a problem? Please describe.**
There does not seem to be an easy way to configure what headers should be attached to responses that are served due to the `navigationFallback`. This makes it hard to get fine grained control of caching headers for an SPA which requires client-side routing.
For my test I set up a Static Web App in combination with Azure Front Door. To give an example, take the following `staticwebapp.config.json`:
```
{
"routes": [
{
"route": "/index.html",
"headers": {
"Cache-Control": "no-cache",
}
},
{
"route": "/",
"headers": {
"Cache-Control": "no-cache",
}
}
],
"navigationFallback": {
"rewrite": "index.html",
"exclude": ["/assets/*"]
},
"globalHeaders": {
"Cache-Control": "public, max-age=3600",
},
...
}
```
With this configuration:
a) requesting `mysite.com/index.html` serves index.html with `no-cache` response header (due to the first route-specific config)
b) requesting `mysite.com/` serves index.html with `no-cache` response header (due to the second route-specific config)
c) requesting `mysite.com/somethingelse` serves index.html with `public, max-age=3600` response header (navigationFallback applies, but it uses the headers from `globalHeaders` and not the `index.html` route config)
By the nature of it being a fallback, there is no obvious pattern that could be used in the `routes` section of the config to capture the "fallback" paths as this could be any string except one where a file exists on disk with that name.
**Describe the solution you'd like**
I'd like all three of the above situations to serve the `no-cache` header to ensure that the user always receives the latest index.html (because it would be forced to validate the etag). Any requests to specific files that _do_ exist (such as somescript.[hash].js) should use the `globalHeaders` (webpack bundled JS files with a hash in the name are suitable for more aggressive caching).
As far as I can gather, routes are _not_ applied on NavigationFallback by design, but this means it is hard to apply specific configuration to them in "scenario C" above.
My suggestion would be to add the ability to specify `headers` as part of the `navigationFallback` section.
```
"navigationFallback": {
"rewrite": "index.html",
"exclude": ["/assets/*"],
"headers": { <-- note: this is my suggestion, not something that is currently supported
"Cache-Control": "no-cache",
}
},
```
**Describe alternatives you've considered**
My current workaround is to negate the logic (conservative caching by default, more aggressive caching for specific routes with a wildcard pattern). This seems to work, but is fiddlier to set up & understand and has the potential to result in new files being cached too heavily if happen to match the pattern.
My workaround works as follows:
- sets `no-cache` as the global default header, such that all requests need to validate the etag before using a cached version
- for any files inside the `assets` directory (containing assets which may be subject to change over time), set `no-cache` header. Because `routes` are matched in order and it stops at the first match, they will not be affected by any subsequent route parameters in the array.
- for specific file patterns outside of `assets` such as JS/CSS files (which the Angular/Webpack build process should have added hashes for, making them suitable for more aggressive caching), set `public, max-age=3600` header so that they can potentially avoid network requests entirely
```
{
"routes": [
{
"route": "/assets/*",
"headers": {
"Cache-Control": "no-cache"
}
},
{
"route": "/*.{js,css,ttf,woff,eot,svg}",
"headers": {
"Cache-Control": "public, max-age=3600"
}
}
],
"navigationFallback": {
"rewrite": "index.html",
"exclude": ["/assets/*"]
},
"globalHeaders": {
"Cache-Control": "no-cache"
},
...
}
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.