前三篇完成了安全加固、终端环境配置和运行环境安装,这一篇把博客真正跑起来——拉代码、构建、挂域名、上 HTTPS。至此,这个博客从 GitHub Pages 迁到了我自己的服务器上。
拉取代码并适配
base 前缀
将 blog 仓库克隆到服务器,现在部署到自己的域名,base 设置项是将所有访问请求加上 base 前缀,访问地址变成 https://你的域名.com/<base>,检查 astro.config.ts:
// astro.config.ts
export default defineConfig({
base: "/blog",
// ...
});
如果你希望博客直接挂在根路径
https://你的域名.com/,那把base改成"/"就行。不过我个人习惯/blog,方便以后在根路径挂别的东西。
site.url 域名
在 astro-paper.config.ts 里的 SITE.url 设置成自己的域名(填根域名即可,不需要带 /blog,Astro 会自动拼接 base 路径):
// astro-paper.config.ts
site: {
url: "https://你的域名.com", // 改成实际域名
// ...
}
构建
pnpm install --frozen-lockfile
pnpm build
pnpm build 做了三件事:类型检查、Astro 静态生成、Pagefind 搜索索引。完成后 dist/ 目录里就是完整的静态网站。
验证一下:
ls dist/
# index.html posts/ tags/ search/ pagefind/ ...
注意 dist/ 里的文件就是最终网站内容,没有 /blog 子目录——/blog 只是 Astro 在 HTML 中生成资源 URL 时的路径前缀,文件系统层面不需要体现。
Nginx 站点配置
因为 base 路径是 /blog,Nginx 需要把 /blog/* 的请求指向 dist/ 目录。创建 Nginx 配置文件:
sudo vim /etc/nginx/conf.d/blog.conf
server {
listen 80;
server_name 你的域名.com www.你的域名.com;
# 根路径重定向到 /blog/(暂时,等挂上更多服务再调整)
location = / {
return 301 /blog/;
}
# /blog 不带斜杠 → 301 到 /blog/
# 这个必须放在 /blog/ 前面,不然 /blog 会被 /blog/ 拦截并 404
location = /blog {
return 301 /blog/;
}
# 博客主路径,alias 到构建产物目录
location /blog/ {
alias /var/www/blog/dist/;
index index.html;
try_files $uri $uri/ $uri.html =404;
# 静态资源缓存(必须嵌套在 /blog/ 内,否则正则优先级更高,截走请求却找不到文件)
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# 安全相关 header
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
}
关键点解释:
base: "/blog"意味着 Astro 生成的 HTML 中所有资源路径都以/blog/开头(比如<script src="/blog/assets/main.js">)。Nginx 用alias把/blog/这个请求路径映射到文件系统的dist/。location = /blog单独处理不带尾部斜杠的请求——/blog匹配不到/blog/(前缀匹配要求路径以/blog/开头),会一路穿透到 nginx 的默认 root/usr/share/nginx/html然后 404。加一个精确匹配做 301 跳转解决。- 静态资源缓存的 location 必须嵌套在
/blog/内部。Nginx 的 location 匹配优先级是正则~*> 前缀,如果放在外面,所有.css/.js请求(包括/blog/_astro/main.css)都被外层正则截走,但它没有alias,会走到默认 root 导致 404,页面丢失所有样式。 - Pagefind 的资源路径已经是
/blog/pagefind/...,被location /blog/统一处理,不需要单独 location。
验证配置并重载:
sudo nginx -t # 先验证语法
sudo systemctl reload nginx
此时访问 http://你的域名.com/blog(或暂时用 IP),应该能看到博客首页。访问 http://你的域名.com 会 301 跳到 /blog。
DNS 解析
在域名管理后台(阿里云 DNS / Cloudflare 等)添加 A 记录:
| 类型 | 主机记录 | 记录值 |
|---|---|---|
| A | @ | 服务器公网 IP |
| A | www | 服务器公网 IP |
生效时间取决于 TTL 设置,通常几分钟到几小时不等。用 dig 你的域名.com 或 nslookup 查看是否已解析。
HTTPS:Let’s Encrypt + Certbot
有了域名+DNS 解析,就能申请免费 SSL 证书了。Ubuntu 下安装 certbot 及其 Nginx 插件:
sudo apt install -y certbot python3-certbot-nginx
--nginx 插件能自动读取 Nginx 配置中的 server_name,自动完成证书申请和配置:
sudo certbot --nginx -d 你的域名.com -d www.你的域名.com
过程中会问几个问题——输入邮箱(用于证书过期提醒)、同意服务条款、是否自动重定向 HTTP 到 HTTPS(选 no)。
选 no 的原因:certbot 的自动重定向会把 80 端口的 server 块替换成一个纯跳转块(
return 301 https://...),原有 location 配置丢失。选 no 保留我们手写的 80 端口配置,后面手动加上重定向更可控。
申请成功后,certbot 会在配置文件中追加 443 端口的 SSL server 块,但不会自动放行 443 端口。需要手动操作:
# UFW 放行 HTTPS
sudo ufw allow 443/tcp
# 阿里云安全组也要添加 443 端口入方向规则
然后手动在 80 端口的 server 块末尾加上 HTTPS 重定向——把原来博客配置底部的 80 server 块改成:
server {
listen 80;
server_name huazaiki.com www.huazaiki.com;
return 301 https://$host$request_uri;
}
最终的 blog.conf 是两块 server(443 放内容、80 跳 HTTPS)。完整配置参考:
# == HTTPS server(certbot 自动生成的 443 块,手动保留内容 location)==
server {
server_name 你的域名.com www.你的域名.com;
location = / {
return 301 /blog/;
}
location = /blog {
return 301 /blog/;
}
location /blog/ {
alias /var/www/blog/dist/;
index index.html;
try_files $uri $uri/ $uri.html =404;
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/你的域名.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/你的域名.com/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
# == HTTP server:全部跳转 HTTPS ==
server {
listen 80;
server_name 你的域名.com www.你的域名.com;
return 301 https://$host$request_uri;
}
重载配置:
sudo nginx -t && sudo systemctl reload nginx
验证一下自动续期是否正常:
sudo certbot renew --dry-run
如果输出 “Congratulations, all simulated renewals succeeded”,就没问题。certbot 默认通过 systemd timer 每天检查一次,证书到期前 30 天自动续期。
收尾
博客部署之后,日常更新文章的流程将在下一篇文章中讲述,涉及使用 Github Actions、配置 Webhook 等。