Skip to content
View as Markdown

Static Website ​

Static Website is a service for hosting static web applications on the Tailor Platform.

Overview ​

Static Website provides:

  • Static file hosting
  • Type-safe URL references for configuration
  • IP address restrictions
  • Custom domain support

For the official Tailor Platform documentation, see Static Website Guide.

Configuration ​

Configure static website hosting using defineStaticWebSite():

Definition Rules:

  • Multiple websites allowed: You can define multiple static websites in your config file
  • Configuration location: Define in tailor.config.ts and add to the staticWebsites array
  • Uniqueness: Website names must be unique across all static websites
typescript
import { defineStaticWebSite, defineConfig } from "@tailor-platform/sdk";

const website = defineStaticWebSite("my-website", {
  description: "My Static Website",
});

// You can define multiple static websites
const adminWebsite = defineStaticWebSite("admin-website", {
  description: "Admin Dashboard",
});

export default defineConfig({
  staticWebsites: [website, adminWebsite], // Add all websites to the array
});

Options ​

description ​

A description of the static website:

typescript
defineStaticWebSite("my-website", {
  description: "Frontend application for my service",
});

allowedIpAddresses ​

Restrict access to specific IP addresses in CIDR format:

typescript
defineStaticWebSite("my-website", {
  allowedIpAddresses: ["192.168.0.0/24", "10.0.0.0/8"],
});

customDomains ​

Associate custom domains with the static website:

typescript
defineStaticWebSite("my-website", {
  customDomains: ["app.example.com"],
});

After deploying, use tailor staticwebsite domain get <domain> to check domain status and retrieve the CNAME targets required for DNS configuration.

A domain can be associated with only one workspace at a time. To set custom domains only in the workspace that owns the domain, see Multi-Environment Configuration. To hand a domain to another site or workspace without taking it offline, see Moving a domain.

Moving a domain ​

A domain already serving traffic can be moved to another static website — in this workspace or another one — without downtime, without reissuing its certificate and without changing DNS. The site that currently holds the domain offers it; the receiving site accepts by listing the domain normally.

On the side giving the domain up, replace the plain entry with the destination:

typescript
defineStaticWebSite("my-website", {
  customDomains: [{ domain: "app.example.com", transferTo: { workspaceId: "..." } }],
});

Deploying opens the offer. On the receiving side, list the domain as usual and deploy:

typescript
defineStaticWebSite("new-website", {
  customDomains: ["app.example.com"],
});

That deploy completes the move. The next plan on the giving side reports that the domain moved, and the entry should then be deleted — it describes a move that already happened, not ongoing state.

The two deploys must be separate runs, even when both sites live in the same workspace: the receiving side cannot accept an offer that the same run is still opening, and tailor deploy says so rather than failing partway.

These rules apply while an offer is open:

  • An offer expires after 7 days. Deploying the entry again after that opens a fresh offer.
  • Neither the domain nor the static website holding it can be removed. Change the entry back to a plain string and deploy to withdraw the offer first; tailor deploy and tailor remove both refuse before changing anything.
  • An offer takes a moment to become ready. Until it is, the receiving side cannot accept it and its deploy is refused; the giving side's plan says so.

Type-safe URL References ​

The returned website object provides a url property that resolves to the actual URL at deployment time. Use this for type-safe configuration:

CORS Settings ​

typescript
const website = defineStaticWebSite("my-frontend", {
  description: "Frontend application",
});

export default defineConfig({
  cors: [website.url], // Resolved at deployment
  staticWebsites: [website],
});

OAuth2 Redirect URIs ​

typescript
const website = defineStaticWebSite("my-frontend", {
  description: "Frontend application",
});

const auth = defineAuth("my-auth", {
  oauth2Clients: {
    "my-client": {
      redirectURIs: [
        `${website.url}/callback`, // https://my-frontend.example.com/callback
        `${website.url}/auth/complete`, // https://my-frontend.example.com/auth/complete
      ],
      grantTypes: ["authorization_code", "refresh_token"],
    },
  },
});

Application Environment Variables ​

typescript
const website = defineStaticWebSite("my-frontend", {
  description: "Frontend application",
});

export default defineConfig({
  env: {
    siteUrl: website.url, // https://my-frontend.example.com
  },
  staticWebsites: [website],
});

Resolver, executor, workflow job, and auth before-login hook code, and TailorDB migration scripts, that read env receive the deployed URL, even when the same deploy both creates the website and reads its URL — one deploy call resolves it, with no second, manually-triggered deploy needed. If the referenced website does not exist at all, the CLI warns and leaves the unresolved reference in place. If the reference still can't be resolved after this deploy's rebuild, the deploy fails instead of shipping the unresolved reference. This platform lookup only happens during deploy; function run passes the literal <name>:url string unchanged, since it never talks to the platform to resolve it.

The deployed URL is also shown by tailor show, which lists the URL of each static website defined in staticWebsites once it has been deployed.

Complete Example ​

typescript
import { defineConfig, defineAuth, defineIdp, defineStaticWebSite } from "@tailor-platform/sdk";
import { user } from "./tailordb/user";

const website = defineStaticWebSite("my-frontend", {
  description: "Frontend application",
});

const idp = defineIdp("my-idp", {
  clients: ["default-client"],
  permission: {
    create: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
    read: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
    update: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
    delete: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
    sendPasswordResetEmail: [{ conditions: [[{ user: "role" }, "=", "ADMIN"]], permit: true }],
  },
});

const auth = defineAuth("my-auth", {
  userProfile: {
    type: user,
    usernameField: "email",
    attributes: { role: true },
  },
  oauth2Clients: {
    "frontend-client": {
      redirectURIs: [`${website.url}/callback`],
      grantTypes: ["authorization_code", "refresh_token"],
    },
  },
  idProvider: idp.provider("default", "default-client"),
});

export default defineConfig({
  name: "my-app",
  cors: [website.url],
  idp: [idp],
  auth,
  staticWebsites: [website],
});