Host anywhere
Putting a site on any host, such as AWS, Google Cloud, DigitalOcean or a server of your own, with a build you set up yourself.
A Goodfellow site is static files: pages, pictures, the admin panel and everything else are written by the build, and nothing runs on a server. So any host that serves files can serve one, such as Amazon S3 with CloudFront, Google Cloud Storage or Firebase Hosting, DigitalOcean's App Platform or Spaces, Azure Static Web Apps, Render, or a server of your own with nginx or Apache. Put it online gives the setup files for GitHub Pages, GitLab Pages, Cloudflare Pages, Netlify and Vercel; on any other host, you set up the build yourself, as this page describes. A full-stack Next.js site runs on any host that runs Node.js.
Building the site
Make the site as usual, choosing Not decided yet for the host, then delete the setup files of the hosts you don't use. The site is still stored on GitHub, GitLab or Bitbucket, where the admin panel publishes to, so your build starts from the repository's main branch:
npm ci, to install exactly whatpackage-lock.jsonnames.npm run build, which writes the site intodist/(out/for the Next.js starter, whose build script runsnext buildand thengoodfellow-next finish out).- Copy that folder to the host, such as with
aws s3 sync dist/ s3://your-bucket --delete.
If the host serves the site from a subfolder, such as https://example.org/parish/, build it with GOODFELLOW_BASE=/parish/ (see Commands). Put the site's address in Site Settings → General → Site address, since search engines, feeds and link previews need full addresses.
Build again whenever the main branch changes, since every Publish in the admin panel is a change there. Most hosts and CI services can start a build on every push; the starters' GitHub workflow, .github/workflows/deploy.yml, is a good place to start from: keep its jobs and replace the one that publishes to GitHub Pages with your host's upload. That keeps what the rest of it does, below.
What the host needs to do
- Serve each page's folder: the build writes each page as a folder with an
index.html, such asabout/index.htmlfor/about. Most hosts serve those by themselves; on S3 that means the bucket's website endpoint, or CloudFront with a function that addsindex.html, and on nginxtry_files $uri $uri/ =404;. - Show "Page not found": point the host's error page at
404.html(S3's error document, nginx'serror_page 404 /404.html;). - Send the right types: files are named by what they are, so hosts that go by the name send the right type. Check that
.icsis sent astext/calendar,.rssand.atomas XML, and.webpand.woff2as what they are, if the host doesn't know them. - End addresses as the host does: see Addresses with or without a slash.
- Send the site's headers, where the host lets you: the build writes them into
_headersin the folder it builds, in the form Cloudflare Pages and Netlify read, so those two send them as they are. Elsewhere, set the same rules in the host's own settings, such as a CloudFront response headers policy or nginx'sadd_header. They're safer settings for every page, the media library's SVGs sandboxed, and the admin panel's own (see Security). - Send old addresses on: the build writes a page at each of the site's redirects' old addresses that sends visitors on, which works on every host. It also lists them in
_redirects, which Cloudflare Pages and Netlify send themselves.
Addresses with or without a slash
Hosts differ in which address they serve a folder's page at: GitHub Pages, Cloudflare Pages and Netlify serve /about/ and send /about there, while others, such as Vercel with trailingSlash: false, do the opposite, and some serve both. The site tells search engines each page's address, in its canonical link, sitemap, hreflang links, feeds, calendar files and structured data, so it should be the one the host serves without sending visitors on. Links from one page to another, in menus, buttons and text, end the same way, so following them doesn't go through a redirect either.
By default those addresses end with a slash (https://example.org/about/), as most hosts serve the folders the build writes. Where yours serves them without one, set it in goodfellow.config.tsx:
export default defineConfig({
trailingSlash: false,
// …
});GOODFELLOW_TRAILING_SLASH=false where the site is built does the same, and wins over the config. On the Next.js starter, set Next.js's own trailingSlash in next.config.ts instead, which also decides how Next.js writes its pages: as folders (true, the default), or as about.html (false).
Keeping it up to date
The starters' setup files do three more things, which your build can do with the same commands:
- Rebuild overnight, so pages that show what's coming up stay current: run
npx goodfellow nightlyeach night, and build when it says so (see Rebuilding overnight), or simply build every night. - Publish scheduled pages on time: run
npx goodfellow dueevery hour, and build when it says something came due (see Scheduling). Without it, scheduled changes go live with the next build. - Install fixes:
npx goodfellow update --publishinstalls Goodfellow's fixes, if the site still builds with them, and publishes them (see Keeping a site up to date). The admin panel's Updates screen follows the starters' GitHub workflow and GitLab pipeline, so on a setup of your own it only explains.
Next.js sites use goodfellow-next in place of goodfellow.
The admin panel
The admin panel is part of the build, at /admin, so it works wherever the site is. Editors sign in with a token, or with a sign-in service or token broker of your own. Where your build runs on GitHub Actions or GitLab CI/CD, the admin panel shows whether the site is rebuilding after a publish, and what went wrong if it failed.
To give the admin panel an address of its own on a host of your choosing, run npx goodfellow admin add admin.example.org, connect that address to the same files at the host, and have the host send Content-Security-Policy: sandbox with every page there but the admin panel, as _headers lists. Once the site has rebuilt, npx goodfellow admin check checks it and starts the admin panel only there.
To connect a domain, use the host's own settings, then put the new address in Site Settings → General → Site address and publish.