部署

sitelo 的构建产物就是普通静态文件:sitelo build 会把 HTML、CSS 和 JS 写入 dist/。任何静态托管都可以 —— 下面的配置只假设 npm run build → dist/。

干净 URL 就是带 index.html 的目录(/about/index.html → /about),所以漂亮的 URL 开箱即用,不需要任何重定向规则。404.html 会自动产出 —— 这正是 Netlify、Cloudflare Pages 和 GitHub Pages 都认得的约定。

这些配置的可直接复制版本都在基础示例里(仓库中的 examples/basic)。

静态托管

这些平台会构建站点,并通过自己的 CDN 托管 dist/。

Netlify · Vercel · Cloudflare · AWS Amplify · AWS S3 + CloudFront · Azure Static Web Apps · Firebase Hosting · GitHub Pages · GitLab Pages · Render · DigitalOcean App Platform

Netlify

[build]
  command = "npm run build"
  publish = "dist"

Vercel

{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "framework": null,
  "buildCommand": "npm run build",
  "outputDirectory": "dist",
  "installCommand": "npm install"
}

Cloudflare

Cloudflare 现在建议新项目使用 Workers 而不是 Pages。有了这份配置,npx wrangler deploy 会构建站点并上传 dist/;在 Cloudflare 控制台连接仓库后,每次推送都会运行同一条命令。html_handling: "drop-trailing-slash" 让 /about 无需重定向即可访问,未知 URL 则返回 404.html。

// Cloudflare Workers
{
  "name": "my-site",
  "compatibility_date": "2026-10-01",
  "build": {
    "command": "npm run build"
  },
  "assets": {
    "directory": "./dist",
    "not_found_handling": "404-page",
    "html_handling": "drop-trailing-slash"
  }
}

还在用 Pages?把构建命令设为 npm run build,输出目录设为 dist,或者用 npx wrangler pages deploy dist 上传本地构建。

AWS Amplify

# AWS Amplify 托管
version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm install
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: dist
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*

AWS S3 + CloudFront

让存储桶保持私有,并通过源访问控制(OAC)在前面放置 CloudFront。CloudFront 只会把 / 映射到 index.html,所以要把这个函数关联到分配的查看器请求上,才能提供 sitelo 的简洁 URL。

function handler(event) {
  var request = event.request
  var uri = request.uri

  if (uri.endsWith('/')) {
    request.uri += 'index.html'
  } else if (uri.slice(uri.lastIndexOf('/') + 1).indexOf('.') === -1) {
    request.uri += '/index.html'
  }

  return request
}

私有存储桶在文件缺失时会返回 403,所以要为 403 和 404 添加自定义错误响应,以 404 状态码返回 /404.html。之后每次构建都上传并清除缓存:

npm run build
aws s3 sync dist/ s3://my-bucket --delete
aws cloudfront create-invalidation --distribution-id MY_DISTRIBUTION_ID --paths "/*"

Azure Static Web Apps

在 Azure 门户中创建 Static Web App,应用位置填 /,输出位置填 dist;Azure 会向仓库提交一个运行 npm run build 的 GitHub Actions 工作流。把这个文件放进 public/,构建时会把它复制到 dist/——没有它,Azure 会用自己的错误页而不是 404.html 响应未知 URL。

{
  "responseOverrides": {
    "404": {
      "rewrite": "/404.html"
    }
  }
}

Firebase Hosting

使用 Firebase Hosting 时,先用 firebase use --add 选择项目,再运行 firebase deploy——predeploy 钩子会先构建。未知 URL 会自动返回 404.html,而 trailingSlash: false 让 /about 无需重定向即可访问。

{
  "hosting": {
    "public": "dist",
    "predeploy": "npm run build",
    "trailingSlash": false
  }
}

GitHub Pages

# GitHub Pages
name: Deploy
on:
  push:
    branches: [main]
permissions:
  contents: read
  pages: write
  id-token: write
jobs:
  deploy:
    runs-on: ubuntu-latest
    environment:
      name: github-pages
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
      - run: npm ci && npm run build
      - uses: actions/upload-pages-artifact@v5
        with:
          path: dist
      - uses: actions/deploy-pages@v5

部署在子路径下(user.github.io/repo)?构建时加上 --base /repo/。

GitLab Pages

GitLab 会自行处理 404.html 和目录 URL,所以这个任务只负责构建并发布 dist/。

# GitLab Pages
deploy-pages:
  image: node:24
  script:
    - npm ci
    - npm run build
  pages:
    publish: dist
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Render

把下面的 Blueprint 加入仓库,或在控制台新建一个静态站点,填入相同的构建命令和发布目录。简洁 URL 和 404.html 无需任何重定向规则即可生效。

# Render
services:
  - type: web
    name: my-site
    runtime: static
    buildCommand: npm install && npm run build
    staticPublishPath: ./dist

DigitalOcean App Platform

把 github.repo 改成你的仓库,然后用 doctl apps create --spec .do/app.yaml 创建应用,或者把这份规范粘贴到控制台。App Platform 默认会解析目录 URL 并返回 404.html。

# DigitalOcean App Platform
name: my-site
static_sites:
  - name: site
    github:
      repo: your-name/your-site
      branch: main
      deploy_on_push: true
    build_command: npm run build
    output_dir: dist

容器托管

这些平台运行容器,一个 Dockerfile 就能覆盖全部:先由 Node 构建站点,再由 Caddy 以简洁 URL 托管 dist/,未知 URL 则返回 404.html。平台设置了 PORT 时 Caddy 就监听它,否则监听 80 端口。把这三个文件都放在项目根目录。

Railway · Fly.io · Heroku · Google Cloud Run

FROM node:24-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM caddy:2-alpine
COPY Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/dist /app/dist
node_modules
dist
.git
:{$PORT:80} {
  root * /app/dist
  encode zstd gzip
  try_files {path} {path}/index.html
  file_server
  handle_errors {
    rewrite * /{err.status_code}.html
    file_server
  }
}

Railway

Railway 只要发现 Dockerfile 就会用它构建,无需任何设置。没有 Dockerfile 时,添加服务变量 RAILPACK_SPA_OUTPUT_DIR=dist 并保留 Caddyfile:Railpack 会通过它托管 dist/,而不是使用默认的单页应用配置(那会用首页响应所有未知 URL)。

Fly.io

先运行 fly launch 创建应用,之后运行 fly deploy。

# Fly.io
app = "my-site"

[http_service]
  internal_port = 80
  force_https = true
  auto_stop_machines = "stop"
  auto_start_machines = true
  min_machines_running = 0

Heroku

Heroku 通过 heroku.yml 构建 Dockerfile。先用 heroku stack:set container 把应用切换到容器栈(只需一次),再运行 git push heroku main。

# Heroku
build:
  docker:
    web: Dockerfile

Google Cloud Run

在项目根目录运行 gcloud run deploy --source .:Cloud Build 会构建 Dockerfile,再由 Cloud Run 提供服务。

自有服务器

用 rsync -a --delete dist/ you@server:/var/www/my-site/ 把构建产物复制到服务器,再用 Caddy 或 nginx 托管——简洁 URL 和 404.html 的表现与上面的平台相同。Caddy 会自行为域名申请 HTTPS 证书;用 nginx 时,请用 certbot 添加证书。

example.com {
  root * /var/www/my-site
  encode zstd gzip
  try_files {path} {path}/index.html
  file_server
  handle_errors {
    rewrite * /{err.status_code}.html
    file_server
  }
}
server {
  listen 80;
  server_name example.com;
  root /var/www/my-site;

  location / {
    try_files $uri $uri/index.html =404;
  }

  error_page 404 /404.html;
}

发布之前