Azure / static-web-apps

Azure Static Web Apps. For bugs and feature requests, please create an issue in this repo. For community discussions, latest updates, kindly refer to the Discussions Tab. To know what's new in Static Web Apps, visit https://aka.ms/swa/ThisMonth
https://aka.ms/swa
MIT License
330 stars 56 forks source link

[Suggestion] Allow headers to be specified as part of `navigationFallback` configuration #698

Open mattwilson1024 opened 2 years ago

mattwilson1024 commented 2 years ago

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:

{
  "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"
  },
  ...
}
dczychon commented 2 years ago

I´m currently evaluating azure static web app to switch over from static website hosting in azure storage and also encountered this problem.

I have a VueJS app and can´t easily set the cache-control header to no-cache when the index.html file is served as fallback.

It would be nice if all configurations specified in the routes array for the fallback file could be applied, when the fallback route is served. So if I use /index.html as fallback, then all configuration for the /index.html route (specified in the routes array) should be applied.

therealmarkber commented 2 years ago

@joslinmicrosoft any update?

DeckardCain001 commented 1 year ago

any update yet?

rosemcc commented 1 year ago

Also interested to know if there's any update..

alexandrudanpop commented 9 months ago

Any update on this?