这篇是服务器搭建笔记,和 DNS 无关。站内的加密 DNS 服务在dns.23-4.cn。
Alpine 做 Web 服务器有几个很实际的好处:镜像小、内存占用低、包管理干净。但它的目录结构、服务管理和多数人熟悉的 Debian 系不一样,第一次搭容易在几个固定位置上卡住。
这篇是把一个 Typecho 博客在 Alpine 上跑起来的完整清单,按顺序执行即可,重点标出容易踩的坑。
一、装包
apk update
apk add nginx \
php84 php84-fpm php84-session php84-json \
php84-pdo_sqlite php84-mbstring php84-openssl \
php84-curl php84-dom php84-fileinfo php84-iconv \
sqlite注意几点:
- Alpine 的 PHP 包按大版本命名(
php84),不是php。装之前先apk search php84确认版本号,Alpine 升级后包名会变; php84-pdo_sqlite必须显式安装。Typecho 用 SQLite 时依赖它,缺了会在安装页报「无法连接数据库」,而错误信息不会告诉你缺的是哪个扩展;php84-session也是必需的,Typecho 的登录和 CSRF 依赖它。
二、服务管理是 OpenRC,不是 systemd
这是 Debian 用户最容易懵的地方:
rc-update add nginx default # 开机自启
rc-update add php-fpm84 default
rc-service nginx start
rc-service php-fpm84 start
rc-service nginx status- 没有
systemctl,对应的是rc-service/rc-update; - 日志不在
journalctl里,而是各自的日志文件(nginx 在/var/log/nginx/,PHP 在/var/log/php84/error.log); - 服务的 init 脚本在
/etc/init.d/。
三、PHP-FPM 监听
确认 /etc/php84/php-fpm.d/www.conf 里的监听方式:
listen = 127.0.0.1:9000
; 或者用 unix socket,性能略好
; listen = /run/php-fpm84/php-fpm.sock用 TCP 更省事,不用处理 socket 的属主和权限。用 socket 的话记得让 nginx 用户能读写。
四、nginx 站点配置
server {
listen 80;
server_name example.com;
root /var/www/typecho;
index index.php index.html;
client_max_body_size 20m;
location / {
try_files $uri $uri/ /index.php$uri?$query_string;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2?)$ {
expires 7d;
add_header Cache-Control "public";
try_files $uri =404;
}
location ~ .*\.php(\/.*)*$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_read_timeout 60s;
}
}这里有三个必须理解的点
1. try_files ... /index.php$uri?$query_string —— 这是伪静态的关键。
Typecho 的「重写」打开后,链接是 /archives/1/、/category/xxx/ 这种没有 .php 的形式。这些路径在磁盘上不存在,必须交给 index.php 处理。写成 /index.php?$query_string 会丢掉路径信息,结果是所有伪静态链接都变成首页或 404。
2. fastcgi_split_path_info + PATH_INFO —— 少了它,Typecho 认不出路由。
请求 /index.php/archives/1/ 时,PHP 需要知道 .php 后面那一段是「路径信息」。没有这两行,Typecho 拿不到 /archives/1/,就会走 404 分支。这个坑的症状是:首页正常,但所有文章页、分类页、标签页全部 404。
3. location ~* \.(js|css|...)$ 这个正则块要放在 location / 后面、php 块前面。
nginx 的匹配规则是「正则优先于前缀,正则之间按书写顺序」。顺序写错会让静态资源的 try_files 覆盖掉别的规则,或者反过来让 .php 请求走了静态分支——后者会把 PHP 源码当文本吐出来,非常危险。配完一定要测:
# 如果这条命令返回了 PHP 源码,说明 php 块没匹配上
curl -s http://example.com/index.php | head -3五、装 Typecho 与 config.inc.php
下载解压到 /var/www/typecho,然后走网页安装。SQLite 的配置在 config.inc.php 里长这样:
$db = new \Typecho\Db('Pdo_SQLite', 'typecho_');
$db->addServer(array(
'file' => '/var/lib/typecho/typecho.db',
), \Typecho\Db::READ | \Typecho\Db::WRITE);
\Typecho\Db::set($db);把数据库文件放在 Web 根目录之外(上面放在 /var/lib/typecho/)。放在根目录里的后果是:如果哪天 nginx 配置被人改错、或者多了一个不严谨的 location,.db 文件可能被直接下载——里面是所有用户的密码哈希。
六、权限
# Web 根目录给 web 用户,不要给 root
chown -R nobody:nobody /var/www/typecho
# 数据库目录只给 web 用户
chown -R nobody:nobody /var/lib/typecho
chmod 750 /var/lib/typecho
chmod 640 /var/lib/typecho/typecho.dbAlpine 上 nginx 的 worker 默认跑在 nginx 用户、PHP-FPM 的 pool 默认是 nobody。要让写文件的进程(PHP-FPM)对目录有写权限,否则上传附件、写 session、更新配置都会失败。查当前是谁在跑:
ps aux | grep -E "nginx|php-fpm" | grep -v grep七、上 HTTPS
apk add certbot
certbot certonly --webroot -w /var/www/acme -d example.com -d www.example.com在 nginx 里加一个 location /.well-known/acme-challenge/ { root /var/www/acme; },然后 80 端口 301 到 443。
续期一定要验证能跑通,别等三个月后证书过期才发现:
certbot renew --dry-run八、上线前检查清单
# 1. 伪静态是否生效(文章页应该 200,不是 404)
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/archives/1/
# 2. 分类 / 标签页
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/category/default/
# 3. feed
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/feed/
# 4. PHP 没有被当源码吐出
curl -s https://example.com/index.php | head -1
# 5. 数据库文件不可下载(应该是 403 或 404)
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/typecho.db
# 6. 根分区还剩多少
df -h /第 5 条尤其要做。小服务器上数据库就几十到几百 KB,很容易被随手放在根目录里。
九、最容易踩的五个坑
| 症状 | 原因 |
|---|---|
| 首页正常,其他页面全 404 | 缺 PATH_INFO / fastcgi_split_path_info |
| 所有伪静态链接跳到首页 | try_files 写成了 /index.php?$query_string,丢了 $uri |
| 装 Typecho 时报连不上数据库 | 没装 php84-pdo_sqlite |
| 上传附件 / 登录失败 | PHP-FPM 的运行用户对目录没有写权限 |
浏览器直接下载到了 .db 文件 | 数据库放在 Web 根目录里,且没有 deny 规则 |