By the way, Frontity adds a unique hash to all the static files. So itās safe to use an immutable maxage
for the cache-control whole /static
folder.
Hi @luisherranz, thanks for sharing this progress! Option 5 would definitely work though Iām not sure if Iād rank it above option 3 because Iām looking to get the benefit of Pantheonās CDN. Putting everything behind one domain as far as the CDN and the public is concerned is preferable to me compared to exposing the fact that the static directory is sourced from a some other place.
And the more I think about it, the more I think that I do just need PHP/Theme Bridge to be able to handle multiple backends, so option 3 is fine.
Iām curious in your experience how large the static directory can become for real sites. How large are the largest assets? How big can the overall directory get? Any large image would be an image would be a part of the design/theme, not part of the content of the site, yes?
Let me understand this better:
- Is the CDN caching requests of the main domain? (HTML requests)
- Or is the CDN only used for static assets in a different domain?
Apart from that, does your CDN honor the cache-control headers?
These are the cache-control
headers we recommend for Frontity: https://github.com/frontity/now-builder/blob/master/src/index.ts#L116-L128
[
// HTML.
{
src: `/.*`,
headers: { "cache-control": "s-maxage=1,stale-while-revalidate" },
dest: `/server.js`
},
// Static assets.
{
src: `/static/(.*)`,
headers: { "cache-control": "public,max-age=31536000,immutable" },
dest: `/static/$1`
}
]
We recommend stale-while-revalidate
for the HTML for small sites because that way it always serves the cached response, but itās not important for big sites because 99.9% of the visits will get a cached response.
s-maxage
is 1 because is a safe default, but of course this should be configured by the site owners, depending on their needs.
For the static
folder, we just set it as immutable because all the files include a unique hash.
I donāt quite understand this. Point 5 does precisely that: it puts everything under the same domain. Remember that when the PHP Theme Bridge is in charge, it does an internal HTTP request and returns the response, so everything that goes through it will use the WordPress domain.
Maybe we need some diagrams with all the scenarios to make sure weāre talking about the same cases
A Frontity server should always remain quite small.
The current server size is 1Mb, including React, Koa, the Frontity core and the rest of the libraries required. I guess the most complex app possible shouldnāt get bigger than 3Mb.
The client assets are a bit smaller, 600Kbs. If we embed them in the server, itād be 1.6Mb. I guess a very complex app with client assets embedded could be 5Mb, which is still a tolerable size for serverless.
Adding fonts or an image for the logo is fine. But to store images included in the WP content, they should use their WordPress site, not Frontity.
But they shouldnāt doesnāt mean they wonātā¦ Maybe, if people start doing it we will need to teach them that they should use WP for that.
Is option 3 using the publicPath
option or doing the routing with your own proxying logic?
I donāt quite understand this. Point 5 does precisely that: it puts everything under the same domain.
@luisherranz Ah, yes, I definitely misread what you had in mind for point 5. Sorry about that!
Maybe we need some diagrams with all the scenarios to make sure weāre talking about the same cases
Yes, this is what I had in mind for point 3, and I think it is also might be what you were imagining for point 5.
Pantheonās CDN does respect cache-control headers and it caches both static assets and HTML responses with headers designating them as cache-able.
Thanks for sharing the recommended cache-control
headers from the now-builder
example. That looks like Now-specific configuration. Is there a way to get the Frontity server to emit those headers?
A Frontity server should always remain quite small.
Cool, those numbers align with what I expected.
Hey, Iām glad we are on the same page. Thanks for the diagram, that was exactly what I meant
Frontity doesnāt add any headers right now. It could add default headers,
Server extensibility, which is currently on the roadmap, will allow any Frontity package to extend the underlying Koa* server.
For example, this will add the recommended cache-control
headers:
export default {
// Your normal package stuff: state, actions, React...
};
// Extend Koa server.
export const server = ({ app }) => {
app.use(ctx => {
if (ctx.path.startsWith("/static/")
ctx.set("cache-control", "max-age=31536000,immutable");
else
ctx.set("cache-control", "s-maxage=1,stale-while-revalidate");
});
};
We also have in mind to create an official package for this. Iām not sure if @frontity/cache-control
or a more general @frontity/headers
.
People will be able to configure this @frontity/cache-control
package in their Frontity settings.
- With the default settings:
const settings = {
packages: [
"@frontity/cache-control",
// ...
]
};
- With custom settings:
const settings = {
packages: [
{
name: "@frontity/cache-control",
state: {
headers: {
ssr: "s-maxage=300"
}
}
}
]
};
*We choose Koa over Express because itās half the size, easier to use and donāt include dynamic imports (bad for bundling it in a single server.js
file).
Awesome
@stevepersch I have a question for you.
What is the benefit of using a proxy over just replacing the PHP theme?
I ask because if you are going to cache the final HTML anyway, the performance gain is going to be minimal and with that type of proxy some of the things that usually work in WordPress are going to break: direct access to PHP files used by some plugins, plugins that add custom files/routes like sitemaps, robots or ads.txt, plugins for 301 redirections, plugins that modify headersā¦
My idea was to use template_include
to overwrite the current theme and wp_remote_get
for the request so Iād love to hear your opinion on this matter
Hi @luisherranz!
Are you asking about doing the proxying in a CDN instead of in PHP? Yeah, Iāve been working on the assumption that developers will be skeptical of proxying in PHP because they know itās faster and more scalable to do so in a CDN. But youāre right that cache-hits make the difference negligible in most situations.
For me, itās less about specific technical benefits as it is about the mental model. In the ideal diagram, would Frontity be inside of WordPress or next to WordPress?
I think Iāve been mentally drawing the ideal diagram with Frontity next to WordPress. But maybe in the ideal diagram, Frontity would be inside WordPress, replacing the theme. The fact that getting the HTML from Frontity requires calling out to a separate Node.js environment could be abstracted away similar to the way an object cache API abstracts away the detail of whether your cache objects are coming from PHP memory, a database, or Redis.
Putting Frontity inside WordPress would be less disruptive to existing WordPress sites. It could allow plugins to alter responses and do things youāre pointing out like sitemaps, etc (although even when Frontity is outside WordPress there would need to be configurable logic for which paths go to which runtime, PHP or Node)
Putting Frontity next to WordPress might be a clearer mental model for newcomers. I think it would allow front-end developers to have a better understanding of what is happening where compared to a model where PHP has the potential to alter the output.
Another very tangible version of this question is āone repo or two?ā As youāve seen, the sample projects Iāve done with Frontity combine the repos. Frontity becomes a directory inside the WordPress codebase and deployments to PHP and Node.js happen in the same deployment pipeline. But would teams prefer two repos with two deployment pipelines?
Either model can be optimized for performance. Which model is better for optimizing understanding?
Yes, those are precisely our thoughts on the matter and the reason I asked you
- If Frontity is next to WordPress then we are talking about Headless WordPress.
- But if Frontity is inside WordPress then we are talking about an alternative React rendering engine for WordPress.
Both approaches are very interesting but the second one is more natural to WordPress. So we think it will make more sense for hostings like Pantheon, which are already taking care of the CDN/caching of WordPress output gracefully. But please give it a thought and let me know.
We plan to support both architectures by the way.
I donāt think the mental model of the PHP Theme Bridge will be hard to understand. It may be even easier for those not familiar with the Headless CMS architecture.
I guess thatās up to each team to decide. But in my opinion, having both codebaes in the same repo makes sense because, as you said in an early message, sometimes a new feature will contain changes in both WP and Frontity that must be tested and deployed simultaneously.
Hey @stevepersch, I hope you are doing great in these uncertain times
I just wanted to share with you a proof of concept of the Theme Bridge:
The implementation is so simple that it doesnāt make much sense to do an explanation. Just take a look at the code
The only problem Iāve stumbled upon so far was that the normal Nginx configuration for WordPress doesnāt send static file requests (like js, fonts, imagesā¦) to WordPress, it just returns a 404 if it doesnāt find those assets in the file system.
To solve that, people would have to either:
- Change their Ngix configuration.
- Use a different
publicPath
setting to request the static assets directly from their Node server (or static storage). - Remove extensions from Webpack. For example,
file.js
becomesfile--js
.
But we donāt like this second idea because that means they have to configure an additional cache/CDN for that URL and we donāt like the third idea because, wellā¦ it feels really hacky and incompatible with any other system that expects files to have proper extensions.
Iād love to know you solved the Nginx config problem in Pantheon. I guess you already had that problem before with the HackyProxy, hadnāt you?
@SantosGuillamot is going to open a Feature Discussion here in our forum to start talking about the possible features and configurations.
Apart from that, I forked the simple-cache
plugin to add the Content-Type
header of static assets.
I must say that I am amazed by the performance. Itās faster than I thought. Once itās cached, my local server answers in about 5ms for both the HTML and static assets from Frontity. Itās not something you need for Pantheon, but Iād like to mention it as well
Hey @luisherranz,
Sorry for the delay here, Iām holding up!
On Pantheon, we hit that nginx limitation you describe on woff2 files but luckily not js, css, or images. I think thatās because Pantheon was first built for Drupal which sometimes creates those files on-demand.
To accommodate the āFrontity inside of WordPressā case, weāll need to alter nginx or sync those assets to an unversion-controlled directory on the PHP container.
Would you be open to a pair programming session later this week where we could try to set up theme-bridge-poc
on the Pantheon+GCP Cloud Function architecture?
Absolutely
Iāll send you an email.
To summarize our meeting: The Theme Bridge PoC plugin is working great in Pantheon
The instant cache invalidation of Pantheon is working great with this approach, both for the HTML and the REST API requests.
Next steps:
- Frontity
- Add build-time configuration of
publicPath
.
- Add build-time configuration of
- Pantheon:
- Find a way to upload the
/static
folder to the WordPress file system after runningnpx frontity build
in the CI.
- Find a way to upload the
Once we have those things, we can test a real site in their infrastructure.
After that, we can work on other not so critical issues:
- Frontity
- Release the final Theme Bridge plugin with support for using ENV variables to change the settings.
- Add dynamic configuration of
publicPath
.
- Pantheon
- Create an upstream of WordPress+Frontity.
I have added a Feature Discussion for the publicPath
and I gues we will include it in the next sprint: Change publicPath.
Can you @SantosGuillamot confirm that?
We havenāt finished planning what weāre going to do the next sprint but this is one of our top priorities so itāll be probably included yes.
Finally got a chance to play with this POC and Iām frightened by how well it worked. Likeā¦ it just works - I was not expecting that. This is on 2 separate Digital Ocean instances (one nginx and one node jobby).
I just popped it on my WP instance and it pulled the JS from the separate external node machine. Awesome.
(medusa.403page.com is the hostname for my backend for 403page.com - for this test you can see itās serving both)
Testing on WP Engine next.
Confirmed working nicely on WP Engine (nginx).
Itās a valid point about the /static/ folder. Got 404ās out of the box as expected. But I added this nginx rule easily enough to make it work:
# Serve /static from node server through main domain
location ~* ^/static/?(.*) {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Cache-Control $http_cache_control;
proxy_pass [http://MY_NODE.JS_SERVER_IP:3000];
}
Itād be very cool if that could be handled inside PHP - but Iām not sure how/if that would work yet. Gonna play around a bit and seeā¦
P.S. @stevepersch is that nginx rule easy enough to implement on Pantheon on a per user basis? Might be a good first step for now at least.
@403page if Iām not mistaken, the approach in Pantheon is going to be changing Webpackās publicPath
to use a different /static
folder.
Imagine you have a folder structure like this:
/var/www/html
/wp-includes
/wp-content
/frontity
/build
/static
...
Then you would change the publicPath
to "/frontity/build/static"
and the URL of your scripts/fonts will become:
https://yourdomain.com/frontity/build/static/my-assets.some-hash.js
If the Frontity folder is instead inside wp-content
, like this:
/var/www/html
/wp-includes
/wp-content
/frontity
/build
/static
...
Then, then publicPath
would be "/wp-content/frontity/build/static"
and the URL:
https://yourdomain.com/wp-content/frontity/build/static/my-assets.some-hash.js
That way, Nginx can find the files in the file-system and everything works fine.
Our current idea is to add the ability to change the publicPath
in build-time:
> npx frontity build --publicPath="/other/folder"
or in run-time, to be able to store the setting in WordPress:
https://mydomain.com/some-post/?frontity_publicPath=/other/folder
More info on the Public Path feature discussion. Feel free to give us your feedback
Thanks for clarifying that @luisherranz.
Gotcha. I was struggling at first to see the workflow where youād deploy /static/ to the Wordpress host and the rest of the site on the Node instance - but that helps a good deal. It would be easy enough to automate that anyway.
I think I was coming from a place of oversimplifying. Install the plugin and add the nginx rule one time on the WP instance and then deploy/update Frontity the external node.js server as normal.
The Public Path feature is definitely very interesting. Heading over to that thread nowā¦
Well, changing the Nginx configuration is going to be always possible, and that problem doesnāt seem to be present on Apache. But we want to provide a method for those who donāt want (or donāt know) to change their Nginx configuration. So far, changing the publicPath
seems to be the best option, although we are open to other ideas
Yeah, to echo what @luisherranz said, Iād like to try a configurable publicPath
before making nginx config changes. Pantheon standardize nginx config across all sites. In my R&D work right now Iām sometimes going outside what our normal platform does, but ultimately I want to find a way to support Frontity that can minimize changes to our infrastructure and workflow.