Implementação
Nesta página
Uma compilação do sitelo são ficheiros estáticos e mais nada: sitelo build escreve HTML, CSS e JS em dist/. Qualquer alojamento estático serve — as configurações abaixo só assumem npm run build → dist/.
Os URLs limpos são diretórios com index.html (/about/index.html → /about), por isso os URLs bonitos funcionam logo, sem regras de redirecionamento. O 404.html é emitido automaticamente — é a convenção que o Netlify, o Cloudflare Pages e o GitHub Pages entendem.
Há versões prontas a copiar de tudo isto no exemplo básico (examples/basic no repositório).
Alojamento estático
Estes serviços compilam o site e servem dist/ a partir da sua própria CDN.
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
A Cloudflare recomenda agora o Workers em vez do Pages para projetos novos. Com esta configuração, npx wrangler deploy compila o site e envia dist/, e ligar o repositório no painel da Cloudflare corre o mesmo comando a cada push. html_handling: "drop-trailing-slash" serve /about sem redirecionamento, e os URL desconhecidos recebem o 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"
}
}Ainda no Pages? Define o comando de compilação npm run build e o diretório de saída dist, ou envia uma compilação local com npx wrangler pages deploy dist.
AWS Amplify
# Alojamento 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
Mantém o bucket privado e coloca o CloudFront à frente com origin access control. O CloudFront só mapeia / para index.html, por isso associa esta função aos pedidos de visualizador da distribuição para servir os URL limpos do sitelo.
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
}Um bucket privado responde com 403 a um ficheiro em falta, por isso adiciona respostas de erro personalizadas para 403 e 404 que devolvam /404.html com o estado 404. Depois envia cada compilação e limpa a cache:
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
Cria a Static Web App no portal do Azure com / como localização da app e dist como localização de saída; o Azure adiciona ao repositório um workflow do GitHub Actions que corre npm run build. Coloca este ficheiro em public/ para que a compilação o copie para dist/ — sem ele, o Azure responde a URL desconhecidos com a sua própria página de erro em vez de 404.html.
{
"responseOverrides": {
"404": {
"rewrite": "/404.html"
}
}
}Firebase Hosting
No Firebase Hosting, escolhe um projeto com firebase use --add e depois corre firebase deploy — o hook predeploy compila primeiro. Os URL desconhecidos recebem o 404.html automaticamente, e trailingSlash: false serve /about sem redirecionamento.
{
"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@v5Implementas num subcaminho (user.github.io/repo)? Compila com --base /repo/.
GitLab Pages
O GitLab serve o 404.html e os URL de diretório sozinho, por isso o job só compila e publica dist/.
# GitLab Pages
deploy-pages:
image: node:24
script:
- npm ci
- npm run build
pages:
publish: dist
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHRender
Adiciona o Blueprint abaixo ao repositório ou cria um site estático no painel com o mesmo comando de compilação e diretório de publicação. Os URL limpos e o 404.html funcionam sem regras de redirecionamento.
# Render
services:
- type: web
name: my-site
runtime: static
buildCommand: npm install && npm run build
staticPublishPath: ./distDigitalOcean App Platform
Indica o teu repositório em github.repo e cria a app com doctl apps create --spec .do/app.yaml — ou cola a especificação no painel. O App Platform resolve URL de diretório e serve o 404.html por predefinição.
# 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: distAlojamento de contentores
Estes serviços correm um contentor, e um só Dockerfile serve para todos: o Node compila o site e depois o Caddy serve dist/ com URL limpos e responde aos desconhecidos com o 404.html. O Caddy fica à escuta em PORT quando o serviço o define, e na porta 80 caso contrário. Adiciona os três ficheiros à raiz do projeto.
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/distnode_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
O Railway compila o Dockerfile sempre que o encontra, por isso não há nada a configurar. Sem o Dockerfile, adiciona a variável de serviço RAILPACK_SPA_OUTPUT_DIR=dist e mantém o Caddyfile: o Railpack serve então dist/ através dele em vez da predefinição de SPA, que responde a URL desconhecidos com a página inicial.
Fly.io
Corre fly launch para criar a app e depois 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 = 0Heroku
O Heroku compila o Dockerfile através do heroku.yml. Muda a app para a stack de contentores uma vez com heroku stack:set container e depois corre git push heroku main.
# Heroku
build:
docker:
web: DockerfileGoogle Cloud Run
Corre gcloud run deploy --source . na raiz do projeto: o Cloud Build compila o Dockerfile e o Cloud Run serve-o.
O teu próprio servidor
Copia a compilação para o servidor com rsync -a --delete dist/ you@server:/var/www/my-site/ e serve-a com Caddy ou nginx — os URL limpos e o 404.html funcionam como nos serviços acima. O Caddy obtém sozinho um certificado HTTPS para o domínio; com nginx, adiciona um com o 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;
}Antes de publicar
- Define
siteemsitelo.config.jspara que ositemap.xmlseja gerado — vê Configuração - Acrescenta um
src/404.ht.jspara teres uma página de «não encontrado» com a tua identidade (caso contrário é emitida uma por omissão) sitelo previewserve a compilação de produção localmente para uma verificação final