---
url: https://docs.tailor.tech/sdk/services/staticwebsite.md
---
# 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](/guides/static-website-hosting).

## 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](../multi-environment.md#settings-that-belong-to-a-single-environment).

## 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`](../configuration.md#environment-variables) 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: "_loggedIn" }, "=", true]], permit: true }],
    read: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
    update: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
    delete: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], permit: true }],
    sendPasswordResetEmail: [{ conditions: [[{ user: "_loggedIn" }, "=", true]], 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],
});
```
