Web 服务、反向代理、HTTPS 与 systemd
黄玮
2026-01
你写的 Python/Node.js 程序通常监听在 localhost:8000。
为什么不直接暴露给公网?
/etc/nginx/sites-available/default:
server {
listen 80;
server_name example.com;
# 静态文件
location /static/ {
root /var/www/html;
}
# 反向代理
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
修改配置后,不要直接重启服务(会断开现有连接):
# 检查语法(养成习惯:每次改配置都先检查)
sudo nginx -t
# 输出示例:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
# 平滑重载配置(不断开连接)
sudo systemctl reload nginx
口诀:改配置 →
nginx -t→reload。永远不要跳过测试步骤。
Caddy — 默认安全,零配置 HTTPS 的 Web 服务器。
在共享服务器上你没有 root 权限,无法安装/配置 Nginx。 Caddy 可以在用户态运行,一行命令启动反向代理。
适用于有 root 权限的 Debian/Ubuntu 系统:
# 1. 安装依赖
sudo apt install -y debian-keyring debian-archive-keyring \
apt-transport-https curl
# 2. 添加 GPG 签名密钥
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# 3. 添加软件源
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
# 4. 安装
sudo apt update && sudo apt install caddy
优势:自动注册为 systemd 服务,
apt upgrade即可跟随上游”傻瓜式”滚动升级。
上面的第 2、3 步在国内网络环境下大概率失败:
$ curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl: (6) Could not resolve host: dl.cloudsmith.io
问题出在哪里?
curl 失败(DNS 无法解析),但管道 |
不会阻止后续命令执行gpg --dearmor
读到空输入,可能创建空文件或报错gpg 没报错,apt update
后续会遇到签名验证失败——错误被”延迟暴露”教训:不要盲目复制粘贴”一行安装”命令。执行前先验证网络连通性。
# 在脚本开头加上 pipefail,管道中任一命令失败则整体失败
set -euo pipefail
# 执行前先验证网络连通性
curl -sSf 'https://dl.cloudsmith.io' > /dev/null && echo "网络可达" || echo "网络不通"
# 或者将管道拆成多步,逐步检查
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
-o /tmp/caddy-key.asc || { echo "下载 GPG 密钥失败"; exit 1; }
sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg \
/tmp/caddy-key.asc
口诀:
set -euo pipefail是脚本的”安全带”。管道不是黑箱,拆开看才放心。
无需 root,适合共享服务器 / 开发环境:
# 从 GitHub Releases 下载(选择对应架构)
# https://github.com/caddyserver/caddy/releases
# 解压并放到 ~/bin/ 或其他 PATH 目录
chmod +x caddy
mv caddy ~/bin/
启动反向代理:
# 反向代理 :8000 → :8080
caddy reverse-proxy --from :8000 --to :8080
# 指定域名时自动申请 HTTPS 证书
caddy reverse-proxy --from myapp.example.com --to :8080
| 方式 | apt 源 | 直接下载二进制 |
|---|---|---|
| 需要 root | 是 | 否 |
| 自动 systemd 服务 | 是 | 需手动配置 |
| 版本升级 | apt upgrade 傻瓜式滚动更新 |
手动下载替换二进制 |
| 网络要求 | 需访问 cloudsmith.io | 需访问 github.com |
| 适合场景 | 有 root 的生产服务器 | 共享服务器 / 开发环境 |
| 特性 | Nginx | Caddy |
|---|---|---|
| 行业地位 | 事实标准,33%+ 市场份额 | 新兴选择,快速成长 |
| 配置复杂度 | 手动编写 conf 文件 | 极简命令行 / Caddyfile |
| 需要 root | 是(监听 80/443) | 否(可监听高端口) |
| HTTPS | 需手动配置证书 | 自动申请/续期 |
| 适合场景 | 生产环境、复杂需求 | 开发环境、共享服务器 |
策略:生产用 Nginx,开发/共享服务器用 Caddy。两条路都掌握。
tail -f /var/log/nginx/access.log:
192.168.1.5 - - [12/Jan/2026:10:00:00 +0800] "GET /api/v1/users HTTP/1.1" 200 1024 "-" "Mozilla/5.0..."
关注点:
自定义 log_format 添加 $request_time:
log_format timed '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" $request_time';
access_log /var/log/nginx/access.log timed;
场景:老板说「网站好像有点慢」,你需要用数据说话。
# 1. 状态码分布 — 服务健康吗?
awk '{print $9}' access.log | sort | uniq -c | sort -rn
# 1523 200
# 234 404
# 12 500 ← 500 错误需要关注!
# 2. 响应最慢的 5 个请求(假设已配置 $request_time)
awk '{print $NF, $0}' access.log | sort -rn | head -5
# 3. 访问量 Top 10 IP
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -10
# 4. 实时监控 500 错误
tail -f access.log | awk '$9 == 500 {print $0}'
管道思维:
awk 提取字段→sort 排序→uniq -c 计数→sort -rn 按数量排序。这套组合拳适用于任何文本日志分析。
HTTP 是明文传输,任何人都可以截获你的数据:
TLS 握手(简化版):客户端和服务器协商密钥 → 之后所有数据加密传输。
2018 年起,Chrome 对所有 HTTP 站点标记「不安全」。HTTPS 已是必选项,不是可选项。
局域网/开发环境,没有公网域名?用自签名证书:
# 生成自签名证书(一行命令)
openssl req -x509 -newkey rsa:2048 \
-keyout key.pem -out cert.pem \
-days 365 -nodes \
-subj "/CN=localhost"
Nginx 配置 HTTPS:
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://127.0.0.1:8080;
}
}
验证:curl -k https://localhost(-k
跳过证书验证警告)
Caddy 的 HTTPS 是零配置的:
# 有公网域名 → 自动申请 Let's Encrypt 证书
caddy reverse-proxy --from example.com --to :8080
# 局域网/localhost → 自动生成自签名证书
caddy reverse-proxy --from :443 --to :8080
| 场景 | Nginx | Caddy |
|---|---|---|
| 公网域名 | 手动配置 certbot + 证书 | 自动(Let’s Encrypt) |
| 局域网/localhost | 手动 openssl + 配置 | 自动(自签名) |
| 证书续期 | 手动 crontab | 自动后台续期 |
实验时:两条路都走一遍,体验「手动 vs 自动」的差异。
python3 -m http.server 8080 & 的问题:
systemd = Linux 的「服务管家」,解决以上所有问题。
# 服务生命周期管理
sudo systemctl start myweb # 启动
sudo systemctl stop myweb # 停止
sudo systemctl restart myweb # 重启
sudo systemctl status myweb # 查看状态(最常用!)
# 开机自启动
sudo systemctl enable myweb # 开机自动启动
sudo systemctl disable myweb # 取消自动启动
# 查看日志(神器)
sudo journalctl -u myweb # 查看全部日志
sudo journalctl -u myweb -f # 实时跟踪日志(类似 tail -f)
sudo journalctl -u myweb --since today # 今天的日志
记忆技巧:
systemctl= 「系统控制」,所有服务操作都走它。
创建 /etc/systemd/system/myweb.service:
[Unit]
Description=My Python Web Server
After=network.target
[Service]
Type=simple
User=你的用户名
WorkingDirectory=/home/你的用户名/www
ExecStart=/usr/bin/python3 -m http.server 8080
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
启用并启动:
sudo systemctl daemon-reload # 通知 systemd 读取新配置
sudo systemctl enable myweb # 开机自启
sudo systemctl start myweb # 立即启动
sudo systemctl status myweb # 确认运行状态
验证闭环:关掉终端 →
curl http://localhost:8080→ 仍然可以访问 = 成功!
没有 sudo 权限也能用 systemd,使用 --user 模式:
# 创建用户级服务目录
mkdir -p ~/.config/systemd/user/
# 放置 service 文件(去掉 User= 行,默认当前用户)
cp myweb.service ~/.config/systemd/user/
# 所有命令加 --user,无需 sudo
systemctl --user daemon-reload
systemctl --user enable --now myweb
systemctl --user status myweb
systemctl --user journalctl -u myweb # 查看日志也不需要 sudo
| 特性 | 系统级 (/etc/systemd/system/) |
用户级 (~/.config/systemd/user/) |
|---|---|---|
| 需要 root | 是 | 否 |
| 用户登录时才运行 | 否(开机即运行) | 默认是(可配置 lingering) |
| 端口限制 | 无 | 只能绑定 >1024 端口 |
开启 lingering(用户注销后服务继续运行):
loginctl enable-linger 你的用户名
每一步都是生产环境的基本要求。你现在已经掌握了 Web 服务交付的完整链路。