Deploy to Cloudflare
This how-to puts an mfeflow site on Cloudflare Workers static assets. It is one option among several in the Deploy overview. The second half covers R2, for teams that want remotes and the index outside the shell.
You can write the files below by hand, or run mfeflow add deploy-cloudflare for files you can edit. The feature does not copy this page. This page serves one site folder from one Worker, and the feature gives the shell and each remote its own Worker, with CORS between them. See deploy-cloudflare for what it adds. The steps here stay valid either way.
You need a Cloudflare account, a domain on Cloudflare, and the site folder from the overview. Cloudflare's own docs cover each step in more depth: static assets and R2.
Deploy the site folder
Install wrangler in the project:
Build the site folder for each environment. The origin must be the domain you will serve it from:
Describe the Worker in one file. The assets.directory setting names the folder to upload. The single-page-application value sends unknown paths to index.html:
Deploy production, then staging:
You should see wrangler upload the files and print the address the Worker runs on. Open the domain and click a remote route.
Each environment is a separate Worker. Cloudflare names the staging one shop-staging. Bindings and vars are not inherited by an environment, so repeat them in each block that needs them.
Set the cache headers
Workers send Cache-Control: public, max-age=0, must-revalidate for static assets. That is already right for index.html, the runtime config and the index. Only the files that never change need a longer time.
Put a _headers file in the shell's public folder. The build copies it into dist, and the assemble script copies it on:
Cloudflare allows 100 rules in this file. It does not apply to responses your own Worker code creates.
Beware the SPA fallback
With single-page-application, any path that is not a file returns index.html with status 200. That includes a mistyped /mfe/... URL.
If a remote fails to load, run curl -I on its manifest URL. Check that you get JSON, not HTML.
Keep remotes and the index in R2
R2 is Cloudflare's object storage. Use it when a remote's team should publish without redeploying the shell. The shell reads the index and the remotes from a separate origin.
Create the bucket:
Connect a custom domain to it in the dashboard. Open the bucket, choose Settings, then Custom Domains. Do not serve production from the r2.dev address. Cloudflare rate-limits it and says it is for development only.
Allow the shell's origin to fetch from the bucket. The rules file looks like this:
Apply it:
Tell the shell where to look. Put this in runtime-config.prod.json, then assemble and deploy the site again:
Push remotes with the mfeflow commands
This is one option. mfeflow deploy uploads a remote's files and writes the index for you, and promote and rollback move it. R2 speaks the S3 protocol, so you set an endpoint:
The key and secret come from an R2 API token. Follow Deploy and roll back a remote for the output and the flags. The files land under <name>/<version>/ and the index under envs/<env>/index.json.
Or upload the files yourself
Nothing requires the commands. The AWS CLI can copy to R2 with its --endpoint-url flag. R2 lists the calls a sync needs (list, head, put) as supported, but its docs do not show sync, so test on a staging bucket first:
For one file at a time, wrangler works. The --remote flag makes it talk to the real bucket:
Upload the index last, after every file it points to.
Optional: serve the index from the shell's domain
You may want the shell's own address to answer /registry/index.json and /config/runtime-config.json, with the data in R2. A small Worker can do that. The run_worker_first setting makes it run for those two paths only.
The Worker maps each path to an R2 key. It must set Cache-Control itself, because _headers does not apply to Worker responses:
Upload config/runtime-config.prod.json to the bucket yourself. This Worker is a sketch checked against Cloudflare's docs, not a tested package.
Pages is for existing projects
Cloudflare's own docs say "Start new projects with Workers." This page therefore uses Workers. If you already host on Pages, you can keep it. Check Cloudflare's Pages docs for its header and fallback rules, which this page does not cover.
Next
- Run it in CI: One build, three platforms.
- Compare hosts: Deploy overview.