Websites

Forte builds front-end apps from a GitHub repository and serves them over HTTPS on a global content delivery network. Use websites for marketing pages, dashboards, single-page apps, documentation sites, and other static or server-rendered front ends.

Websites are front-end only. When you need user-authentication enforcement, the server-side Forte API (forte.projects.*, which requires FORTE_API_TOKEN), or request observability, pair your website with a Service and call it from the browser — see Authentication and request handling below.

Supported frameworks and hosting

Forte auto-detects and builds most popular frameworks — Next.js, Vite, Astro, Create React App, Vue, Angular, SvelteKit, Nuxt, plain HTML, and more. Based on your framework, Forte hosts your site in one of two ways:

  • Static hosting — for sites that build to static files, Forte serves the output directly from a global CDN.
  • Server-rendered (SSR) hosting — for frameworks that render on each request, Forte runs your server across regions worldwide.

Either way, your site is served over a global CDN with HTTPS, and Forte applies CDN caching following standard cache-control conventions (see Caching and revalidation).

How websites work

When you create a website, Forte connects to your GitHub repository, builds it, and serves the output globally over a CDN. Each git push to the configured branch triggers a new build and deployment automatically.

Website URL

Every website is assigned a unique HTTPS URL on the sites.tryforte.dev domain:

javascript
https://{websiteId}.sites.tryforte.dev

This URL is permanent — Forte generates it when you create the website, and it never changes for the lifetime of the website, even across redeployments. You can copy it directly into DNS records, marketing materials, or any place that expects a stable URL.

Custom domains

Add a custom domain from the website's page in the Forte Console, then point your DNS at the domains.tryforte.dev endpoint shown there (a CNAME). Forte provisions and renews TLS certificates automatically. Your generated sites.tryforte.dev URL keeps working alongside your custom domain.

Build configuration

Forte detects how to build most frameworks (Next.js, Vite, Astro, Create React App, Vue, Angular, plain HTML, and more). When auto-detection isn't enough, you can override:

SettingDescription
Build commandThe command Forte runs to build your website (for example, npm run build)
Output directoryThe folder where built files land (for example, dist, out, build)
Install commandThe command to install dependencies (for example, npm install)
Package managerOne of npm, yarn, pnpm, or bun
Node.js versionThe major version of Node.js used to build (for example, 18, 20)
SubdirectoryThe path within your repository to build from — used for monorepos

If your repository changes — say, you add a new framework or move where package.json lives — you can reset the auto-detected configuration on the website's edit page so Forte detects fresh on the next build.

Build triggers

Choose how new deployments are triggered:

  • On every push to branch — Every commit to the configured branch redeploys automatically. Best for staging and continuous-deployment workflows.
  • On GitHub release published — Only redeploys when a new GitHub release is published. Best for production websites that should only roll forward on tagged releases.

You can also manually deploy any commit at any time from the website's deployment page.

Environment variables and secrets

Set environment variables and secrets on your website:

  • Environment variables are injected into the build environment and accessible through standard mechanisms like process.env.NAME in Node.js. They're visible in the console and in build logs. Use them for non-sensitive build-time configuration like feature flags and public API URLs.
  • Secrets are encrypted at rest and never displayed in the console once saved. Forte injects them where the website type actually has somewhere to read them: into the build environment (also process.env.NAME) for static websites, since a static site has no runtime to inject into afterward; into the runtime environment for server-rendered (SSR) websites, the same way as for Services. Secret values are never written to build logs or echoed anywhere Forte logs output.
Secrets aren't a guard against client exposure

Naming a secret with a framework's public prefix (NEXT_PUBLIC_, VITE_, etc.) still adds it to the client-visible bundle. Secrets hide a value from the Forte console and build logs, but your framework can still expose it. Don't use a public prefix for values that must stay server-side.

The FORTE_ and AWS_ prefixes are reserved — you cannot create custom environment variables that start with either. A handful of other names are also reserved for the automatic identifiers described below.

Automatic identifiers

Forte injects the website's project ID and website ID into every build and, for server-rendered sites, into the runtime. Each identifier is exposed under every common framework prefix so it reaches both your server code and your client bundle without extra configuration:

IdentifierCanonicalNext.jsViteCRAAngularSvelteKitNuxt
Project IDFORTE_PROJECT_IDNEXT_PUBLIC_FORTE_PROJECT_IDVITE_FORTE_PROJECT_IDREACT_APP_FORTE_PROJECT_IDNG_APP_FORTE_PROJECT_IDPUBLIC_FORTE_PROJECT_IDNUXT_PUBLIC_FORTE_PROJECT_ID
Website IDFORTE_WEB_APP_IDNEXT_PUBLIC_FORTE_WEB_APP_IDVITE_FORTE_WEB_APP_IDREACT_APP_FORTE_WEB_APP_IDNG_APP_FORTE_WEB_APP_IDPUBLIC_FORTE_WEB_APP_IDNUXT_PUBLIC_FORTE_WEB_APP_ID

All these names are reserved — attempting to set one of them as a custom env var or secret is rejected with a validation error so the platform value is always the one your code reads.

No API token in the bundle

FORTE_API_TOKEN is not injected into websites. A token in client code would let every visitor act as the project owner. To use the Forte API with project-level privileges, deploy that logic as a Service. The Service receives FORTE_API_TOKEN securely, and your website can call it. See API Surfaces for the full credential-safety model.

Authentication and request handling

Websites are public — every visitor can access them. Website requests are served directly from the CDN and do not pass through the Forte gateway. This holds for both static and server-rendered (SSR) websites: your SSR server code runs, but Forte does not sit in front of it.

Websites bypass the Forte gateway

The request-time features that Services get do not apply to websites — static or SSR:

  • No user-authentication enforcement. Forte does not authenticate requests to a website or inject the X-Forte-User-Id header. Auth path exclusions and end-user session enforcement are Service features — an SSR website must build and enforce its own auth.
  • No per-request observability. The request logs, metrics, and latency percentiles described in Monitoring are captured for Services only. Forte does not log or meter individual requests to a website.
  • forte proxy and local auth testing are Service features.

If your front-end needs authenticated, observable request handling, deploy that logic as a Service. A Service receives the X-Forte-User-Id header and Forte session context automatically on every authenticated request, and each request is logged and metered. Your website can call that Service from the browser.

Caching and revalidation

Forte serves your website over a global CDN. This section covers what gets cached, how to control it, and the rules around revalidating server-rendered content.

Static assets

The fingerprinted build output a framework emits — hashed JavaScript, CSS, and media files — is cached aggressively. These files are immutable: when their content changes, their filename changes, so a new deploy is picked up right away without serving anything stale. You don't need to configure this.

Server-rendered responses

For a server-rendered (SSR) website, caching is driven by the Cache-Control header your app sets on each response:

  • No Cache-Control (or no-store) — the response is generated fresh on every request. This is the right default for dynamic, per-user pages.
  • Cache-Control with a positive shared lifetime (for example s-maxage) — the response is cached at the CDN for that period and reused for every visitor, then refreshed.

Set these headers in your framework the same way you would behind any CDN.

Time-based revalidation

Time-based revalidation works. In Next.js, a route segment with:

ts
export const revalidate = 60

is served from cache for the configured number of seconds, then refreshed on the next request after it expires. This is honored at the CDN through the response's cache headers, so it behaves the same for every visitor. Use it for content that changes on a schedule — a blog index, a product listing, a homepage.

On-demand revalidation

On-demand revalidation is not applied reliably

On-demand revalidation — revalidateTag, revalidatePath, or the Pages Router res.revalidate — is not reliable on Forte.

Forte runs your app on more than one server. An on-demand revalidation call only refreshes the cache on the server that handles it; the other servers and the CDN are unaffected, so a page you expect to update may keep serving its previous version.

Use one of these instead:

  • Time-based revalidation (export const revalidate) for content that changes.
  • Redeploy to publish updated content immediately — a deploy refreshes everything.

If your build calls an on-demand revalidation API, Forte adds a note to your build log pointing back to this page.

Server-rendered request limits

Server-rendered websites handle ordinary request/response web traffic. A few limits apply when you build one:

  • No WebSockets or other long-lived connections. Use ordinary HTTP requests.
  • Requests must finish within about a minute. Move long-running work — large reports, media processing — to a background job instead of doing it inside a request.
  • Keep request bodies small. Large uploads (multi-megabyte files) should go straight to your storage provider, with your app handling only a reference to the file.

Reading the visitor's domain

Forte sits in front of your app, so the standard Host header your code sees is an internal value. To get the domain the visitor actually used — for canonical URLs, redirects, metadata, or sitemaps — read the x-forwarded-host header. Most frameworks already do this for you.

If you use an authentication library, turn on its trust-host option so it builds callback URLs from the forwarded host. In Auth.js / NextAuth, set:

ts
trustHost: true

Search

Search documentation and console pages