Astro 部署完全指南
本教程将详细介绍如何将 Astro 应用部署到各种平台,包括静态托管和服务端渲染的配置。
部署前准备
1. 构建配置优化
// astro.config.ts
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
import vercel from '@astrojs/vercel/serverless';
import netlify from '@astrojs/netlify/functions';
export default defineConfig({
// 根据部署目标选择输出模式
output: 'static', // 'static' | 'server' | 'hybrid'
// 根据部署平台选择适配器
adapter: process.env.DEPLOY_TARGET === 'vercel' ? vercel() :
process.env.DEPLOY_TARGET === 'netlify' ? netlify() :
process.env.DEPLOY_TARGET === 'node' ? node({ mode: 'standalone' }) :
undefined,
build: {
// 生产环境优化
inlineStylesheets: 'auto',
split: true,
excludeMiddleware: false,
},
// 站点配置
site: 'https://your-domain.com',
base: '/', // 如果部署在子路径,修改此值
// 服务端渲染配置
server: {
port: 3000,
host: true
}
});2. 环境变量配置
# .env.production
PUBLIC_SITE_URL=https://your-domain.com
PUBLIC_API_BASE_URL=https://api.your-domain.com
# 私有环境变量(服务端)
DATABASE_URL=postgresql://...
API_SECRET_KEY=your-secret-key
SMTP_PASSWORD=your-smtp-password// src/env.d.ts
/// <reference types="astro/client" />
interface ImportMetaEnv {
readonly PUBLIC_SITE_URL: string;
readonly PUBLIC_API_BASE_URL: string;
readonly DATABASE_URL: string;
readonly API_SECRET_KEY: string;
readonly SMTP_PASSWORD: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}静态部署
1. Netlify 部署
netlify.toml 配置:
[build]
command = "npm run build"
publish = "dist"
[build.environment]
NODE_VERSION = "18"
NPM_FLAGS = "--prefix=/opt/buildhome/.nodejs/bin/"
# 重定向规则
[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
# 头部设置
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
[[headers]]
for = "/assets/*"
[headers.values]
Cache-Control = "public, max-age=31536000, immutable"GitHub Actions 自动部署:
# .github/workflows/deploy-netlify.yml
name: Deploy to Netlify
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
PUBLIC_SITE_URL: ${{ secrets.PUBLIC_SITE_URL }}
- name: Deploy to Netlify
uses: netlify/actions/cli@master
with:
args: deploy --prod --dir=dist
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}2. Vercel 部署
vercel.json 配置:
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"devCommand": "npm run dev",
"installCommand": "npm install",
"framework": "astro",
"rewrites": [
{
"source": "/api/(.*)",
"destination": "/api/$1"
}
],
"headers": [
{
"source": "/assets/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}3. GitHub Pages 部署
# .github/workflows/deploy-github-pages.yml
name: Deploy to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Setup Pages
id: pages
uses: actions/configure-pages@v4
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
PUBLIC_SITE_URL: ${{ steps.pages.outputs.origin }}
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./dist
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4服务端渲染 (SSR) 部署
1. Node.js 服务器部署
Dockerfile:
# 多阶段构建
FROM node:18-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY package*.json ./
RUN npm ci --only=production
# 复制源代码并构建
COPY . .
RUN npm run build
# 生产镜像
FROM node:18-alpine AS runner
WORKDIR /app
# 创建非 root 用户
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 astro
# 复制构建产物
COPY --from=builder --chown=astro:nodejs /app/dist ./dist
COPY --from=builder --chown=astro:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=astro:nodejs /app/package.json ./package.json
# 切换到非 root 用户
USER astro
EXPOSE 3000
ENV HOST=0.0.0.0
ENV PORT=3000
CMD ["node", "./dist/server/entry.mjs"]docker-compose.yml:
version: '3.8'
services:
astro-app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=${DATABASE_URL}
- API_SECRET_KEY=${API_SECRET_KEY}
restart: unless-stopped
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- astro-app
restart: unless-stoppedNginx 配置:
# nginx.conf
events {
worker_connections 1024;
}
http {
upstream astro_app {
server astro-app:3000;
}
# 启用 gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
server {
listen 80;
server_name your-domain.com;
# 重定向到 HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL 配置
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# 静态资源缓存
location /assets/ {
proxy_pass http://astro_app;
expires 1y;
add_header Cache-Control "public, immutable";
}
# 代理到 Astro 应用
location / {
proxy_pass http://astro_app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}
}2. Serverless 部署
AWS Lambda (使用 SST):
// sst.config.ts
import { SSTConfig } from "sst";
import { AstroSite } from "sst/constructs";
export default {
config(_input) {
return {
name: "astro-app",
region: "us-east-1",
};
},
stacks(app) {
app.stack(function Site({ stack }) {
const site = new AstroSite(stack, "site", {
path: ".",
buildCommand: "npm run build",
environment: {
PUBLIC_SITE_URL: process.env.PUBLIC_SITE_URL!,
DATABASE_URL: process.env.DATABASE_URL!,
},
customDomain: {
domainName: "your-domain.com",
hostedZone: "your-domain.com",
},
});
stack.addOutputs({
SiteUrl: site.url,
});
});
},
} satisfies SSTConfig;部署优化策略
1. 构建优化
// scripts/optimize-build.ts
import { execSync } from 'child_process';
import { readFileSync, writeFileSync } from 'fs';
import { gzipSync } from 'zlib';
// 构建前清理
execSync('rm -rf dist');
// 执行构建
execSync('npm run build');
// 分析构建产物
const distFiles = execSync('find dist -type f -name "*.js" -o -name "*.css"', { encoding: 'utf8' })
.split('\n')
.filter(Boolean);
console.log('构建产物分析:');
distFiles.forEach(file => {
const content = readFileSync(file);
const gzipped = gzipSync(content);
console.log(`${file}: ${content.length} bytes (${gzipped.length} bytes gzipped)`);
});2. 缓存策略
// middleware/cache-headers.ts
import type { MiddlewareHandler } from 'astro';
export const cacheHeaders: MiddlewareHandler = async (context, next) => {
const response = await next();
const { pathname } = context.url;
// 静态资源长期缓存
if (pathname.startsWith('/assets/')) {
response.headers.set('Cache-Control', 'public, max-age=31536000, immutable');
}
// HTML 页面短期缓存
else if (pathname.endsWith('.html') || !pathname.includes('.')) {
response.headers.set('Cache-Control', 'public, max-age=3600, must-revalidate');
}
// API 响应缓存
else if (pathname.startsWith('/api/')) {
response.headers.set('Cache-Control', 'public, max-age=300');
}
return response;
};3. 监控和日志
// utils/monitoring.ts
export class DeploymentMonitor {
private static instance: DeploymentMonitor;
static getInstance() {
if (!this.instance) {
this.instance = new DeploymentMonitor();
}
return this.instance;
}
logDeployment(version: string, environment: string) {
console.log(`Deployment started: ${version} to ${environment}`);
// 发送到监控服务
fetch('/api/monitoring/deployment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
version,
environment,
timestamp: new Date().toISOString(),
status: 'started'
})
});
}
logError(error: Error, context: string) {
console.error(`Deployment error in ${context}:`, error);
// 发送错误报告
fetch('/api/monitoring/error', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
error: error.message,
stack: error.stack,
context,
timestamp: new Date().toISOString()
})
});
}
}部署检查清单
✅ 部署前检查
- 环境变量配置正确
- 构建命令测试通过
- 静态资源路径正确
- API 端点配置正确
- SSL 证书配置(生产环境)
✅ 性能优化
- 启用 gzip/brotli 压缩
- 配置 CDN
- 设置适当的缓存头
- 优化图片和字体
- 启用 HTTP/2
✅ 安全配置
- 设置安全头
- 配置 CORS
- 隐藏服务器信息
- 定期更新依赖
- 配置防火墙规则
✅ 监控和维护
- 设置健康检查
- 配置错误监控
- 设置性能监控
- 配置日志收集
- 建立备份策略
通过遵循这个完整的部署指南,你将能够成功地将 Astro 应用部署到各种平台,并确保应用的稳定性和性能。