diff --git a/dictionary.txt b/dictionary.txt
index 6f1218f5a..2c220cea1 100644
--- a/dictionary.txt
+++ b/dictionary.txt
@@ -252,6 +252,8 @@ Trivy's
KMS
deployable
VMs
+CDN
+subdirectories
[0-9]+px
^.+[-:_]\w+$
[a-z]+([A-Z0-9]|[A-Z0-9]\w+)
diff --git a/docs/architecture/websites.mdx b/docs/architecture/websites.mdx
new file mode 100644
index 000000000..7b3016d43
--- /dev/null
+++ b/docs/architecture/websites.mdx
@@ -0,0 +1,142 @@
+---
+description: 'Learn how Nitric provisions and manages websites with Terraform and Pulumi across AWS, GCP, and Azure.'
+---
+
+# Websites
+
+## 1. System Context
+
+**Developers** use Nitric to define required websites within their application.
+
+- App code uses the [Website resource](/websites) defined in `nitric.yaml` to specify the websites and their configurations.
+- Developers can configure base path, index pages, error pages, and other settings in their `nitric.yaml` file.
+- When a website is defined, a single CDN endpoint is automatically created to serve the website content globally, including APIs as proxies.
+
+**Operations** use default or overridden IaC (e.g., Terraform modules) to provision the necessary resources for their target cloud.
+
+
+ Example AWS Provider
+
+- **AWS S3** serves as the storage backend for static files (e.g., HTML, CSS, JS, images).
+- **AWS CloudFront** is used to distribute the website globally and serve as a single entry point for the whole application.
+- **AWS IAM** providers roles for access to the Website bucket
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ NitricUp["nitric up"]
+ S3["AWS S3 Bucket"]
+ CloudFront["AWS CloudFront"]
+ Rewrite["AWS API Gateway"]
+
+ Developer -->|Deploy| NitricUp
+ NitricUp -->|Upload Assets| S3
+ NitricUp -->|Create CDN| CloudFront
+ CloudFront -->|Serve Static Files| S3
+ CloudFront -->|Rewrite /api/* to API| Rewrite
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+
+
+
+ Example Azure Provider
+- **Static Website in Azure Blob Storage** serves as the storage backend for static files (e.g., HTML, CSS, JS, images).
+- **Azure Front Door** is used to distribute the website globally and serve as a single entry point for the whole application.
+
+```mermaid
+flowchart TD
+ Developer["Developer"]
+ NitricUp["nitric up"]
+ Storage["Azure Storage"]
+ FrontDoor["Azure Front Door"]
+ Rewrite["Azure API Management"]
+
+ Developer -->|Deploy| NitricUp
+ NitricUp -->|Upload Assets| Storage
+ NitricUp -->|Create CDN| FrontDoor
+ FrontDoor -->|Serve Static Files| Storage
+ FrontDoor -->|Rewrite /api/* to API| Rewrite
+
+classDef default line-height:1;
+classDef edgeLabel line-height:2;
+```
+
+
+
+## 2. Sequence
+
+### Build Sequence
+
+Below is the sequence of events that occur when a developer registers a website with Nitric:
+
+```mermaid
+sequenceDiagram
+ participant Config as nitric.yaml
+ participant CLI as Nitric CLI
+ participant Provider as Nitric Provider
(plugin)
+ participant IAC as IaC (e.g. Terraform)
+
+ CLI->>Config: Parse website configuration
+ CLI->>Provider: Forward Nitric Spec
+ Provider->>IAC: Provision Website
+ Provider->>IAC: Provision IAM
+```
+
+## 3. Component
+
+### Website Module
+
+- Deploys website assets to a cloud-based storage solution for flexible and scalable hosting.
+- Configures a distribution layer to serve the site globally, rewriting API endpoints to `/api/{apiName}` for consistent routing.
+- Automatically invalidates the cache based on file changes, ensuring users always receive the latest content.
+
+## 4. Code
+
+**Developers** write yaml configuration to define the website and implement logic to serve static files.
+
+### Nitric website configuration - nitric.yaml
+
+```yaml
+name: service-name
+services:
+ - match: ./services/*.js
+ start: npm run dev:services $SERVICE_PATH
+ runtime: node
+runtimes:
+ node:
+ dockerfile: ./node.dockerfile
+ args: {}
+# The website configuration
+websites:
+ - basedir: ./my-website
+ index: index.html
+ error: 404.html
+ build:
+ command: npm run build
+ output: ./dist
+ dev:
+ command: npm run dev -- --port 4322
+ url: http://localhost:4322
+```
+
+### Access an API from the website frontend
+
+```javascript
+async function fetchData() {
+ // due to apis being served from the same domain thanks to rewrites, no CORS is required
+ const response = await fetch('/api/main/hello')
+ const data = await response.json()
+ console.log(data)
+}
+```
+
+**Operations** will use or extend the Nitric infrastructure modules, including both Terraform and Pulumi:
+
+- Terraform Modules:
+ - Not yet available
+- Pulumi Modules:
+ - [AWS Website Pulumi Module](https://github.com/nitrictech/nitric/blob/main/cloud/aws/deploy/website.go)
+ - [Azure Website Pulumi Module](https://github.com/nitrictech/nitric/blob/main/cloud/azure/deploy/website.go)
diff --git a/docs/get-started/foundations/projects/configuration.mdx b/docs/get-started/foundations/projects/configuration.mdx
index 9f45399ec..ac72f921d 100644
--- a/docs/get-started/foundations/projects/configuration.mdx
+++ b/docs/get-started/foundations/projects/configuration.mdx
@@ -234,3 +234,61 @@ runtimes:
args:
GO_VERSION: 1.16
```
+
+### `websites` (optional)
+
+A list of [website](/websites) types that make up your project. Websites are used to serve static files and assets.
+
+```yaml
+websites:
+ - basedir: ./my-website
+ path: /
+ index: index.html
+ error: 404.html
+ build:
+ command: npm run build
+ output: ./dist
+ dev:
+ command: npm run dev
+ url: http://localhost:4322
+```
+
+#### `basedir`
+
+The base directory to search for website assets. If not provided, the base directory is the root of the project. This is useful when you want to group websites under a specific directory or you're working with a monorepo.
+
+#### `path`
+
+The path to serve the website at. If not provided, the default is `/`. This is useful when you want to serve multiple websites under different paths.
+
+#### `index`
+
+The name of the index file to serve. If not provided, the default is `index.html`.
+
+#### `error`
+
+The name of the error file to serve. If not provided, the default is `404.html`.
+
+#### `build`
+
+A map of build configuration options for the website. This is used to build the website assets before deployment.
+
+##### `command`
+
+The command to build the website assets. This command will be executed in the base directory of the website.
+
+##### `output`
+
+The output directory for the built website assets. This is relative to the base directory of the website.
+
+#### `dev`
+
+A map of development configuration options for the website. This is used to run the website in development mode.
+
+##### `command`
+
+The command to run the website in development mode. This command will be executed in the base directory of the website.
+
+##### `url`
+
+The URL to access the website in development mode. This is used to configure the Nitric CLI to proxy requests to the website during local development.
diff --git a/docs/get-started/foundations/projects/local-development.mdx b/docs/get-started/foundations/projects/local-development.mdx
index 81d745b41..310553b2d 100644
--- a/docs/get-started/foundations/projects/local-development.mdx
+++ b/docs/get-started/foundations/projects/local-development.mdx
@@ -61,6 +61,7 @@ The Nitric CLI also hosts a Local Development Dashboard, which provides a graphi
- **Topics**: View topics and subscriptions, and publish messages to topics.
- **Secrets**: View and manage your application secrets.
- **WebSockets**: Dive deep into connection details, connecting, and sending messages.
+- **Websites**: View your websites.
Many of these features make it possible to build and testing modules of your application in isolation. For example, you can could create and test a topic and subscriber without needing to first write the publisher.
diff --git a/docs/providers/mappings/aws/websites.mdx b/docs/providers/mappings/aws/websites.mdx
new file mode 100644
index 000000000..90ac7e7f4
--- /dev/null
+++ b/docs/providers/mappings/aws/websites.mdx
@@ -0,0 +1,24 @@
+---
+description: 'How Nitric deploys Websites to AWS'
+---
+
+# AWS Resources - Websites
+
+Nitric Websites are deployed to AWS using [Amazon S3](https://aws.amazon.com/s3/) and [Amazon CloudFront](https://aws.amazon.com/cloudfront/).
+
+## AWS Resources
+
+The following resources are created when deploying Websites to AWS:
+
+- S3 Bucket
+- IAM Policies and Permissions
+- CloudFront Distribution
+- CloudFront Functions
+
+## Deployment
+
+During deployment the Nitric CLI creates a bucket for your website assets and a CloudFront distribution to serve them:
+
+- The declared website is created in S3
+- A CloudFront distribution is created to serve your website and rewrite all APIs to `/api/{apiName}` using CloudFront Functions
+- Cache invalidation is automatic based on changed files
diff --git a/docs/providers/mappings/azure/websites.mdx b/docs/providers/mappings/azure/websites.mdx
new file mode 100644
index 000000000..4e4ecff8f
--- /dev/null
+++ b/docs/providers/mappings/azure/websites.mdx
@@ -0,0 +1,23 @@
+---
+description: 'How Nitric deploys Websites to Azure'
+---
+
+# Azure Resources - Websites
+
+Nitric Websites are deployed to Azure using [Azure Blob Storage](https://azure.microsoft.com/en-us/services/storage/blobs/) and [Azure Front Door](https://azure.microsoft.com/en-us/services/frontdoor/).
+
+## Azure Resources
+
+The following resources are created when deploying Websites to Azure:
+
+- Blob Storage Account with Static Website
+- Front Door Endpoint (with Front Door Classic)
+- Delivery Rules for API rewrites
+
+## Deployment
+
+During deployment the Nitric CLI creates a storage account for your website assets and a Front Door distribution to serve them:
+
+- The declared website is created in Blob Storage
+- A Front Door endpoint is created to serve your website and rewrite all APIs to `/api/{apiName}` using Delivery Rules
+- Cache invalidation is automatic based on changed files
diff --git a/docs/reference/preview-features/index.mdx b/docs/reference/preview-features/index.mdx
index c52c56851..7743d8b12 100644
--- a/docs/reference/preview-features/index.mdx
+++ b/docs/reference/preview-features/index.mdx
@@ -17,6 +17,7 @@ Each preview feature will have its own documentation page, which will include in
| [docker-providers](/providers/custom/docker) | CLI v1.39.0 | [feedback](https://github.com/nitrictech/nitric/issues/605) |
| [sql-databases](/sql) | CLI v1.42.0
AWS Provider v1.6.0
Azure Provider v1.9.0
AWS Terraform Provider v1.8.0 | [feedback](https://github.com/nitrictech/nitric/issues/684) |
| [batch-services](/batch) | CLI v1.54.0
AWS Provider v1.14.0
GCP Provider v1.14.0 | [feedback](https://github.com/nitrictech/nitric/issues/685) |
+| [websites](/websites) | CLI v1.58.0
AWS Provider v1.18.0
Azure Provider v1.18.0 | [feedback](https://github.com/nitrictech/nitric/issues/748) |
## Released Preview Features
diff --git a/docs/websites.mdx b/docs/websites.mdx
new file mode 100644
index 000000000..c393bbc09
--- /dev/null
+++ b/docs/websites.mdx
@@ -0,0 +1,203 @@
+---
+description: 'Hosting and managing websites with Nitric'
+---
+
+# Websites
+
+Nitric provides a simple way to deploy websites to the cloud, allowing you to serve website assets in a serverless environment without managing complex infrastructure.
+
+If you're interested in the architecture, provisioning, or deployment steps, they can be found [here](/architecture/websites).
+
+
+ At this time, websites can only be deployed as static assets. If you require
+ more advanced features, let us know:
+ https://github.com/nitrictech/nitric/issues
+
+
+## Overview
+
+Nitric’s website resource lets you:
+
+- Serve static files (HTML, JS, CSS, images) from cloud storage.
+- Serve your entire Nitric application from a single domain.
+- APIs are automatically served under `/api` and websites are served under `/`.
+- Deploy to various supported providers with minimal configuration.
+- Use infrastructure from code to keep your environment consistent.
+
+### About API Routes
+
+API route rewrites (under `/api`) are **only enabled if you have at least one website defined** in your project.
+We are exploring options to allow enabling API routing independently of websites in the future.
+
+## Enabling Websites
+
+Websites are currently in [Preview](/reference/preview-features). To enable this feature in your project add the following to your `nitric.yaml` file.
+
+```yaml
+preview:
+ - websites
+```
+
+## Create a Website
+
+### 1. Create your website project
+
+Create a new website project using your preferred framework. For example, using [Astro](https://astro.build/) within your nitric project:
+
+```bash
+npm create astro@latest my-website
+```
+
+### 2. Add a website resources
+
+Add a website resource to your `nitric.yaml` file.
+
+```yaml
+websites:
+ - basedir: ./my-website
+ build:
+ command: npm run build
+ output: ./dist
+ dev:
+ command: npm run dev -- --port 4322
+ url: http://localhost:4322
+```
+
+### 3. Start your website locally
+
+Run `nitric start` to start your website locally. This will use the `dev` configuration, which proxies requests to your local development server.
+
+Run `nitric run` command to run your production website locally. This will use the `build` configuration and serve the static files.
+
+### 4. Deploy your website
+
+Run `nitric up` to deploy your website to the cloud.
+
+## Sub Sites
+
+You can also configure sub-sites under different paths. These will be deployed as subdirectories of the main site.
+
+Here is an example configuration:
+
+```yaml
+websites:
+ - basedir: ./my-website
+ build:
+ command: npm run build
+ output: ./dist
+ dev:
+ command: npm run dev -- --port 4322
+ url: http://localhost:4322
+ # the sub site is served under /docs, ensure you do not have conflicting routes
+ - basedir: ./docs-website
+ path: /docs
+ build:
+ command: npm run build
+ output: ./dist
+ dev:
+ command: npm run dev -- --port 4323
+ url: http://localhost:4323
+```
+
+## Why Are All Sites Behind a Single Domain?
+
+Currently, Nitric serves all websites behind a single domain to simplify deployment and management. This approach ensures that your API and website can coexist without CORS issues and allows for relative path usage in your frontend code. Additionally, serving all sites behind a single domain can reduce costs associated with managing multiple domains.
+
+At this time, configuring multiple domains for different websites is not supported. However, we understand the need for this feature and may support multiple domains in the future.
+
+## Conflicting Routes
+
+If you have conflicting routes between your API and website or between multiple sub-sites, you can configure the `path` for each website to avoid conflicts.
+
+### What Happens If There Are Overlapping Paths?
+
+If Nitric detects overlapping paths between websites, an error will be raised during development and deployment.
+
+However, client-side routes (like React Router) are not detected at deployment time — so be careful when designing single-page apps to avoid unexpected overlaps.
+
+### Reserved Paths
+
+The following paths are reserved for use by the Nitric framework and cannot be used as website paths:
+
+- `/api` - used to rewrite API requests
+
+We are looking at making the **rewrite path configurable** in the future, so you will have more flexibility in defining your own path structure.
+
+## API Routes
+
+If you have at least **one website enabled**, Nitric automatically serves your API under the `/api` path.
+
+You can access your API routes at:
+
+```bash
+https:///api//
+```
+
+### When Are API Routes Enabled?
+
+API route rewrites are **only enabled if you have at least one website defined** in your project.
+We are exploring options to allow enabling API routing **without requiring a website** in the future.
+
+### Why This Approach?
+
+By serving your API under the same domain as your website, you avoid **CORS issues** and can use **relative paths** to access your API directly from your frontend code.
+
+### Example API Call
+
+```javascript
+async function fetchData() {
+ // due to apis being served from the same domain thanks to rewrites, no CORS is required
+ const response = await fetch('/api/main/hello')
+ const data = await response.json()
+ console.log(data)
+}
+```
+
+## Support for Frameworks like Next.js
+
+If you're new to deploying websites with Nitric, you might wonder whether frameworks like **Next.js** will work by default.
+
+- **Built static websites** (where your framework generates static assets, such as HTML, CSS, and JavaScript files) are fully supported.
+- **Server-side rendering (SSR)** and **framework-specific API routes** (like Next.js API routes) are **not currently supported**.
+
+If you are using **Next.js**, you can follow the official guide on [static exports](https://nextjs.org/docs/app/building-your-application/deploying/static-exports) to generate a fully static version of your site.
+
+We are actively exploring options to **support server-side applications**, including frameworks like Next.js, in the future.
+
+## Configuration
+
+Here is a breakdown of the configuration options:
+
+```yaml
+websites:
+ - # The path to the website project directory
+ basedir: ./my-website
+ # The path to the website when deployed
+ path: /
+ # The default file to serve when no file is specified
+ index: index.html
+ # The file to serve when a 404 error occurs
+ error: 404.html
+ # The build options for the website
+ build:
+ # The command to build your website
+ command: npm run build
+ # The output directory for the build command
+ output: ./dist
+ # dev configuration is used by the `nitric start` command
+ dev:
+ # The command to run your website locally
+ command: npm run dev -- --port 4322
+ # The URL to access your website locally
+ url: http://localhost:4322
+```
+
+## Cloud Service Mapping
+
+Each cloud provider comes with a set of default services used when deploying resources. You can find the default services for each cloud provider below.
+
+- [AWS](/providers/mappings/aws/websites)
+- [Azure](/providers/mappings/azure/websites)
+- Google Cloud - Not implemented
+
+If you need support for additional clouds or website types, let us know by [opening an issue](https://github.com/nitrictech/nitric/issues) or joining the conversation on [Discord](https://nitric.io/chat).
diff --git a/src/config/index.ts b/src/config/index.ts
index 7145988cf..578e721dd 100644
--- a/src/config/index.ts
+++ b/src/config/index.ts
@@ -11,6 +11,7 @@ import {
CodeBracketIcon,
BeakerIcon,
DocumentDuplicateIcon,
+ WindowIcon,
} from '@heroicons/react/24/outline'
import { SiPulumi, SiTerraform } from 'react-icons/si'
import { NavEntry } from './types'
@@ -120,6 +121,10 @@ export const navigation: NavEntry[] = [
icon: CursorArrowRippleIcon,
href: '/architecture/websockets',
},
+ {
+ title: 'Websites',
+ href: '/architecture/websites',
+ },
{
title: 'Storage',
icon: DocumentDuplicateIcon,
@@ -183,6 +188,11 @@ export const navigation: NavEntry[] = [
icon: CursorArrowRippleIcon,
href: '/websockets',
},
+ {
+ title: 'Websites',
+ icon: WindowIcon,
+ href: '/websites',
+ },
{
title: 'Storage',
icon: DocumentDuplicateIcon,