I’m actually in the process of writing a post which explains a model similar to this right this very moment! (With some differences in the nuance I suspect). I presented a little demo during a presentation at NEJS. (You might find the slides helpful)

The model I was exploring was in allowing user generated content to be submitted via a form which would create content on a new URL and initiate a regeneration of the site to include that new page. While that page was being generated. I use Netlify’s custom 404 handling in the redirects to pass requests to that new URL to a serverless function which gets the content directly from the content API and does a serverless render.

Once the site has been regenerated and the new URL exists, the request to it no longer 404 and are simply satisfied by the pre-generated static page.

While I finish up my detailed post describing this all a bit more clearly, you might like to play with the demo as it is now, and you can also node around in the code.

you cannot set custom headers via netlify.toml or_headers for functions; your function needs to return the headers itself, instead, as you’ve discovered. That is operating as expected.

I think you need to rely on max-age/public rather than stale-while-revalidate to achieve your goals; I don’t know what we intend to do with stale-while-revalidate for functions or if our proxy (which connects the function to a web request) handles it correctly, but since your experiment indicates no, I wouldn’t count on it (or on that changing anytime soon).

I’ll review with our operations team next week whether they feel like it is a bug or just intended not to work and follow up here if we come up with anything interesting.

I don’t know what we intend to do with stale-while-revalidate for functions

The guys from Zeit call it serverless pre-rendering although we’ve already been doing this for years for our clients with KeyCDN. Most CDN’s support the state-while-revalidate directive as far as I know.

The mechanism is simple:

The first time a URL is visited, the CDN requests the static HTML to the serverless function, serves the file and saves it in disk/memory for later use. This is the normal behaviour of any CDN, of course.

The second time a URL is visited, CDN serves the static file. Still normal.

The change is here: once the file is stale (controlled by the s-maxage directive), if the state-while-revalidate directive is used, the CDN returns the stale static HTML file to the user. After that, it requests a fresh one in the background to the serverless function. It then overwrites the stale one in its disk/memory.

The next user who visits the site gets the fresh static file from the CDN.

The time-to-stale is controlled by s-maxage directive and it can be 1 second (always check for a fresh file in the background) or any other time, for example 15 minutes. That obviously depends on the site needs. It can be further optimized setting s-maxage to the max and using manual cache invalidation, usually with a soft-purge hook of the CDN API that marks all the files as stale.

As you can see, the approach is similar to the static-generated site, but it has some benefits for medium/large publisher sites with thousands/tens of thousands posts: the static HTML files are generated on demand and over time. The static HTML files most used are generated first and served instantly, while the rest of the static HTML files are generated over time, if ever. And the final user always gets static files from the CDN (but the very first time).