<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/rss-style.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:version="2.0"><channel><title>Code &amp;Flower</title><description>🌸 轻盈、优雅、纯净 —— 用文字与图片记录生活与创意 feedId:172229198194932736+userId:166528077360436224</description><link>https://blog.nywerya.xyz/</link><language>zh</language><item><title>btop — 装完系统第一个 apt install 的终端资源监视器</title><link>https://blog.nywerya.xyz/blog/btop-system-monitor/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/btop-system-monitor/</guid><description>btop 是 htop 的终极进化版，C++ 重写的 TUI 系统监视器。实时展示 CPU/内存/磁盘/网络图表，鼠标可点，内置多套主题。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/btop-system-monitor/&quot;&gt;https://blog.nywerya.xyz/blog/btop-system-monitor/&lt;/a&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;img src=&quot;https://photo.nywerya.xyz/Obsidian/%E5%8F%91%E5%B8%83/%E6%B3%A8%E9%87%8A/btop-cover.png&quot; alt=&quot;封面&quot;&gt;&lt;/p&gt;
&lt;h1&gt;btop — 终端里的资源监视器&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;btop&lt;/code&gt; 是一个用 C++ 重写的系统资源监视器，可以看作 &lt;code&gt;htop&lt;/code&gt; 的终极进化版。在终端里实时展示 CPU、内存、磁盘、网络、进程信息，界面精致、高度可定制。&lt;/p&gt;
&lt;h2&gt;和 htop / top 的区别&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;特性&lt;/th&gt;
&lt;th&gt;top&lt;/th&gt;
&lt;th&gt;htop&lt;/th&gt;
&lt;th&gt;btop&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;界面&lt;/td&gt;
&lt;td&gt;纯文本&lt;/td&gt;
&lt;td&gt;彩色文本&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;全图形化 TUI&lt;/strong&gt;（鼠标可点）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU 显示&lt;/td&gt;
&lt;td&gt;数字&lt;/td&gt;
&lt;td&gt;柱状条&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;每个核心独立折线图 + 颜色主题&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;内存/磁盘&lt;/td&gt;
&lt;td&gt;数字&lt;/td&gt;
&lt;td&gt;柱状条&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;柱状图 + 百分比 + 详细信息&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;网络&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅ 实时上下行速率图&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;进程树&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;鼠标支持&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU 监控&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅ 部分支持&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;主题&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;✅ 内置多套主题&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;安装&lt;/h2&gt;
&lt;h3&gt;Debian / Ubuntu&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install btop
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Arch Linux&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo pacman -S btop
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Fedora&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo dnf install btop
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;macOS&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install btop
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;从源码编译&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/aristocratos/btop.git
cd btop
make
sudo make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;基础操作&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 启动
btop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入后直接用鼠标点击即可切换菜单，也可以用键盘：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;按键&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt; / &lt;code&gt;2&lt;/code&gt; / &lt;code&gt;3&lt;/code&gt; / &lt;code&gt;4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;切换到 CPU / 内存 / 磁盘 / 网络 详细视图&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;f&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;进程过滤器（输入关键词筛选）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;t&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;切换进程树模式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;m&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;切换菜单栏显示&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Esc&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;退出当前菜单 / 全局&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;q&lt;/code&gt; / &lt;code&gt;Ctrl+C&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;退出 btop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;切换进程排序方式&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;+&lt;/code&gt; / &lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;调整刷新间隔&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;配置主题&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 配置文件位置
~/.config/btop/btop.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预设主题切换：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;btop --theme gruvbox_dark
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;btop --theme nord
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;btop --theme monokai
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在配置文件中改主题：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;# ~/.config/btop/btop.conf
color_theme = &amp;quot;gruvbox_dark&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其他常用配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;# 刷新间隔（毫秒）
update_ms = 1000

# 显示温度
show_cpu_temp = True

# 图表背景
theme_background = False

# 网络图表自动缩放
net_auto = True
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;实用场景&lt;/h2&gt;
&lt;h3&gt;排查 CPU 瓶颈&lt;/h3&gt;
&lt;p&gt;编译大项目时开一个 btop，每个核心的折线图一眼看出是不是有单核瓶颈：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 终端 1：编译
make -j$(nproc)

# 终端 2：监控
btop
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;监控磁盘 IO&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 按 3 切换到磁盘视图
# 能看出哪个进程在大量读写
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;排查内存泄漏&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 按 m 打开菜单 → 进程按内存排序
# 看哪个进程内存持续增长
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;网络流量监控&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 按 4 切换到网络视图
# 显示每个网络接口的实时收发速率
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;远程服务器常驻&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# SSH 到服务器后直接跑
ssh user@server -t btop

# 或者在 tmux 里常驻一个面板
tmux split-window -h btop
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;进阶技巧&lt;/h2&gt;
&lt;h3&gt;快捷键绑定别名&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# ~/.bashrc 或 ~/.zshrc
alias top=&amp;#39;btop&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从此敲 &lt;code&gt;top&lt;/code&gt; 就是 btop。&lt;/p&gt;
&lt;h3&gt;进程信号&lt;/h3&gt;
&lt;p&gt;btop 里选中进程后可以直接发送信号（&lt;code&gt;T&lt;/code&gt; 键），支持 &lt;code&gt;SIGTERM&lt;/code&gt;、&lt;code&gt;SIGKILL&lt;/code&gt;、&lt;code&gt;SIGSTOP&lt;/code&gt; 等，不用再开一个终端 &lt;code&gt;kill&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;预设布局&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 启动时直接进入指定视图
btop --preset cpu      # 只看 CPU
btop --preset net      # 只看网络
btop --preset proc     # 只看进程
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;btop 在我所有 Linux 机器上都是装完系统第一个 &lt;code&gt;apt install&lt;/code&gt; 的东西。相比 htop 的优势不只是好看——网络监控和磁盘 IO 面板在很多排查场景里能省掉另外开 &lt;code&gt;iftop&lt;/code&gt; / &lt;code&gt;iotop&lt;/code&gt; 的步骤。而且鼠标可点，对不熟命令行的人也更友好。&lt;/p&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Sat, 13 Jun 2026 13:00:00 GMT</pubDate></item><item><title>N2N 虚拟局域网搭建 — 一键脚本 + 联机游戏实战</title><link>https://blog.nywerya.xyz/blog/n2n-virtual-lan-setup/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/n2n-virtual-lan-setup/</guid><description>用 N2N 自建虚拟局域网，解决无法 IP 直连游戏的联机问题（以撒/饥荒等）。含一键部署脚本和服务端/客户端完整配置。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/n2n-virtual-lan-setup/&quot;&gt;https://blog.nywerya.xyz/blog/n2n-virtual-lan-setup/&lt;/a&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;img src=&quot;https://photo.nywerya.xyz/Obsidian/%E5%8F%91%E5%B8%83/%E6%B3%A8%E9%87%8A/N2N-cover.png&quot; alt=&quot;封面&quot;&gt;&lt;/p&gt;
&lt;h1&gt;N2N 虚拟局域网搭建&lt;/h1&gt;
&lt;h2&gt;为什么搭虚拟局域网&lt;/h2&gt;
&lt;p&gt;和朋友联机玩以撒的结合，被土豆服务器折磨到无言。以撒的联机不同于泰拉瑞亚与 MC，无法使用 IP 直连，内网穿透（frp）也无能为力——只能上虚拟局域网。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;：将不同局域网的用户通过中转节点（Supernode）形成一个模拟的局域网，功能等效于物理局域网，支持所有局域网联机游戏。以撒和饥荒这类游戏的联机方式虽然不能选&amp;quot;局域网联机&amp;quot;，但会自动找到最优网络路径，刚打通的虚拟局域网也在其中。&lt;/p&gt;
&lt;h2&gt;服务端&lt;/h2&gt;
&lt;p&gt;服务器操作系统：Ubuntu 24.04&lt;/p&gt;
&lt;h3&gt;安装 N2N&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 更新 apt
sudo apt update
apt install cmake

# 下载 v3.1.1 稳定版
cd /tmp
rm -rf n2n
git clone https://github.com/ntop/n2n.git
cd n2n
git checkout 3.1.1

# 编译安装
mkdir build &amp;amp;&amp;amp; cd build
cmake ..
make -j4
sudo make install

# 验证
/usr/local/bin/edge --version
/usr/local/bin/supernode --version

# 创建符号链接
sudo ln -sf /usr/local/bin/edge /usr/bin/edge
sudo ln -sf /usr/local/bin/supernode /usr/bin/supernode
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;配置 Supernode（超级节点）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 创建配置文件
sudo sh -c &amp;#39;cat &amp;gt; /etc/n2n/supernode.conf &amp;lt;&amp;lt; &amp;quot;EOF&amp;quot;
-p=7654
-m=5645
-f=/etc/n2n/communities.list
-l=/var/log/n2n/supernode.log
-r
EOF&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建 systemd 服务：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo sh -c &amp;#39;cat &amp;gt; /etc/systemd/system/n2n-supernode.service &amp;lt;&amp;lt; &amp;quot;EOF&amp;quot;
[Unit]
Description=N2N Supernode v3.1.1
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/sbin/supernode -p 7654 -f -i 203.0.113.1
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target
EOF&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl daemon-reload
sudo systemctl start n2n-supernode
sudo systemctl enable n2n-supernode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检查运行状态：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl status n2n-supernode --no-pager
sudo netstat -tulnp | grep 7654
ps aux | grep supernode
sudo journalctl -u n2n-supernode -f --no-pager
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;服务器配置 Edge（服务器也要加入网络）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo sh -c &amp;#39;cat &amp;gt; /etc/systemd/system/n2n-edge.service &amp;lt;&amp;lt; &amp;quot;EOF&amp;quot;
[Unit]
Description=N2N Edge Client v3.1.1
After=network.target n2n-supernode.service
Wants=n2n-supernode.service

[Service]
Type=simple
ExecStart=/usr/local/sbin/edge -d n2n0 -a 10.0.0.1 -c mycommunity -k mysecretkey -l 127.0.0.1:7654 -f
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target
EOF&amp;#39;

sudo systemctl daemon-reload
sudo systemctl start n2n-edge
sudo systemctl enable n2n-edge
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;检查与调试&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 检查 edge 进程
ps aux | grep edge

# 查看网卡
ip link show | grep n2n

# 监听端口
sudo tcpdump -i any udp port 7654 -vv -n

# 查看日志
sudo journalctl -u n2n-supernode -n 50
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;客户端&lt;/h2&gt;
&lt;p&gt;使用 &lt;a href=&quot;https://bugxia.com/357.html&quot;&gt;EasyN2N&lt;/a&gt; 软件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在自定义服务器内填写服务器 IP 和端口&lt;/li&gt;
&lt;li&gt;填写小组名称（与服务器 &lt;code&gt;-c&lt;/code&gt; 参数一致）&lt;/li&gt;
&lt;li&gt;在软件设置中添加密钥附加参数：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;-k mysecretkey
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;连接即可&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;直连命令（不用 EasyN2N）&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 基本直连
edge -a 10.0.0.10 -c mycommunity -k mysecretkey -l 203.0.113.1:7654 -d n2n1
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 如果使用端口映射
edge -a 10.0.0.10 -c mycommunity -k mysecretkey -l example.com:54332 -d n2n1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;一键安装脚本&lt;/h2&gt;
&lt;p&gt;需要一台云服务器（2c2g 够用，1c0.5g 也能跑，重要的是网速≥1Mbps）。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#!/bin/bash
# feat: 一键部署 n2n 局域网
# 系统要求: Ubuntu 24.04
# 网络要求: 能使用 apt 安装软件包, 能 git 抓取 github 文件

LOG_FILE=&amp;quot;/var/log/n2n-deploy.log&amp;quot;
N2N_VERSION=&amp;quot;3.1.1&amp;quot;
SUPERNODE_PORT=7654
EDGE_IP=&amp;quot;10.0.0.1&amp;quot;
COMMUNITY=&amp;quot;mycommunity&amp;quot;
SECRET_KEY=&amp;quot;mysecretkey&amp;quot;  # 请换成自己的密钥！

# 停止已有服务
sudo systemctl stop n2n-supernode n2n-edge 2&amp;gt;/dev/null

# 日志函数
log() {
    echo &amp;quot;[$(date +&amp;#39;%Y-%m-%d %H:%M:%S&amp;#39;)] $*&amp;quot; | tee -a &amp;quot;$LOG_FILE&amp;quot;
}

error_exit() {
    log &amp;quot;错误: $*&amp;quot;
    exit 1
}

# 检查是否为 root
if [ $EUID -ne 0 ]; then
    error_exit &amp;quot;此脚本必须以 root 权限运行！请使用 sudo。&amp;quot;
fi

log &amp;quot;开始部署 n2n v$N2N_VERSION...&amp;quot;

# 更新 apt 并安装依赖
log &amp;quot;更新 apt 并安装必要工具...&amp;quot;
apt update &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;apt update 失败&amp;quot;
apt install -y cmake git build-essential &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;安装依赖失败&amp;quot;

# 安装 n2n
echo &amp;quot;是否需要安装 n2n? (y/n)&amp;quot;
read choice
if [ &amp;quot;$choice&amp;quot; = &amp;quot;y&amp;quot; ] || [ &amp;quot;$choice&amp;quot; = &amp;quot;Y&amp;quot; ]; then
  cd /tmp || error_exit &amp;quot;无法进入 /tmp 目录&amp;quot;
  log &amp;quot;清理旧的 n2n 目录...&amp;quot;
  rm -rf n2n

  log &amp;quot;克隆 n2n 仓库...&amp;quot;
  git clone https://github.com/ntop/n2n.git &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;git clone 失败&amp;quot;
  cd n2n || error_exit &amp;quot;无法进入 n2n 目录&amp;quot;

  log &amp;quot;切换到版本 $N2N_VERSION...&amp;quot;
  git checkout &amp;quot;$N2N_VERSION&amp;quot; &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;无法切换到 tag $N2N_VERSION&amp;quot;

  log &amp;quot;配置并编译 n2n...&amp;quot;
  mkdir -p build &amp;amp;&amp;amp; cd build || error_exit &amp;quot;无法创建 build 目录&amp;quot;
  cmake .. &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;cmake 配置失败&amp;quot;
  make -j$(nproc) &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;编译失败&amp;quot;

  log &amp;quot;安装 n2n...&amp;quot;
  make install &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;安装失败&amp;quot;

  ln -sf /usr/local/bin/edge /usr/bin/edge
  ln -sf /usr/local/bin/supernode /usr/bin/supernode
fi

# 创建配置目录
mkdir -p /etc/n2n /var/log/n2n

# 写入 supernode 配置
log &amp;quot;写入 supernode 配置...&amp;quot;
cat &amp;gt; /etc/n2n/supernode.conf &amp;lt;&amp;lt;EOF || error_exit &amp;quot;写入 supernode.conf 失败&amp;quot;
-p=$SUPERNODE_PORT
-m=5645
-f=/etc/n2n/communities.list
-l=/var/log/n2n/supernode.log
-r
EOF

# 写入 supernode systemd 服务
log &amp;quot;创建 supernode systemd 服务...&amp;quot;
cat &amp;gt; /etc/systemd/system/n2n-supernode.service &amp;lt;&amp;lt;EOF || error_exit &amp;quot;创建 supernode service 失败&amp;quot;
[Unit]
Description=N2N Supernode v$N2N_VERSION
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/sbin/supernode -p $SUPERNODE_PORT -f
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target
EOF

# 写入 edge systemd 服务
log &amp;quot;创建 edge systemd 服务...&amp;quot;
cat &amp;gt; /etc/systemd/system/n2n-edge.service &amp;lt;&amp;lt;EOF || error_exit &amp;quot;创建 edge service 失败&amp;quot;
[Unit]
Description=N2N Edge Client v$N2N_VERSION
After=network.target n2n-supernode.service
Wants=n2n-supernode.service

[Service]
Type=simple
ExecStart=/usr/local/sbin/edge -a $EDGE_IP -c $COMMUNITY -l 127.0.0.1:$SUPERNODE_PORT -d n2n0 -k $SECRET_KEY -f
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target
EOF

# 重载 systemd
log &amp;quot;重载 systemd 配置...&amp;quot;
systemctl daemon-reload &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;systemctl daemon-reload 失败&amp;quot;

# 启动服务
log &amp;quot;启动 n2n-supernode 服务...&amp;quot;
systemctl start n2n-supernode &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;启动 supernode 失败&amp;quot;

log &amp;quot;启动 n2n-edge 服务...&amp;quot;
systemctl start n2n-edge &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;启动 edge 失败&amp;quot;

# 启用开机自启
log &amp;quot;启用开机自启动...&amp;quot;
systemctl enable n2n-supernode n2n-edge &amp;gt;&amp;gt; &amp;quot;$LOG_FILE&amp;quot; 2&amp;gt;&amp;amp;1 || error_exit &amp;quot;启用自启动失败&amp;quot;

log &amp;quot;n2n 部署完成！supernode 运行在端口 $SUPERNODE_PORT，edge IP: $EDGE_IP&amp;quot;
echo &amp;quot;社区名: $COMMUNITY&amp;quot;
echo &amp;quot;密钥: $SECRET_KEY （请妥善保存！）&amp;quot;
echo &amp;quot;请使用服务器公网 IP + 7654 端口连接，加上社区和密钥参数&amp;quot;
echo &amp;quot;部署成功后无需再次运行此脚本！&amp;quot;
echo &amp;quot;日志位置: $LOG_FILE&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;常用管理命令&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 停止服务
sudo systemctl stop n2n-supernode n2n-edge

# 重启服务
sudo systemctl restart n2n-supernode n2n-edge

# 查看状态
sudo systemctl status n2n-supernode --no-pager
sudo systemctl status n2n-edge --no-pager

# 防火墙放行
sudo ufw allow 7654/udp
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;服务端&lt;/strong&gt;：Supernode 作为中转节点，Edge 让服务器自己也加入网络&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;客户端&lt;/strong&gt;：EasyN2N 图形化连接，填 IP + 社区名 + 密钥即可&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适用游戏&lt;/strong&gt;：以撒的结合、饥荒等不支持 IP 直连的局域网游戏&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;成本&lt;/strong&gt;：一台最低配云服务器就行，重要的是带宽&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Fri, 12 Jun 2026 13:20:00 GMT</pubDate></item><item><title>one-api 中转站搭建教程 — 聚合 API 赚差价</title><link>https://blog.nywerya.xyz/blog/one-api-proxy-setup/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/one-api-proxy-setup/</guid><description>用 one-api 搭建自己的 AI API 中转站，聚合 DeepSeek/OpenAI/Claude，统一管理 Key，按量计费赚取差价。附 Cloudflare Workers 零成本起步方案。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/one-api-proxy-setup/&quot;&gt;https://blog.nywerya.xyz/blog/one-api-proxy-setup/&lt;/a&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;img src=&quot;https://photo.nywerya.xyz/Obsidian/%E5%8F%91%E5%B8%83/%E6%B3%A8%E9%87%8A/OneAPI-cover.png&quot; alt=&quot;封面&quot;&gt;&lt;/p&gt;
&lt;h1&gt;one-api 中转站搭建教程&lt;/h1&gt;
&lt;p&gt;API 中转站的核心思路：&lt;strong&gt;你有上游 API Key（DeepSeek/OpenAI/Claude），下游用户通过你中转调用，你赚差价&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;开源项目 &lt;a href=&quot;https://github.com/songquanpeng/one-api&quot;&gt;one-api&lt;/a&gt; 帮你搞定：聚合多平台 API → 统一管理 → 生成子 Key → 按量计费。&lt;/p&gt;
&lt;h2&gt;一、你需要什么&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;资源&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;一台服务器&lt;/td&gt;
&lt;td&gt;轻量云 ¥50-100/月（阿里云/腾讯云），1核2G 够用&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;一个域名&lt;/td&gt;
&lt;td&gt;用于中转站访问（api.你的域名.com），可选&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;上游 API Key&lt;/td&gt;
&lt;td&gt;DeepSeek 官网注册送额度，或找上游渠道&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docker 环境&lt;/td&gt;
&lt;td&gt;服务器上装好 Docker + docker-compose&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;二、服务器初始化&lt;/h2&gt;
&lt;p&gt;SSH 登录后：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 更新系统
sudo apt update &amp;amp;&amp;amp; sudo apt upgrade -y

# 安装 Docker
curl -fsSL https://get.docker.com | sudo bash
sudo usermod -aG docker $USER

# 安装 docker-compose
sudo apt install docker-compose -y

# 防火墙只开 80/443/22
sudo ufw allow 22
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable

# 重新登录使 docker 权限生效
exit
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;三、部署 one-api&lt;/h2&gt;
&lt;h3&gt;3.1 Docker 一键启动&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker run -d \
  --name one-api \
  --restart always \
  -p 3000:3000 \
  -v /opt/one-api/data:/data \
  -e TZ=Asia/Shanghai \
  justsong/one-api
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker ps | grep one-api
curl http://localhost:3000
# 应返回 one-api 的 HTML 页面
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 配置 Nginx 反代 + SSL（如果有域名）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/nginx/sites-available/api
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    listen 80;
    server_name api.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ln -s /etc/nginx/sites-available/api /etc/nginx/sites-enabled/
sudo nginx -t &amp;amp;&amp;amp; sudo systemctl reload nginx
sudo certbot --nginx -d api.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.3 初始登录&lt;/h3&gt;
&lt;p&gt;浏览器打开 &lt;code&gt;http://你的服务器IP:3000&lt;/code&gt; 或 &lt;code&gt;https://api.example.com&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;默认账号：&lt;code&gt;root&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;默认密码：&lt;code&gt;123456&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;登录后第一件事：改密码！&lt;/strong&gt; 右上角头像 → 修改密码&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;四、配置渠道（接入上游 API）&lt;/h2&gt;
&lt;h3&gt;4.1 添加 DeepSeek 渠道&lt;/h3&gt;
&lt;p&gt;渠道 → 添加渠道：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;类型&lt;/td&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;名称&lt;/td&gt;
&lt;td&gt;DeepSeek&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分组&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模型&lt;/td&gt;
&lt;td&gt;&lt;code&gt;deepseek-chat&lt;/code&gt;, &lt;code&gt;deepseek-reasoner&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;密钥&lt;/td&gt;
&lt;td&gt;你的 DeepSeek API Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;代理&lt;/td&gt;
&lt;td&gt;留空（除非需要反代地址）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;4.2 添加 OpenAI 渠道（可选）&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;类型&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;名称&lt;/td&gt;
&lt;td&gt;OpenAI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分组&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模型&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-4o&lt;/code&gt;, &lt;code&gt;gpt-4o-mini&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;密钥&lt;/td&gt;
&lt;td&gt;你的 OpenAI API Key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;4.3 添加 Claude 渠道（可选，需反代）&lt;/h3&gt;
&lt;p&gt;Claude API 对国内 IP 不友好，需要反代。在代理栏填反代地址，或通过 Cloudflare Workers 中转（见第七节）。&lt;/p&gt;
&lt;h3&gt;4.4 添加自定义渠道（对接其他中转站）&lt;/h3&gt;
&lt;p&gt;如果你的上游也是中转站（兼容 OpenAI 格式）：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;字段&lt;/th&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;类型&lt;/td&gt;
&lt;td&gt;自定义渠道&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;名称&lt;/td&gt;
&lt;td&gt;上游中转&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;分组&lt;/td&gt;
&lt;td&gt;default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;模型&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-4o&lt;/code&gt;, &lt;code&gt;claude-sonnet-4-6&lt;/code&gt; 等&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;密钥&lt;/td&gt;
&lt;td&gt;上游给的 Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;代理&lt;/td&gt;
&lt;td&gt;上游中转站的 API 地址&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;五、定价策略：设置倍率&lt;/h2&gt;
&lt;h3&gt;5.1 模型倍率设置&lt;/h3&gt;
&lt;p&gt;模型 pricing → 编辑每个模型的倍率：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DeepSeek 官方价格：输入 ¥1 / 百万 token&lt;/li&gt;
&lt;li&gt;倍率设为 &lt;strong&gt;2.0&lt;/strong&gt;：用户付费 ¥2 / 百万 token&lt;/li&gt;
&lt;li&gt;你的毛利：¥1 / 百万 token（成本 ¥1，收 ¥2）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建议倍率：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模型&lt;/th&gt;
&lt;th&gt;官方价（约）&lt;/th&gt;
&lt;th&gt;建议倍率&lt;/th&gt;
&lt;th&gt;用户价（约）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;DeepSeek Chat&lt;/td&gt;
&lt;td&gt;¥1/M&lt;/td&gt;
&lt;td&gt;1.5-2.0&lt;/td&gt;
&lt;td&gt;¥1.5-2/M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek Reasoner&lt;/td&gt;
&lt;td&gt;¥4/M&lt;/td&gt;
&lt;td&gt;1.5-2.0&lt;/td&gt;
&lt;td&gt;¥6-8/M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-4o&lt;/td&gt;
&lt;td&gt;¥35/M&lt;/td&gt;
&lt;td&gt;1.3-1.5&lt;/td&gt;
&lt;td&gt;¥45-52/M&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet&lt;/td&gt;
&lt;td&gt;¥45/M&lt;/td&gt;
&lt;td&gt;1.3-1.5&lt;/td&gt;
&lt;td&gt;¥58-67/M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;低价模型用高倍率&lt;/strong&gt;：DeepSeek 本身便宜，用户对 ¥1 和 ¥2 不敏感，2 倍就是 100% 毛利&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;高价模型用低倍率&lt;/strong&gt;：GPT/Claude 单价高，倍率高了用户会被吓跑&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;5.2 用户分组（差异化定价）&lt;/h3&gt;
&lt;p&gt;可以为不同用户设置不同分组，每个分组对应不同倍率：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;default&lt;/code&gt; 分组：标准价格（倍率 2.0）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vip&lt;/code&gt; 分组：大客户优惠（倍率 1.5）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;trial&lt;/code&gt; 分组：体验组（倍率 1.0，不赚钱，纯引流）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;六、生成用户 Key&lt;/h2&gt;
&lt;p&gt;用户 → 添加用户 → 生成 Key：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;填写用户名（客户昵称）&lt;/li&gt;
&lt;li&gt;设置额度（如 ¥50）&lt;/li&gt;
&lt;li&gt;选择分组（default / vip）&lt;/li&gt;
&lt;li&gt;生成 → 复制 Key 发给客户&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;客户拿到 Key 后，在 Claude Code / Cursor 等工具里配置 API 地址和 Key 就能用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;API 地址&lt;/strong&gt;：&lt;code&gt;https://api.example.com/v1&lt;/code&gt;（兼容 OpenAI 格式）&lt;/p&gt;
&lt;h2&gt;七、零成本方案：Cloudflare Workers&lt;/h2&gt;
&lt;p&gt;如果暂时不想买服务器，用 Cloudflare Workers 免费搭反代：&lt;/p&gt;
&lt;h3&gt;7.1 创建 Worker&lt;/h3&gt;
&lt;p&gt;登录 Cloudflare → Workers &amp;amp; Pages → 创建 → 编辑代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;export default {
  async fetch(request) {
    const url = new URL(request.url);
    
    // 反代到 DeepSeek API
    const targetUrl = &amp;quot;https://api.deepseek.com&amp;quot; + url.pathname;
    
    // 验证用户 Key（简单鉴权）
    const userKey = request.headers.get(&amp;quot;Authorization&amp;quot;);
    const VALID_KEYS = [&amp;quot;sk-user1-xxx&amp;quot;, &amp;quot;sk-user2-yyy&amp;quot;];
    
    if (!userKey || !VALID_KEYS.includes(userKey.replace(&amp;quot;Bearer &amp;quot;, &amp;quot;&amp;quot;))) {
      return new Response(&amp;quot;Unauthorized&amp;quot;, { status: 401 });
    }
    
    // 替换为你的 DeepSeek Key
    const newHeaders = new Headers(request.headers);
    newHeaders.set(&amp;quot;Authorization&amp;quot;, &amp;quot;Bearer sk-your-deepseek-key&amp;quot;);
    
    return fetch(targetUrl, {
      method: request.method,
      headers: newHeaders,
      body: request.body
    });
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;7.2 绑定自定义域名&lt;/h3&gt;
&lt;p&gt;Worker → 设置 → 触发器 → 自定义域 → 添加 &lt;code&gt;api.example.com&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;7.3 局限性&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Workers 免费版每天 10 万次请求，个人用够&lt;/li&gt;
&lt;li&gt;只能反代一个上游，不能像 one-api 聚合多平台&lt;/li&gt;
&lt;li&gt;没有额度管理系统，需要手动计费&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适合起步验证需求，确认有客户了再升级到 one-api&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;八、运维要点&lt;/h2&gt;
&lt;h3&gt;8.1 监控余额&lt;/h3&gt;
&lt;p&gt;one-api 后台 → 渠道 → 点击「测试」检查上游 Key 是否有效。建议每周检查一次余额。&lt;/p&gt;
&lt;h3&gt;8.2 备份数据&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# one-api 数据在 /opt/one-api/data/
tar -czf one-api-backup-$(date +%Y%m%d).tar.gz /opt/one-api/data/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;8.3 防止滥用&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;单用户设置额度上限（如 ¥200）&lt;/li&gt;
&lt;li&gt;发现异常调用立刻禁用 Key&lt;/li&gt;
&lt;li&gt;在用户协议里写明「禁止自动化批量调用」&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;8.4 多账号分散风险&lt;/h3&gt;
&lt;p&gt;DeepSeek 一个账号免费额度有限。如果客户量大：多注册几个 DeepSeek 账号，在 one-api 里添加多个同类型渠道，自动负载均衡。&lt;/p&gt;
&lt;h2&gt;九、成本收益估算&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;项目&lt;/th&gt;
&lt;th&gt;月成本&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;轻量云服务器&lt;/td&gt;
&lt;td&gt;¥50-100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;域名（年付）&lt;/td&gt;
&lt;td&gt;¥20-50/年 ≈ ¥2-4/月&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;上游 API 成本&lt;/td&gt;
&lt;td&gt;看客户用量&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;假设有 10 个稳定客户，每人月均消耗 ¥50 的 API：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;月流水：10 × ¥50 = ¥500&lt;/li&gt;
&lt;li&gt;上游成本（倍率 2.0）：¥250&lt;/li&gt;
&lt;li&gt;毛利：¥500 - ¥250 - ¥100（服务器）= &lt;strong&gt;¥150/月&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;客户越多，固定成本摊得越薄。30 个客户时毛利超过 ¥500/月。&lt;/p&gt;
&lt;h2&gt;十、升级路径&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;起步&lt;/strong&gt;：Cloudflare Worker 免费方案，验证有客户愿意付费&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;有稳定 5-10 个客户后&lt;/strong&gt;：上服务器 + one-api，正规化管理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规模化&lt;/strong&gt;：多上游 → 自动负载均衡 → 监控告警 → 自动充值&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;关键是&lt;strong&gt;先验证需求再投入&lt;/strong&gt;，不要服务器买了一堆没客户。&lt;/p&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Fri, 12 Jun 2026 13:20:00 GMT</pubDate></item><item><title>自建图床：Syncthing + Nginx 零成本图片 CDN</title><link>https://blog.nywerya.xyz/blog/self-host-image-cdn/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/self-host-image-cdn/</guid><description>利用 Syncthing 实时同步 + Nginx 静态服务，把已有服务器变成个人图片 CDN。Obsidian 写作流程零改变，数据全在自己手里。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/self-host-image-cdn/&quot;&gt;https://blog.nywerya.xyz/blog/self-host-image-cdn/&lt;/a&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;img src=&quot;https://photo.nywerya.xyz/Obsidian/%E5%8F%91%E5%B8%83/%E6%B3%A8%E9%87%8A/%E8%87%AA%E5%BB%BA%E5%9B%BE%E5%BA%8A-cover.png&quot; alt=&quot;封面&quot;&gt;&lt;/p&gt;
&lt;h1&gt;自建图床：Syncthing + Nginx&lt;/h1&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;本地 Vault 图片  ──Syncthing──▶  服务器 /var/www/photo/  ──Nginx──▶  photo.example.com/xxx.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本地写完笔记直接丢图到 vault 里，Syncthing 自动推到服务器，Nginx 提供公网访问。写作流程零改变。&lt;/p&gt;
&lt;p&gt;重点：保留了 vault 的文件结构，只需要添加链接头即可访问。&lt;/p&gt;
&lt;h2&gt;第一步：服务器准备&lt;/h2&gt;
&lt;h3&gt;1.1 创建图片目录 + 占位页&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo mkdir -p /var/www/photo
sudo chown $USER:$USER /var/www/photo
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 占位页面，避免空目录 403
cat &amp;gt; /var/www/photo/index.html &amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&amp;quot;zh&amp;quot;&amp;gt;
&amp;lt;head&amp;gt;&amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;&amp;lt;title&amp;gt;Photo&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;&amp;lt;h1&amp;gt;Photo CDN&amp;lt;/h1&amp;gt;&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.2 配置 Nginx（先 80，不要写 SSL）&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/nginx/sites-available/photo
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    listen 80;
    server_name photo.example.com;

    root /var/www/photo;
    index index.html;

    # 允许跨域（博客站点引用图片）
    add_header Access-Control-Allow-Origin &amp;quot;*&amp;quot;;

    # 图片缓存 1 年
    location ~* \.(png|jpg|jpeg|webp|gif|svg)$ {
        expires 1y;
        add_header Cache-Control &amp;quot;public, immutable&amp;quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ln -s /etc/nginx/sites-available/photo /etc/nginx/sites-enabled/
sudo nginx -t &amp;amp;&amp;amp; sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.3 DNS + Certbot 自动升级 HTTPS&lt;/h3&gt;
&lt;p&gt;DNS 先加 A 记录：&lt;code&gt;photo → 203.0.113.1&lt;/code&gt;，生效后：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo certbot --nginx -d photo.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certbot 会自动：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;申请证书&lt;/li&gt;
&lt;li&gt;把 &lt;code&gt;listen 80&lt;/code&gt; 改为 &lt;code&gt;listen 443 ssl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;补上 &lt;code&gt;ssl_certificate&lt;/code&gt; / &lt;code&gt;ssl_certificate_key&lt;/code&gt; 路径&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;关键&lt;/strong&gt;：不要手写 SSL 配置——证书还不存在时 nginx 配置校验会失败，certbot 也无法运行。先 &lt;code&gt;listen 80&lt;/code&gt;，让 certbot 自己升级。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;第二步：Syncthing 同步&lt;/h2&gt;
&lt;h3&gt;2.1 添加共享目录&lt;/h3&gt;
&lt;p&gt;在服务器 Syncthing 的 GUI 中：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;添加文件夹&lt;/strong&gt; → 路径填 &lt;code&gt;/var/www/photo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;文件夹 ID 随意，如 &lt;code&gt;photo-cdn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在「共享」选项卡，选择你的本地电脑&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2.2 本地电脑接受&lt;/h3&gt;
&lt;p&gt;本地 Syncthing 收到共享请求后，路径选 vault 的图片目录，接受。&lt;/p&gt;
&lt;h3&gt;2.3 筛选同步（.stignore）&lt;/h3&gt;
&lt;p&gt;在服务器 &lt;code&gt;/var/www/photo/.stignore&lt;/code&gt; 里创建：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 不同步 Syncthing 冲突文件
.sync-conflict-*

# 不同步临时文件
*.tmp
*.temp
~$*

# 只同步图片
!*.png
!*.jpg
!*.jpeg
!*.webp
!*.gif
!*.svg
!*.ico

# 默认拒绝
*
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;.stignore&lt;/code&gt; 的规则：&lt;code&gt;!&lt;/code&gt; 是允许，&lt;code&gt;*&lt;/code&gt; 是拒绝所有。上面意思是&amp;quot;只同步图片文件&amp;quot;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;第三步：与 Obsidian 写作流程对接&lt;/h2&gt;
&lt;h3&gt;3.1 本地写文章时&lt;/h3&gt;
&lt;p&gt;仍然用 Obsidian 的 wiki 链接：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;![[笔记/工具软件/CopyQ/data/注释/screenshot.png]]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3.2 发布时替换链接&lt;/h3&gt;
&lt;p&gt;发布到博客时，将 &lt;code&gt;![[...]]&lt;/code&gt; 替换为 Markdown 标准格式：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;![screenshot](https://photo.example.com/笔记/CopyQ/data/注释/screenshot.png)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;转换规则：去掉 &lt;code&gt;[[&lt;/code&gt;-&lt;code&gt;]]&lt;/code&gt;，加上 &lt;code&gt;https://photo.example.com/&lt;/code&gt; 前缀。&lt;/p&gt;
&lt;h2&gt;第四步：验证&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 服务器放一张测试图
touch /var/www/photo/test.png
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 公网可达性
curl -s -o /dev/null -w &amp;quot;%{http_code}&amp;quot; https://photo.example.com/test.png
# 应返回 200
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 缓存头
curl -I https://photo.example.com/test.png 2&amp;gt;/dev/null | grep -i cache
# Cache-Control: public, immutable
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;优缺点&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;维度&lt;/th&gt;
&lt;th&gt;评价&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;成本&lt;/td&gt;
&lt;td&gt;✅ 用已有服务器，零额外支出&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;速度&lt;/td&gt;
&lt;td&gt;⚠️ 家用服务器上行带宽有限（10-30Mbps），大图加载慢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;维护&lt;/td&gt;
&lt;td&gt;⚠️ 需要维护 Nginx + Syncthing + SSL 证书&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CDN&lt;/td&gt;
&lt;td&gt;❌ 无 CDN，全国/全球访问不如云对象存储&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;写作体验&lt;/td&gt;
&lt;td&gt;✅ 零改变——扔图进 vault，自动同步&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据安全&lt;/td&gt;
&lt;td&gt;✅ 数据全在手里，不怕图床跑路&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;如果后续图片多了、访问慢了，可以升级到 Cloudflare R2（配合 PicGo），Syncthing + Nginx 作为起步方案够用。&lt;/p&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Fri, 12 Jun 2026 12:30:00 GMT</pubDate></item><item><title>Umami 网站统计部署指南 — 15 分钟自建隐私友好分析</title><link>https://blog.nywerya.xyz/blog/self-host-umami-analytics/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/self-host-umami-analytics/</guid><description>不想把访客数据送给 Google？用 Umami 在自有服务器上部署网站分析，Docker 一把梭，Nginx 反代加 SSL，配合 RyuChan 模板展示全站 PV/UV 和单篇阅读量。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/self-host-umami-analytics/&quot;&gt;https://blog.nywerya.xyz/blog/self-host-umami-analytics/&lt;/a&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;img src=&quot;https://photo.nywerya.xyz/Obsidian/%E5%8F%91%E5%B8%83/%E6%B3%A8%E9%87%8A/Umami-cover.png&quot; alt=&quot;封面&quot;&gt;&lt;/p&gt;
&lt;h1&gt;Umami 自建网站统计&lt;/h1&gt;
&lt;h2&gt;这是什么&lt;/h2&gt;
&lt;p&gt;Umami 是开源的网站访问统计工具，Google Analytics 的轻量替代品。部署在自己的服务器上，数据归你，不会追踪用户隐私。&lt;/p&gt;
&lt;p&gt;对比 GA4：Umami 部署 5 分钟，打开 dashboard 就能看懂，不用考 GA 分析师证。&lt;/p&gt;
&lt;h2&gt;效果展示&lt;/h2&gt;
&lt;p&gt;博客底部的&amp;quot;阅读量&amp;quot;就是 Umami 提供的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;┌──────────────────────────────────┐
│ 📊 站点统计                        │
│ 👁 总 PV：12,345                  │
│ 👥 总 UV：4,567                   │
│ 📈 今日 PV：123                   │
└──────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个文章页面显示单篇浏览量，ProfileBar 展示全站实时统计。&lt;/p&gt;
&lt;h2&gt;部署架构&lt;/h2&gt;
&lt;p&gt;Umami 是 Node.js 应用，需要 Postgres 或 MySQL 数据库。用 Docker Compose 一把梭：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;blog.example.com  ──→  Cloudflare Pages (Astro 静态页面)
                          │
                          ├─ &amp;lt;script&amp;gt; 加载 umami.example.com/script.js
                          │
umami.example.com  ──→  云服务器 (203.0.113.1)
                          │
                          ├─ Nginx (反代 + SSL)
                          │   └─ proxy_pass → localhost:3000
                          │
                          └─ Docker Compose
                              ├─ umami (Node.js, port 3000)
                              └─ postgres (port 5432)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第一步：Docker Compose 部署 Umami&lt;/h2&gt;
&lt;p&gt;在服务器上找一个目录，比如 &lt;code&gt;~/umami/&lt;/code&gt;，创建 &lt;code&gt;docker-compose.yml&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;version: &amp;#39;3&amp;#39;
services:
  umami:
    image: ghcr.nju.edu.cn/umami-software/umami:postgresql-latest
    ports:
      - &amp;quot;127.0.0.1:3000:3000&amp;quot;
    environment:
      DATABASE_URL: postgresql://umami:example-db-password@db:5432/umami
      DATABASE_TYPE: postgresql
      APP_SECRET: example-app-secret-replace-with-openssl-rand
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: umami
      POSTGRES_USER: umami
      POSTGRES_PASSWORD: example-db-password
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    healthcheck:
      test: [&amp;quot;CMD-SHELL&amp;quot;, &amp;quot;pg_isready -U umami&amp;quot;]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数据库密码和 APP_SECRET 换成你自己的。APP_SECRET 跑个 &lt;code&gt;openssl rand -hex 32&lt;/code&gt; 生成。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 启动
docker-compose up -d

# 确认跑起来了
docker-compose ps
# 应该看到两个容器都是 Up 状态
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第二步：Nginx 反代 + SSL&lt;/h2&gt;
&lt;p&gt;Umami 跑在 &lt;code&gt;localhost:3000&lt;/code&gt;，用一个子域名反代出去。在 &lt;code&gt;/etc/nginx/sites-available/&lt;/code&gt; 中创建 &lt;code&gt;umami&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    listen 80;
    server_name umami.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;记得配置 DNS，然后启用并上 SSL。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不要直接用 &lt;code&gt;certbot --nginx&lt;/code&gt;&lt;/strong&gt; — 它有概率把 SSL 配置写进 &lt;code&gt;default&lt;/code&gt; 文件，导致 403。用更稳的 &lt;code&gt;certonly&lt;/code&gt; 方式：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 先启用 site（一定要在 certbot 之前做）
sudo ln -s /etc/nginx/sites-available/umami /etc/nginx/sites-enabled/

# 只申请证书，不动 nginx 配置
sudo certbot certonly --nginx -d umami.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;证书拿到后，补完整 SSL 配置：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    listen 80;
    server_name umami.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name umami.example.com;
    ssl_certificate /etc/letsencrypt/live/umami.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/umami.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nginx -t &amp;amp;&amp;amp; sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;为什么不直接用 &lt;code&gt;certbot --nginx&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;certbot --nginx&lt;/code&gt; 会自动修改 nginx 配置加 SSL，但它判断&amp;quot;改哪个文件&amp;quot;的逻辑有问题 — 如果 &lt;code&gt;default&lt;/code&gt; 里有 &lt;code&gt;listen 80&lt;/code&gt;，它可能把 SSL block 写进 &lt;code&gt;default&lt;/code&gt; 而不是你的独立配置文件。结果就是：443 请求命中 &lt;code&gt;default&lt;/code&gt; 的静态文件 &lt;code&gt;root&lt;/code&gt;，反代没生效，返回 403。&lt;/p&gt;
&lt;h2&gt;第三步：在 Umami 添加网站&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;浏览器打开 &lt;code&gt;https://umami.example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;用刚创建的管理员账号登录&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Settings → Websites → Add website&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Name 随便填，Domain 填你博客域名&lt;/li&gt;
&lt;li&gt;创建后拿到 &lt;strong&gt;Website ID&lt;/strong&gt;（一串 UUID）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;第四步：博客集成&lt;/h2&gt;
&lt;p&gt;在 &lt;code&gt;ryuchan.config.yaml&lt;/code&gt; 里填：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;umami:
  enable: true
  baseUrl: https://umami.example.com
  websiteId: your-website-id-here
  shareId: &amp;quot;&amp;quot;
  timezone: Asia/Shanghai
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;部署后 F12 → Network，应该能看到 &lt;code&gt;/script.js&lt;/code&gt; 请求。&lt;/p&gt;
&lt;h2&gt;第五步：开启分享&lt;/h2&gt;
&lt;p&gt;博客底部的 PV/UV 数字需要开&lt;strong&gt;分享功能&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Umami 后台 → &lt;strong&gt;Settings → Websites → 点你的网站&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Share → Enable share URL&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;填入密码，保存后得到分享 URL&lt;/li&gt;
&lt;li&gt;URL 中那段随机字符串就是 &lt;strong&gt;shareId&lt;/strong&gt;，填进 &lt;code&gt;ryuchan.config.yaml&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;原理&lt;/h3&gt;
&lt;p&gt;Umami Share API 不需要认证 token：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;请求 &lt;code&gt;/api/share/{shareId}&lt;/code&gt; 获取临时 token&lt;/li&gt;
&lt;li&gt;带 token 请求 &lt;code&gt;/api/websites/{websiteId}/stats&lt;/code&gt; 拉数据&lt;/li&gt;
&lt;li&gt;结果缓存 1 小时（localStorage）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;不想自建？用 Umami Cloud&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;打开 &lt;a href=&quot;https://cloud.umami.is&quot;&gt;cloud.umami.is&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;注册账号，添加网站，拿 Website ID&lt;/li&gt;
&lt;li&gt;baseUrl 填 &lt;code&gt;https://cloud.umami.is&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;免费版 3 个网站 + 无限事件，个人博客够用。&lt;/p&gt;
&lt;h2&gt;实操要点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;端口只绑 127.0.0.1&lt;/strong&gt;：Umami 容器别暴露到公网&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据库持久化&lt;/strong&gt;：&lt;code&gt;./pgdata&lt;/code&gt; 映射出来&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;首次访问直接注册&lt;/strong&gt;：打开网页创建管理员账号&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;定期备份&lt;/strong&gt;：&lt;code&gt;pg_dump&lt;/code&gt; 或直接备份 &lt;code&gt;./pgdata&lt;/code&gt; 目录&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;APP_SECRET 很重要&lt;/strong&gt;：session 加密密钥，丢了所有登录失效&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Fri, 12 Jun 2026 12:30:00 GMT</pubDate></item><item><title>Syncthing 部署与配置指南</title><link>https://blog.nywerya.xyz/blog/p2p-file-sync-setup/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/p2p-file-sync-setup/</guid><description>开源 P2P 文件同步工具 Syncthing 的完整部署教程，涵盖 Nginx 反代、踩坑实录与诊断命令。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/p2p-file-sync-setup/&quot;&gt;https://blog.nywerya.xyz/blog/p2p-file-sync-setup/&lt;/a&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;img src=&quot;https://photo.nywerya.xyz/Obsidian/%E5%8F%91%E5%B8%83/%E6%B3%A8%E9%87%8A/Syncthing-cover.png&quot; alt=&quot;封面&quot;&gt;&lt;/p&gt;
&lt;h1&gt;Syncthing 部署与配置指南&lt;/h1&gt;
&lt;p&gt;Syncthing 是一款开源的连续文件同步工具，P2P 架构，无需中心服务器。本文记录在云服务器上部署 Syncthing 并配置 Nginx 反代的全过程。&lt;/p&gt;
&lt;h2&gt;GUI 访问&lt;/h2&gt;
&lt;p&gt;GUI 默认监听 &lt;code&gt;127.0.0.1:8384&lt;/code&gt;（仅本地）。&lt;strong&gt;不要改为 &lt;code&gt;0.0.0.0&lt;/code&gt;&lt;/strong&gt;——走 Nginx 反代更安全。&lt;/p&gt;
&lt;h3&gt;临时：SSH 隧道&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ssh -L 8384:127.0.0.1:8384 root@服务器IP -N
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器 &lt;code&gt;http://127.0.0.1:8384&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;长期：Nginx 反代 + HTTPS&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/nginx/sites-available/syncthing
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    listen 443 ssl;
    server_name sync.example.com;

    ssl_certificate     /etc/letsencrypt/live/sync.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/sync.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8384;
        proxy_http_version 1.1;
        proxy_set_header Host 127.0.0.1:8384;
        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_set_header Upgrade $http_upgrade;
        proxy_set_header Connection &amp;quot;upgrade&amp;quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ln -s /etc/nginx/sites-available/syncthing /etc/nginx/sites-enabled/
sudo nginx -t &amp;amp;&amp;amp; sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DNS 添加 A 记录 &lt;code&gt;sync → 203.0.113.1&lt;/code&gt;，certbot 申请证书：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo certbot --nginx -d sync.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;踩坑：403 Forbidden&lt;/h3&gt;
&lt;p&gt;直接访问 &lt;code&gt;127.0.0.1:8384&lt;/code&gt; 返回 200，但通过域名返回 403。两个原因叠加：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;序号&lt;/th&gt;
&lt;th&gt;原因&lt;/th&gt;
&lt;th&gt;现象&lt;/th&gt;
&lt;th&gt;修复&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Certbot 生成的 server block 是&lt;strong&gt;静态文件服务器&lt;/strong&gt;（&lt;code&gt;root&lt;/code&gt; + &lt;code&gt;try_files&lt;/code&gt;），不是反代&lt;/td&gt;
&lt;td&gt;nginx 尝试从 &lt;code&gt;/var/www/html&lt;/code&gt; 找文件，找不到返回 403&lt;/td&gt;
&lt;td&gt;改为 &lt;code&gt;proxy_pass http://127.0.0.1:8384&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Syncthing 的 &lt;strong&gt;CSRF Host 校验&lt;/strong&gt;拒绝域名头&lt;/td&gt;
&lt;td&gt;改了反代后依然 403&lt;/td&gt;
&lt;td&gt;&lt;code&gt;proxy_set_header Host 127.0.0.1:8384&lt;/code&gt;（传 Syncthing 自己监听的地址）&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;关键：Host 头不能传 &lt;code&gt;$host&lt;/code&gt;（域名），必须传 &lt;code&gt;127.0.0.1:8384&lt;/code&gt;，Syncthing 才认。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;同步端口&lt;/h2&gt;
&lt;h3&gt;防火墙&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw allow 22000/tcp
sudo ufw allow 21027/udp
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;阿里云安全组&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;端口&lt;/th&gt;
&lt;th&gt;协议&lt;/th&gt;
&lt;th&gt;授权&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;22000&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21027&lt;/td&gt;
&lt;td&gt;UDP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;监听地址&lt;/h3&gt;
&lt;p&gt;GUI 仅本地，同步端口监听所有接口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;!-- ~/.local/state/syncthing/config.xml --&amp;gt;
&amp;lt;gui enabled=&amp;quot;true&amp;quot; tls=&amp;quot;false&amp;quot;&amp;gt;
    &amp;lt;address&amp;gt;127.0.0.1:8384&amp;lt;/address&amp;gt;
&amp;lt;/gui&amp;gt;
&amp;lt;options&amp;gt;
    &amp;lt;listenAddress&amp;gt;tcp://0.0.0.0:22000&amp;lt;/listenAddress&amp;gt;
&amp;lt;/options&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;NAT 穿透验证&lt;/h3&gt;
&lt;p&gt;云服务器无 UPnP（&lt;code&gt;Detected NAT services: count=0&lt;/code&gt; 正常）。开端口后确认直连：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 看日志：不应再有 &amp;quot;Joined relay&amp;quot;
journalctl -u syncthing -f | grep -i relay
# 应出现 quic://公网IP:22000，不再走 relay
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;设置访问密码&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;gui enabled=&amp;quot;true&amp;quot; tls=&amp;quot;false&amp;quot;&amp;gt;
    &amp;lt;address&amp;gt;127.0.0.1:8384&amp;lt;/address&amp;gt;
    &amp;lt;user&amp;gt;admin&amp;lt;/user&amp;gt;
    &amp;lt;password&amp;gt;example-password&amp;lt;/password&amp;gt;
&amp;lt;/gui&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;密码会在下次启动时自动哈希。&lt;/p&gt;
&lt;h2&gt;开机自启&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p ~/.config/systemd/user
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;# ~/.config/systemd/user/syncthing.service
[Unit]
Description=Syncthing - Open Source Continuous File Synchronization
Documentation=man:syncthing(1)
After=network.target

[Service]
ExecStart=/usr/local/bin/syncthing -no-browser -gui-address=127.0.0.1:8384 -logflags=0
Restart=on-failure
RestartSec=10
UMask=0027
Environment=STNORESTART=1

[Install]
WantedBy=default.target
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;systemctl --user daemon-reload
systemctl --user enable syncthing.service
systemctl --user start syncthing.service
systemctl --user status syncthing.service
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;诊断命令&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# GUI 监听
ss -tlnp | grep 8384

# 同步端口
ss -tlnp | grep 22000

# 外部可达性
nc -zv 203.0.113.1 22000

# GUI 可达性
curl -s -o /dev/null -w &amp;quot;%{http_code}&amp;quot; http://127.0.0.1:8384
curl -s -o /dev/null -w &amp;quot;%{http_code}&amp;quot; https://sync.example.com

# 实时日志
journalctl -u syncthing -f

# 检查 relay 状态（无输出=直连成功）
journalctl -u syncthing --since &amp;quot;5 min ago&amp;quot; | grep &amp;quot;Joined relay&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Thu, 11 Jun 2026 21:15:00 GMT</pubDate></item><item><title>GNOME 剪贴板管理器 Clipboard Indicator — 原生 · 高响应 · 安全</title><link>https://blog.nywerya.xyz/blog/gnome-clipboard-indicator/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/gnome-clipboard-indicator/</guid><description>GNOME 上最好的剪贴板管理插件，200 万+ 下载。原生 Shell 集成、即搜即得、 数据不出机器。装了就忘不掉。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/gnome-clipboard-indicator/&quot;&gt;https://blog.nywerya.xyz/blog/gnome-clipboard-indicator/&lt;/a&gt;&lt;/blockquote&gt; &lt;h2&gt;痛点&lt;/h2&gt;
&lt;p&gt;GNOME 桌面默认的剪贴板只有**「最后一次复制」**的内容。前脚复制了一段代码，后脚不小心 &lt;code&gt;Ctrl+C&lt;/code&gt; 了个空行 — 刚才那段话就没了。&lt;/p&gt;
&lt;p&gt;Windows 有 &lt;code&gt;Win+V&lt;/code&gt; 剪贴板历史，macOS 有第三方工具，GNOME 呢？靠插件。&lt;/p&gt;
&lt;h2&gt;Clipboard Indicator&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://extensions.gnome.org/extension/779/clipboard-indicator/&quot;&gt;Clipboard Indicator&lt;/a&gt; 是 GNOME Extensions 上&lt;strong&gt;下载量最高的剪贴板管理插件&lt;/strong&gt;（200 万+），由 Tudmotu 开发维护，支持 GNOME 3.36 ~ 50。&lt;/p&gt;
&lt;p&gt;为什么选它？三个词：&lt;strong&gt;原生、高响应、安全&lt;/strong&gt;。&lt;/p&gt;
&lt;h2&gt;原生：长在 GNOME 身上&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Clipboard Indicator 是一个 GNOME Shell Extension，不是独立 App。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;零寄生依赖&lt;/strong&gt; — 它跑在 GNOME Shell 进程里，用 GNOME 原生的 JS 运行时，不依赖 Electron、不依赖 Python 环境、不需要独立守护进程&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原生面板集成&lt;/strong&gt; — 图标直接嵌入顶部 panel，不走系统托盘（GNOME 本来也没有托盘），和时钟、网络、音量图标平起平坐&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原生 UI 风格&lt;/strong&gt; — 下拉菜单、搜索框、设置窗口全部走 GNOME 系统主题，开深色模式自动跟随，不会出现 GTK 和 Qt 混搭的割裂感&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;原生扩展管理&lt;/strong&gt; — 用系统自带的 Extensions 应用就能配置、开关、升级，没有额外的后台管理界面&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;做个不严谨的类比：CopyQ 像是装了个第三方输入法，Clipboard Indicator 像是系统输入法自带的功能增强 — 前者功能更强，后者毫无存在感。&lt;/p&gt;
&lt;h2&gt;高响应：不让你等&lt;/h2&gt;
&lt;p&gt;剪贴板管理器有个反直觉的要求：&lt;strong&gt;你必须比 &lt;code&gt;Ctrl+V&lt;/code&gt; 还快，才有存在意义。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果按下快捷键后菜单弹出要卡半秒，人会下意识放弃它，继续用 &lt;code&gt;Ctrl+V&lt;/code&gt; 完事。&lt;/p&gt;
&lt;p&gt;Clipboard Indicator 做到了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;即时弹出&lt;/strong&gt; — 按下快捷键，菜单即刻出现。原因是它只用 GNOME Shell 原生 API，没有跨进程通信，没有 DBus 来回，所有数据在内存里&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;即搜即得&lt;/strong&gt; — 历史列表内直接打字搜索，无索引重建、无后台扫描，就是简单的前端文本匹配&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;常驻内存但不吃资源&lt;/strong&gt; — 作为 GNOME Shell 扩展运行，不需要额外进程、不轮询剪贴板（用 GNOME 的信号机制被动接收复制事件），闲置时 CPU 占用趋近于零&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;图片也流畅&lt;/strong&gt; — 少数支持剪贴板图片历史的 GNOME 管理器，预览、粘贴图片不卡顿&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;装了跟没装一样的性能开销，但关键时刻按一下 &lt;code&gt;Super+V&lt;/code&gt;，半年前复制的那段命令还能找回来。&lt;/p&gt;
&lt;h2&gt;安全：剪贴板数据不出机器&lt;/h2&gt;
&lt;p&gt;剪贴板是桌面上最敏感的数据通道之一。密码、Token、私钥、API Key、身份证号 — 都可能经过剪贴板。&lt;/p&gt;
&lt;p&gt;Clipboard Indicator 在安全上做对了三件事：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;① 数据完全本地&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不联网、不请求任何远程 API、不上传任何数据&lt;/li&gt;
&lt;li&gt;历史记录只存在内存中，重启后清空（不会写入磁盘）&lt;/li&gt;
&lt;li&gt;代码开源在 &lt;a href=&quot;https://github.com/Tudmotu/gnome-shell-extension-clipboard-indicator&quot;&gt;GitHub&lt;/a&gt;，审过再说&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;② 隐私模式 — 一键暂停记录&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;进入隐私模式后，复制的内容不会进入历史。临时处理敏感信息时打开，做完关掉 — 密码、银行卡号之类的东西不会留在列表里。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;③ 随时可清&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;右键菜单 → Clear history，所有历史一键清空。没有「软删除」，没有「回收站」，数据即刻消失。&lt;/p&gt;
&lt;p&gt;相比某些剪贴板管理器会把历史存到 SQLite 甚至同步到云端，Clipboard Indicator 的安全模型简单粗暴：&lt;strong&gt;你的剪贴板只属于你的内存，关机即清零。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;核心功能速览&lt;/h2&gt;
&lt;h3&gt;剪贴板历史&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;自动记录文本 + 图片&lt;/li&gt;
&lt;li&gt;可设缓存条数上限&lt;/li&gt;
&lt;li&gt;支持搜索&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;内容置顶&lt;/h3&gt;
&lt;p&gt;右键 Pin 到顶部，适合固定邮箱地址、Git commit 模板、代码片段。&lt;/p&gt;
&lt;h3&gt;全键盘操作&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;按键&lt;/th&gt;
&lt;th&gt;功能&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;↑&lt;/code&gt; &lt;code&gt;↓&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;浏览&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Enter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;粘贴&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;删除&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;置顶/取消&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;其他&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;粘贴后自动关闭菜单&lt;/li&gt;
&lt;li&gt;可自定义快捷键（默认 &lt;code&gt;Super+V&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;面板图标可换&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;安装&lt;/h2&gt;
&lt;h3&gt;方式一：网页一键&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://extensions.gnome.org/extension/779/clipboard-indicator/&quot;&gt;extensions.gnome.org/extension/779&lt;/a&gt; → 点右上角开关。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;开关灰色？先装浏览器连接器：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install chrome-gnome-shell
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;h3&gt;方式二：包管理器&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Debian / Ubuntu
sudo apt install gnome-shell-extension-clipboard-indicator
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Fedora
sudo dnf install gnome-shell-extension-clipboard-indicator
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Arch
sudo pacman -S gnome-shell-extension-clipboard-indicator
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;方式三：手动&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/Tudmotu/gnome-shell-extension-clipboard-indicator.git \
  ~/.local/share/gnome-shell/extensions/clipboard-indicator@tudmotu.com

gnome-extensions enable clipboard-indicator@tudmotu.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;装完后 &lt;code&gt;Alt+F2&lt;/code&gt; → &lt;code&gt;r&lt;/code&gt; → 回车。&lt;/p&gt;
&lt;h2&gt;配置建议&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Extensions 应用 → Clipboard Indicator → 设置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache Size&lt;/strong&gt; → 50~100&lt;/li&gt;
&lt;li&gt;开启 &lt;strong&gt;Paste on selection&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;快捷键绑到 &lt;code&gt;Super+Shift+V&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;同类对比&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;Clipboard Indicator&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;GPaste&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;CopyQ&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;原生 GNOME 集成&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;响应速度&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;即时&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;有延迟&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;有延迟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;数据不出机器&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌（可能写磁盘）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;隐私模式&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图片支持&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;❌&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;额外进程&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;无&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;有&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;有&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Clipboard Indicator 不是功能最多的剪贴板管理器，但它是最「GNOME」的 — 原生集成、零延迟响应、数据不出机器。&lt;/p&gt;
&lt;p&gt;装了就忘不掉，GNOME 桌面必装。&lt;/p&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate></item><item><title>为什么你输入的链接从「幽香.jpg」变成了「%E7...」</title><link>https://blog.nywerya.xyz/blog/url-encoding-chinese-links/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/url-encoding-chinese-links/</guid><description>中文 URL 为什么变成 %XX 乱码？从 HTTP 协议的 ASCII 遗产说起，讲透 URL 编码的原理、UTF-8 字节转换、浏览器何时帮你藏、curl 何时炸、以及实操建议。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/url-encoding-chinese-links/&quot;&gt;https://blog.nywerya.xyz/blog/url-encoding-chinese-links/&lt;/a&gt;&lt;/blockquote&gt; &lt;h1&gt;为什么你输入的链接从「幽香.jpg」变成了「%E7...」&lt;/h1&gt;
&lt;h2&gt;你有没有见过这种场景&lt;/h2&gt;
&lt;p&gt;你往 Markdown 里插一张图片：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;![幽香](https://cdn.example.com/Obsidian/私人/绘画/幽香.jpg)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写完还挺满意——路径清晰，中文文件名一目了然。Ctrl+C 复制链接，贴到地址栏，回车——图片正常显示。一切正常。&lt;/p&gt;
&lt;p&gt;然后你 &lt;code&gt;curl&lt;/code&gt; 一下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -I &amp;quot;https://cdn.example.com/Obsidian/私人/绘画/幽香.jpg&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回 &lt;code&gt;400 Bad Request&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;过了几天，你在 Obsidian 里把同一个文件拖进编辑器，发现它自动变成了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://cdn.example.com/Obsidian/%E7%A7%81%E4%BA%BA/%E7%BB%98%E7%94%BB/%E5%B9%BD%E9%A6%99.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你盯着这串乱码——不对，这是有规律的，看起来像是某种编码。&lt;/p&gt;
&lt;p&gt;没错。这就是 &lt;strong&gt;URL 编码&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;你每天在互联网上看到的每一个链接，只要包含非英文内容，背后都在经历同样的变形。只是浏览器帮你遮住了——它把乱码藏了起来，让你以为中文链接&amp;quot;本来就能用&amp;quot;。&lt;/p&gt;
&lt;h2&gt;URL 为什么不能直接写中文&lt;/h2&gt;
&lt;p&gt;回到 1994 年。&lt;/p&gt;
&lt;p&gt;那年 HTTP 1.0 还没正式发布，互联网上跑的链接长这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://example.com/index.html
http://example.com/search?q=hello
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;全是 ASCII。因为 RFC 1738（URL 规范）明确规定：&lt;strong&gt;URL 只能包含 ASCII 可打印字符&lt;/strong&gt;。理由很朴素——当时互联网上的服务器和路由器五花八门，谁知道某个中间设备收到非 ASCII 字节会怎么处理？&lt;/p&gt;
&lt;p&gt;于是得出一条铁律：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;URL 里不能有非 ASCII 字符。要传？先编码。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是 URL 编码存在的唯一原因。&lt;/p&gt;
&lt;p&gt;你说「浏览器不是能显示中文吗？」——那是在&lt;strong&gt;地址栏&lt;/strong&gt;里。浏览器对用户友好，会自动把中文解码显示。但背后的 HTTP 请求，永远、必须、只能是 ASCII。你粘贴到地址栏看到的中文，发出去那一瞬间已经被浏览器偷偷编码了。&lt;/p&gt;
&lt;p&gt;不信你打开 F12 → Network，随便点一个中文路径的请求，看 Request URL——全是 &lt;code&gt;%XX&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;怎么变的：三句话&lt;/h2&gt;
&lt;p&gt;编码规则本身不复杂：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;把中文字符转成 UTF-8 字节&lt;/strong&gt;。比如「私」= &lt;code&gt;E7 A7 81&lt;/code&gt;（3 字节）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每个字节前面加个 &lt;code&gt;%&lt;/code&gt;&lt;/strong&gt;，写成十六进制 → &lt;code&gt;%E7 %A7 %81&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;英文字母和数字保持原样&lt;/strong&gt;，因为 ASCII 安全&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以「幽香.jpg」为例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;幽 → UTF-8: E5 B9 BD → %E5%B9%BD
香 → UTF-8: E9 A6 99 → %E9%A6%99
.  → ASCII:  2E       → .（保持）
j  → ASCII:  6A       → j
p  → ASCII:  70       → p
g  → ASCII:  67       → g
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果：「幽香.jpg」→ &lt;code&gt;%E5%B9%BD%E9%A6%99.jpg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;同理，「私人」→ &lt;code&gt;%E7%A7%81%E4%BA%BA&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;脑子里过一遍：UTF-8 里一个 CJK 汉字占 3 字节，所以每个汉字变成 3 组 &lt;code&gt;%XX&lt;/code&gt;。&lt;/p&gt;
&lt;h2&gt;各种场景实测&lt;/h2&gt;
&lt;p&gt;同一个中文链接，不同地方的表现：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;看到的&lt;/th&gt;
&lt;th&gt;实际发出的&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;浏览器地址栏&lt;/td&gt;
&lt;td&gt;&lt;code&gt;私人/绘画/幽香.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%E7%A7%81%E4%BA%BA/...&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Markdown 渲染&lt;/td&gt;
&lt;td&gt;&lt;code&gt;私人/绘画/幽香.jpg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;同上，浏览器自动编码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;curl (bash)&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;取决于有没有引号，可能炸&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;wget&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;&lt;code&gt;400 Bad Request&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nginx access log&lt;/td&gt;
&lt;td&gt;&lt;code&gt;%E7%A7%81...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;原始编码后请求&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;想安全地在命令行用中文 URL：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 错误：shell 可能把中文吞了
curl https://cdn.example.com/Obsidian/私人/绘画/幽香.jpg

# 正确：手动编码
curl &amp;quot;https://cdn.example.com/Obsidian/$(python3 -c &amp;quot;import urllib.parse; print(urllib.parse.quote(&amp;#39;私人/绘画/幽香.jpg&amp;#39;))&amp;quot;)&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以现找一个在线 URL encoder，输入中文得到 &lt;code&gt;%XX&lt;/code&gt; 版本。&lt;/p&gt;
&lt;h2&gt;为什么有的链接中文没问题&lt;/h2&gt;
&lt;p&gt;现代规范（RFC 3986 + WHATWG URL Standard）放宽了限制——URL 可以包含 Unicode，&lt;strong&gt;但浏览器必须内部编码后再发&lt;/strong&gt;。所以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;浏览器里的链接&lt;/strong&gt;：大概率没问题，浏览器帮你兜底&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;微信/QQ 里点的链接&lt;/strong&gt;：可能没问题，客户端会编码&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;curl 里的链接&lt;/strong&gt;：看 shell 和 curl 版本，有时候行有时候炸&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;nginx rewrite 规则&lt;/strong&gt;：必须写正则匹配 &lt;code&gt;%XX&lt;/code&gt;，不能直接匹配中文&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以是「看你运气」。靠谱做法是自己编码一步到位。&lt;/p&gt;
&lt;h2&gt;有没有办法不编码直接传中文&lt;/h2&gt;
&lt;p&gt;有一个提案叫 &lt;strong&gt;IRI&lt;/strong&gt;（Internationalized Resource Identifier，RFC 3987），允许 URL 直接包含 Unicode。现代浏览器和大部分 Web 框架都支持 IRI。&lt;/p&gt;
&lt;p&gt;实际效果：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你在地址栏输入 &lt;code&gt;https://网站/中文路径&lt;/code&gt; ← 浏览器支持 IRI 输入&lt;/li&gt;
&lt;li&gt;但 HTTP 请求头里还是 &lt;code&gt;https://%E7%BD%91%E7%AB%99/...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;所以底层还是编码的&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IRI 只是在用户层面让你可以&lt;strong&gt;看中文&lt;/strong&gt;，传输层从未改变。&lt;/p&gt;
&lt;h2&gt;操作建议&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;场景&lt;/th&gt;
&lt;th&gt;建议&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;手写 HTML/Markdown&lt;/td&gt;
&lt;td&gt;能编码就编码，文件名尽量用英文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;配 Nginx location&lt;/td&gt;
&lt;td&gt;用 &lt;code&gt;%XX&lt;/code&gt;，别写中文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;bash 脚本&lt;/td&gt;
&lt;td&gt;用 Python &lt;code&gt;urllib.parse.quote()&lt;/code&gt; 编码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;图床 CDN&lt;/td&gt;
&lt;td&gt;Syncthing 同步到 VPS 时文件名是中文没问题，但公开链接里建议统一编码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;写文章引用 CDN 图片&lt;/td&gt;
&lt;td&gt;把图片路径跑一遍 encodeURIComponent，粘贴编码后的&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;我的自建图床 &lt;code&gt;cdn.example.com&lt;/code&gt; 上两种链接都能访问——Nginx 收到的都是 &lt;code&gt;%XX&lt;/code&gt; 格式，不存在不兼容。区别只在于你肉眼想读中文还是读编码。&lt;/p&gt;
&lt;h2&gt;最后&lt;/h2&gt;
&lt;p&gt;URL 编码这件事，说到底是互联网在 30 年前留下的疤痕。&lt;/p&gt;
&lt;p&gt;当年那群工程师觉得「ASCII 够用了」，没想过有一天中国人要在互联网上写汉字。等到发现不够用了，只能在上面糊一层编码层——用户看到中文，传输层传 ASCII，来回翻译，谁都不爽但谁也没法推到重来。&lt;/p&gt;
&lt;p&gt;你现在看到的每一个 &lt;code&gt;%E7%A7%81&lt;/code&gt;，都在替 1994 年的设计决策还债。&lt;/p&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate></item><item><title>自建博客 SEO 实战：不提废话，做完就有效</title><link>https://blog.nywerya.xyz/blog/self-hosted-blog-seo-guide/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/self-hosted-blog-seo-guide/</guid><description>自建博客搜索优化的手把手教程，覆盖 Sitemap、robots.txt、 Google Search Console、内容 Checklist、站外引流。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/self-hosted-blog-seo-guide/&quot;&gt;https://blog.nywerya.xyz/blog/self-hosted-blog-seo-guide/&lt;/a&gt;&lt;/blockquote&gt; &lt;h1&gt;自建博客 SEO 实战：不提废话，做完就有效&lt;/h1&gt;
&lt;p&gt;适用：Astro / Hexo / Hugo / VitePress 等静态博客，自建域名。&lt;/p&gt;
&lt;h2&gt;1. 技术基建（一次配好）&lt;/h2&gt;
&lt;h3&gt;1.1 Sitemap&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Astro
pnpm add @astrojs/sitemap
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// astro.config.mjs
import sitemap from &amp;#39;@astrojs/sitemap&amp;#39;;
export default { integrations: [sitemap()] };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;构建后会生成 &lt;code&gt;sitemap-index.xml&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;1.2 robots.txt&lt;/h3&gt;
&lt;p&gt;放入 &lt;code&gt;public/robots.txt&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;User-agent: *
Allow: /
Sitemap: https://你的域名/sitemap-index.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.3 文章 Frontmatter 必填项&lt;/h3&gt;
&lt;p&gt;每篇不缺不漏：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;title: 含关键词的标题
description: 150 字内摘要
pubDate: 2026-06-02
image: https://你的图床域名/封面图.png
draft: false
tags:
  - 关键词1
  - 关键词2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1.4 图片优化&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;用 WebP / AVIF（PNG 太大）&lt;/li&gt;
&lt;li&gt;标签写 &lt;code&gt;width&lt;/code&gt; &lt;code&gt;height&lt;/code&gt;（防 CLS）&lt;/li&gt;
&lt;li&gt;封面图放 CDN / 图床，不存 Git 仓库&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;2. Google Search Console（必做）&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;打开 &lt;a href=&quot;https://search.google.com/search-console&quot;&gt;search.google.com/search-console&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;添加资源 → 网址前缀 → 填 &lt;code&gt;https://你的域名&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;验证（HTML 文件上传最简单：下载 &lt;code&gt;.html&lt;/code&gt; 放到 &lt;code&gt;public/&lt;/code&gt;，部署即可）&lt;/li&gt;
&lt;li&gt;左侧菜单 → 站点地图 → 提交 &lt;code&gt;sitemap-index.xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;网址检查 → 逐个输入你的文章 URL → 请求编入索引&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;每发一篇新文章，第 5 步重复一次。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. 内容 Checklist（每篇对照）&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;项&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;标题含关键词&lt;/td&gt;
&lt;td&gt;搜的人打什么，标题就含什么&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URL 语义化&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/blog/deploy-waline&lt;/code&gt; 不是 &lt;code&gt;/blog/post-3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;内链 2+&lt;/td&gt;
&lt;td&gt;每篇链到两篇旧文&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;H2/H3 含关键词变体&lt;/td&gt;
&lt;td&gt;不只是「问题」，而是「搜索权重低怎么办」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;首段 100 字摘要&lt;/td&gt;
&lt;td&gt;Google 展示在搜索结果里&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;封面图 1000×600&lt;/td&gt;
&lt;td&gt;Open Graph 分享时的社交卡片&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;4. 站外引流（按优先级）&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;GitHub&lt;/strong&gt;：README / 项目主页放博客链接&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;技术社区&lt;/strong&gt;：V2EX、掘金发帖时引用&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;社交媒体&lt;/strong&gt;：Twitter / 朋友圈转发&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;5. 检查清单（每次写完文章）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&gt; &lt;code&gt;title&lt;/code&gt; &lt;code&gt;description&lt;/code&gt; &lt;code&gt;pubDate&lt;/code&gt; &lt;code&gt;image&lt;/code&gt; 不缺&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&gt; 一张封面图，CDN URL 可访问&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&gt; 内链 ≥ 2 篇旧文&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&gt; URL 可读&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&gt; 部署后去 Search Console 手动提交 URL&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&quot;&quot; type=&quot;checkbox&quot;&gt; Sitemap 状态正常&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Tue, 02 Jun 2026 00:00:00 GMT</pubDate></item><item><title>Linux包管理升级 - 终极指令大全(2026-05)</title><link>https://blog.nywerya.xyz/blog/linux-package-upgrade/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/linux-package-upgrade/</guid><description>Linux 各发行版全量升级已安装软件包的命令速查，覆盖 apt/dnf/pacman/zypper/emerge/apk/snap/flatpak/xbps/nix 共 10 种包管理器。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/linux-package-upgrade/&quot;&gt;https://blog.nywerya.xyz/blog/linux-package-upgrade/&lt;/a&gt;&lt;/blockquote&gt; &lt;h1&gt;Linux 包管理升级&lt;/h1&gt;
&lt;p&gt;升级系统内所有已安装软件包，按发行版和包管理器分类。&lt;/p&gt;
&lt;h2&gt;Debian / Ubuntu (apt)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 更新软件包列表
sudo apt update
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 升级已安装软件包至最新稳定版本
sudo apt upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 完整升级：处理包依赖变更和替换
sudo apt full-upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 清理不再需要的依赖包
sudo apt autoremove
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;RHEL / CentOS 7 (yum)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 检查可用更新
sudo yum check-update
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 升级所有已安装软件包
sudo yum update
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;RHEL / CentOS 8+ / Fedora (dnf)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 检查可用更新
sudo dnf check-update
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 升级所有已安装软件包
sudo dnf upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 清理缓存
sudo dnf clean all
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Arch Linux (pacman)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 同步软件包数据库并升级所有已安装软件包
sudo pacman -Syu
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 清理包缓存（保留最近 3 个版本）
sudo paccache -r
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;OpenSUSE (zypper)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 更新软件源索引
sudo zypper refresh
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 升级所有已安装软件包
sudo zypper update
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Gentoo (emerge)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 同步 Portage 树并更新整个系统
sudo emerge --sync &amp;amp;&amp;amp; sudo emerge --update --deep --newuse @world
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 清理不再需要的依赖
sudo emerge --depclean
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Alpine (apk)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 更新索引并升级所有包
sudo apk update &amp;amp;&amp;amp; sudo apk upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Snap（跨发行版）&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 刷新所有已安装 snap 包
sudo snap refresh
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 查看待升级的 snap 列表
sudo snap refresh --list
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Flatpak（跨发行版）&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 升级所有已安装 flatpak 应用和运行时
flatpak update
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 清理不再需要的运行时
flatpak uninstall --unused
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;NixOS (nix)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 重建系统并升级所有包（切换至新世代）
sudo nixos-rebuild switch --upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 仅构建不切换（测试用）
sudo nixos-rebuild build --upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 清理旧世代和包缓存
sudo nix-collect-garbage -d
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Void Linux (xbps)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 同步仓库索引
sudo xbps-install -S
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 升级所有包
sudo xbps-install -u xbps &amp;amp;&amp;amp; sudo xbps-install -Su
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 清理旧包缓存
sudo xbps-remove -Oo
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;升级后操作&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 检查是否需要重启（内核/ glibc / systemd 更新后）
sudo needrestart
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Debian/Ubuntu: 清理旧内核
sudo apt autoremove --purge
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 查看残留配置文件（dpkg 系）
dpkg -l | grep &amp;#39;^rc&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 对比 .pacnew 配置文件（Arch 系）
sudo pacdiff
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;通用工具 (topgrade)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 跨包管理器一键升级所有软件
topgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：生产环境升级前建议先在测试环境验证，并做好系统备份。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Sun, 31 May 2026 16:48:00 GMT</pubDate></item><item><title>让 Ubuntu 用上官方最新版：跳过「中间商」直达上游</title><link>https://blog.nywerya.xyz/blog/bypass-ubuntu-repo-use-official/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/bypass-ubuntu-repo-use-official/</guid><description>绕开 Ubuntu 保守的软件仓库，从官方源直装最新版——以 Nginx 为例， 涵盖 apt pinning、GPG 密钥管理，附排雷指南。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/bypass-ubuntu-repo-use-official/&quot;&gt;https://blog.nywerya.xyz/blog/bypass-ubuntu-repo-use-official/&lt;/a&gt;&lt;/blockquote&gt; &lt;h1&gt;让 Ubuntu 用上官方最新版：跳过「中间商」直达上游&lt;/h1&gt;
&lt;p&gt;你有没有过这种经历——&lt;code&gt;apt install&lt;/code&gt; 一个包，装完之后一查版本号，发现比 GitHub 上的 Release 落后了整整一个大版本？&lt;/p&gt;
&lt;p&gt;这不是你的问题，是 Ubuntu 的「老派哲学」在作祟。&lt;/p&gt;
&lt;h2&gt;为什么 Ubuntu 仓库总是慢半拍&lt;/h2&gt;
&lt;p&gt;Ubuntu（尤其是 LTS 版本）的核心承诺是&lt;strong&gt;稳如老狗&lt;/strong&gt;。发行版发布之后，大多数软件的版本号就被钉死了——维护者只往回移植安全补丁，不给你升大版本。&lt;/p&gt;
&lt;p&gt;这套逻辑在服务器上很合理：你不想半夜被一个上游更新炸醒。但当你真的需要某个软件的最新特性（比如 Nginx 刚修了个 9.2 分的 CVE），或者单纯不想用三年前的版本——你就得绕过 Ubuntu，直接找上游。&lt;/p&gt;
&lt;h2&gt;三步切到官方源&lt;/h2&gt;
&lt;p&gt;核心思路很简单：&lt;strong&gt;把软件作者维护的 apt 仓库加进来，然后告诉 apt &amp;quot;这个源比 Ubuntu 的更优先&amp;quot;&lt;/strong&gt;。&lt;/p&gt;
&lt;h3&gt;1. 加密钥，证明你认识这个源&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL &amp;lt;官方GPG_URL&amp;gt; | sudo gpg -dearmor -o /usr/share/keyrings/&amp;lt;name&amp;gt;-archive-keyring.gpg
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没有 GPG 密钥，apt 不认这个源。密钥文件统一放进 &lt;code&gt;/usr/share/keyrings/&lt;/code&gt;，比 &lt;code&gt;/etc/apt/trusted.gpg.d/&lt;/code&gt; 更安全。&lt;/p&gt;
&lt;h3&gt;2. 加源，告诉 apt 这个仓库在哪&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;echo &amp;quot;deb [signed-by=/usr/share/keyrings/&amp;lt;name&amp;gt;-archive-keyring.gpg] &amp;lt;官方镜像URL&amp;gt; $(lsb_release -cs) &amp;lt;组件&amp;gt;&amp;quot; | sudo tee /etc/apt/sources.list.d/&amp;lt;name&amp;gt;.list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意 &lt;code&gt;signed-by=&lt;/code&gt; 这个参数——它把这个源&lt;strong&gt;锁定&lt;/strong&gt;到你刚导入的密钥上，别的源就算碰巧同域名也碰不了你。&lt;/p&gt;
&lt;h3&gt;3. 设优先级，明确表态&amp;quot;我要上游&amp;quot;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;printf &amp;#39;Package: *\nPin: origin &amp;lt;域名&amp;gt;\nPin-Priority: 900\n&amp;#39; | sudo tee /etc/apt/preferences.d/&amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就是 &lt;code&gt;apt pinning&lt;/code&gt;。通俗解释就是给 apt 排座次：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;900 分：apt 会优先选这个源的版本，即使 Ubuntu 官方源有同名包&lt;/li&gt;
&lt;li&gt;默认 500 分：两个源都有的话，版本号高的赢&lt;/li&gt;
&lt;li&gt;-1 分：死也不装&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;实战：给 Nginx 装最新版&lt;/h2&gt;
&lt;p&gt;最近 Nginx 爆了两个有意思的 CVE（&lt;a href=&quot;https://www.cycognito.com/blog/emerging-threat-cve-2026-42945-nginx-rift-heap-overflow-in-rewrite-module/&quot;&gt;CVE-2026-42945&lt;/a&gt; 和 &lt;a href=&quot;https://www.indusface.com/blog/nginx-cve-2026-42945-and-cve-2026-9256/&quot;&gt;CVE-2026-9256&lt;/a&gt;），都是 rewrite 模块的堆溢出，一个活了 18 年没被发现，CVSS 双双 9.2 分。Ubuntu 仓库还在 &lt;code&gt;1.24.0&lt;/code&gt;，修复版是 &lt;code&gt;1.30.2&lt;/code&gt;——这中间差了 6 个大版本。&lt;/p&gt;
&lt;p&gt;直接上官方源：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 加密钥
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo gpg -dearmor -o /usr/share/keyrings/nginx-archive-keyring.gpg
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 加源
echo &amp;quot;deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://nginx.org/packages/ubuntu/ $(lsb_release -cs) nginx&amp;quot; | sudo tee /etc/apt/sources.list.d/nginx.list
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 设优先级
printf &amp;#39;Package: *\nPin: origin nginx.org\nPin-Priority: 900\n&amp;#39; | sudo tee /etc/apt/preferences.d/nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 安装
sudo apt update &amp;amp;&amp;amp; sudo apt install nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 验证
nginx -v
# nginx version: nginx/1.31.1
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;警告：走这条路之前你最好知道&lt;/h2&gt;
&lt;p&gt;绕开 Ubuntu 维护的包不是没代价的：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;风险&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;无人兜底&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;不再享受 Ubuntu 安全团队的补丁支持——上游修了你知道，上游没修你只能盯着&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;依赖地狱&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;新版本可能依赖更高版本的库，而这些库 Ubuntu 仓库可能没有。装不上就挠头&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;升级猝死&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;官方源推一个大版本号更新的时候，配置文件可能不兼容（尤其是 nginx 这种）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;无人售后&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;出问题去 GitHub Issue 对线，没人替你 debug&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;卸载不易&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;想退回 Ubuntu 源版本，得先 &lt;code&gt;ppa-purge&lt;/code&gt; 或手动降级，不是一行命令的事&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;简单给自己打三个问号再动手：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;我真的需要那个新版本的特性吗，还是单纯想「最新」？&lt;/li&gt;
&lt;li&gt;这个服务崩了，我能兜得住吗？&lt;/li&gt;
&lt;li&gt;我有测试环境可以先试试吗？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果三条都是「是」——那就放心切。生产环境跑的好好的东西，别动。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Pin-Priority 速查&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;th&gt;效果&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;1000+&lt;/td&gt;
&lt;td&gt;命令降级——「我要这个版本，管你是升是降」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;990&lt;/td&gt;
&lt;td&gt;允许降级——「可以往回走」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500-900&lt;/td&gt;
&lt;td&gt;优先于同版本号的 Ubuntu 源——「大家都是 1.24，但我选你」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1-499&lt;/td&gt;
&lt;td&gt;备胎——「别人都没了才找你」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;-1&lt;/td&gt;
&lt;td&gt;永不安装——「别再出现了」&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;收尾&lt;/h2&gt;
&lt;p&gt;绕开 Ubuntu 仓库有点像从官方直营店买东西——快、新，但没有经销商给你兜底。对于面向公网的服务（比如反代），装最新版反而是件好事：至少 CVE 修得快，不用靠 WAF 硬扛。&lt;/p&gt;
&lt;p&gt;一句话总结：&lt;strong&gt;知道自己在干什么，就别让中间商赚差价。&lt;/strong&gt;&lt;/p&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate></item><item><title>部署 Waline 评论系统到自己的服务器完全指南 (保姆级教程 2026)</title><link>https://blog.nywerya.xyz/blog/deploy-waline-self-host/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/deploy-waline-self-host/</guid><description>从零部署 Waline 评论系统到自有服务器，涵盖 Docker、SQLite、 Nginx 反代、前端接入、管理后台全流程。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/deploy-waline-self-host/&quot;&gt;https://blog.nywerya.xyz/blog/deploy-waline-self-host/&lt;/a&gt;&lt;/blockquote&gt; &lt;h1&gt;部署 Waline 评论系统到自己的服务器完全指南 (保姆级教程 2026)&lt;/h1&gt;
&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://waline.js.org&quot;&gt;Waline&lt;/a&gt; 是一个自托管的静态博客评论系统。基于 Node.js，&lt;strong&gt;内置管理后台&lt;/strong&gt;，数据存 SQLite —— 意味着&lt;strong&gt;不需要单独装数据库&lt;/strong&gt;，一个 Docker 容器就跑了。前端是 Web Component（Vue），原生支持暗黑模式、浏览量统计、表情包、Markdown 渲染、数学公式。&lt;/p&gt;
&lt;p&gt;这篇教程假设你有一台服务器和一个域名，带你从零把 Waline 跑起来，每一步都有解释。&lt;/p&gt;
&lt;h3&gt;为什么选 Waline 而不是 Twikoo&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;对比&lt;/th&gt;
&lt;th&gt;Waline&lt;/th&gt;
&lt;th&gt;Twikoo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;数据库&lt;/td&gt;
&lt;td&gt;SQLite（无需额外容器）&lt;/td&gt;
&lt;td&gt;MongoDB（必须）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;管理后台&lt;/td&gt;
&lt;td&gt;内置 &lt;code&gt;/ui&lt;/code&gt;，注册即用&lt;/td&gt;
&lt;td&gt;访问 &lt;code&gt;/#/login&lt;/code&gt; 设置密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;垃圾过滤&lt;/td&gt;
&lt;td&gt;Akismet + IP 频率 + 关键词 + 相似度检测&lt;/td&gt;
&lt;td&gt;仅 IP 频率限制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;浏览量统计&lt;/td&gt;
&lt;td&gt;内置，一行配置&lt;/td&gt;
&lt;td&gt;不支持&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;前端框架&lt;/td&gt;
&lt;td&gt;Web Component（Vue）&lt;/td&gt;
&lt;td&gt;传统 JS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;暗黑模式&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dark: &amp;#39;auto&amp;#39;&lt;/code&gt; 自适应&lt;/td&gt;
&lt;td&gt;需手动适配&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;登录方式&lt;/td&gt;
&lt;td&gt;注册 + GitHub OAuth&lt;/td&gt;
&lt;td&gt;仅密码&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;镜像体积&lt;/td&gt;
&lt;td&gt;~150MB（含 SQLite）&lt;/td&gt;
&lt;td&gt;需额外 MongoDB 镜像&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;结论：新项目直接用 Waline&lt;/strong&gt;，更轻量、功能更全、部署更简单。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第一步：准备工作&lt;/h2&gt;
&lt;h3&gt;你需要这些&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;东西&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;一台服务器&lt;/td&gt;
&lt;td&gt;1c1g 足够。能 SSH 上去、能装 Docker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;一个域名&lt;/td&gt;
&lt;td&gt;前端 HTTPS 页面无法请求 HTTP 后端，所以必须有域名配 SSL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;一个子域名&lt;/td&gt;
&lt;td&gt;给 Waline 专用，比如 &lt;code&gt;comment.你的域名.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15 分钟&lt;/td&gt;
&lt;td&gt;从零到评论可用&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;没有域名怎么办&lt;/h3&gt;
&lt;p&gt;如果你还没有域名，先去任意域名注册商买一个（Namesilo / Cloudflare / 阿里云 都行）。最便宜的 &lt;code&gt;.xyz&lt;/code&gt; 一年几块钱。&lt;/p&gt;
&lt;p&gt;买完后，在你的 DNS 管理页面添加一条 A 记录，把子域名指向服务器 IP：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;类型: A
主机记录: comment      （表示 comment.你的域名.com）
记录值:   123.456.789.0   （你的服务器公网 IP）
TTL:      600
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;DNS 生效可能需要几分钟。验证一下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 在服务器上执行，看子域名是否解析到你的服务器 IP
nslookup comment.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第二步：安装 Docker&lt;/h2&gt;
&lt;p&gt;如果你的服务器还没装 Docker，先装上。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 官方一键安装脚本
curl -fsSL https://get.docker.com | sudo sh

# 把当前用户加入 docker 组，以后不用每次加 sudo
sudo usermod -aG docker $USER

# 退出 SSH 重新登录，让用户组生效
exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重新登录后验证：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker -version
# 输出类似: Docker version 27.x.x, build ...

docker run -rm hello-world
# 输出 Hello from Docker! 表示 Docker 正常运行
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果 &lt;code&gt;docker pull&lt;/code&gt; 特别慢或被墙，参考文末「常见问题」配镜像加速。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第三步：生成配置值&lt;/h2&gt;
&lt;p&gt;部署 Waline 前，先把要填的值准备好。以下 5 个值需要你替换成自己的：&lt;/p&gt;
&lt;h3&gt;3.1 JWT_TOKEN —— 管理员密钥&lt;/h3&gt;
&lt;p&gt;这是 Waline 内部用来签发登录凭证的密钥，&lt;strong&gt;必须设，必须保密&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 在服务器上执行，生成一个随机字符串
openssl rand -hex 32
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出类似 &lt;code&gt;a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6e1f2a3b4c5d6&lt;/code&gt;。&lt;strong&gt;把这个字符串记下来&lt;/strong&gt;，后面要用。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不要用你别的密码，用 &lt;code&gt;openssl rand&lt;/code&gt; 生成的随机值。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;3.2 SITE_NAME —— 网站名称&lt;/h3&gt;
&lt;p&gt;随便写，会显示在管理后台和邮件通知里。比如「XX 的博客」。&lt;/p&gt;
&lt;h3&gt;3.3 SITE_URL —— 博客地址&lt;/h3&gt;
&lt;p&gt;你的博客完整地址，&lt;strong&gt;带上 &lt;code&gt;https://&lt;/code&gt;&lt;/strong&gt;。比如 &lt;code&gt;https://blog.example.com&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;Waline 用这个来判断哪些请求是合法的（CORS 白名单）。&lt;/p&gt;
&lt;h3&gt;3.4 SECURE_DOMAINS —— 安全域名&lt;/h3&gt;
&lt;p&gt;你的域名，&lt;strong&gt;不带 &lt;code&gt;https://&lt;/code&gt;&lt;/strong&gt;。比如 &lt;code&gt;blog.example.com&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这是评论框允许嵌入的域名白名单。如果你有多个域名（比如 &lt;code&gt;example.com&lt;/code&gt; 和 &lt;code&gt;www.example.com&lt;/code&gt;），用逗号分隔：&lt;code&gt;example.com,www.example.com&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;3.5 AUTHOR_EMAIL —— 你的邮箱&lt;/h3&gt;
&lt;p&gt;收到新评论时会往这个邮箱发通知（需要配 SMTP，见第五步）。&lt;/p&gt;
&lt;h3&gt;填完后的参考&lt;/h3&gt;
&lt;p&gt;假设你的域名是 &lt;code&gt;blog.example.com&lt;/code&gt;，子域名打算用 &lt;code&gt;comment.example.com&lt;/code&gt;：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;变量&lt;/th&gt;
&lt;th&gt;值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;JWT_TOKEN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;a1b2c3d4...&lt;/code&gt;（openssl rand 那串）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SITE_NAME&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;我的博客&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SITE_URL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://blog.example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SECURE_DOMAINS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;blog.example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AUTHOR_EMAIL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user@example.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第四步：部署 Waline 后端&lt;/h2&gt;
&lt;p&gt;终于要开始部署了。以下操作全部在服务器上执行。&lt;/p&gt;
&lt;h3&gt;4.1 拉取镜像&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker pull lizheming/waline:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;镜像大约 150MB，半分钟到一分钟能拉完。&lt;/p&gt;
&lt;h3&gt;4.1.5 安装 docker-compose（可选，推荐）&lt;/h3&gt;
&lt;p&gt;之后用 Compose 管理容器比手敲 &lt;code&gt;docker run&lt;/code&gt; 方便得多，建议先装上：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install -y docker-compose
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你的 Docker 版本 ≥ 20.10，自带 &lt;code&gt;docker compose&lt;/code&gt; 插件（注意是空格不是连字符），那么下面的 &lt;code&gt;docker-compose&lt;/code&gt;（有连字符）命令要替换成 &lt;code&gt;docker compose&lt;/code&gt;（空格）。版本查看：&lt;code&gt;docker -version&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;4.2 创建数据目录&lt;/h3&gt;
&lt;p&gt;Waline 的评论数据存在 SQLite 文件里，这个文件需要挂载到宿主机，否则删容器数据就没了。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p /opt/waline/data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/opt/waline/data&lt;/code&gt; 是约定俗成的路径，你想放别的地方也行，但后面命令里的路径要跟着改。&lt;/p&gt;
&lt;h3&gt;4.3 启动容器&lt;/h3&gt;
&lt;p&gt;把 3.1~3.5 准备好的值填进去，执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 替换以下 5 个值:
#   JWT_TOKEN:      openssl rand -hex 32 生成
#   SITE_NAME:      你的网站名称（含 &amp;amp; 等特殊字符需加引号，如 &amp;quot;Code &amp;amp; Flower&amp;quot;）
#   SITE_URL:       你的博客域名，带 https://
#   SECURE_DOMAINS: 你的域名，不带 https://
#   AUTHOR_EMAIL:   你的邮箱

docker run -d \
  -name waline \
  -restart=unless-stopped \
  -p 127.0.0.1:8360:8360 \
  -v /opt/waline/data:/app/data \
  -e TZ=Asia/Shanghai \
  -e SQLITE_PATH=/app/data \
  -e JWT_TOKEN=a1b2c3d4... \
  -e &amp;quot;SITE_NAME=我的博客&amp;quot; \
  -e SITE_URL=https://blog.example.com \
  -e SECURE_DOMAINS=blog.example.com \
  -e AUTHOR_EMAIL=user@example.com \
  lizheming/waline:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个参数的含义：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;参数&lt;/th&gt;
&lt;th&gt;作用&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;后台运行&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-name waline&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;给容器取名，方便后续操作&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-restart=unless-stopped&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;服务器重启后自动启动容器&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-p 127.0.0.1:8360:8360&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;只监听本机 8360 端口（不暴露公网，安全）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-v /opt/waline/data:/app/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;把宿主机目录挂载进容器，数据持久化&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-e SQLITE_PATH=/app/data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;告诉 Waline SQLite 文件存哪&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-e JWT_TOKEN=...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;管理员登录密钥&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-e SITE_NAME/URL/DOMAINS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;站点信息&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-e AUTHOR_EMAIL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;博主邮箱&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;关键提示&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;bash 续行符 &lt;code&gt;\&lt;/code&gt; 必须是该行最后一个字符，&lt;strong&gt;后面不能跟任何东西（包括空格和 &lt;code&gt;# 注释&lt;/code&gt;）&lt;/strong&gt;，否则续行会断裂。&lt;/li&gt;
&lt;li&gt;如果 &lt;code&gt;SITE_NAME&lt;/code&gt; 含 &lt;code&gt;&amp;amp;&lt;/code&gt; 等特殊字符，要用双引号包住整个 &lt;code&gt;-e &amp;quot;SITE_NAME=xxx&amp;quot;&lt;/code&gt;，否则 bash 会把 &lt;code&gt;&amp;amp;&lt;/code&gt; 解释为后台运行符。&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;h3&gt;4.4 验证容器状态&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker ps | grep waline
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看到 &lt;code&gt;Up&lt;/code&gt; 字样说明容器在运行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;abc123def456   lizheming/waline:latest   ...   Up 10 seconds   127.0.0.1:8360-&amp;gt;8360/tcp   waline
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以看日志确认有没有报错：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker logs waline -tail 20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正常的话最后一行类似 &lt;code&gt;ThinkJS 3.x is running at http://127.0.0.1:8360&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;4.5 使用 Docker Compose（可选且推荐，在后面操作成功之后回来把配置写进文件里，再启动就不用用一大串命令）&lt;/h3&gt;
&lt;p&gt;用 Compose 管理比手敲 &lt;code&gt;docker run&lt;/code&gt; 更方便，配置一目了然。&lt;/p&gt;
&lt;p&gt;新建 &lt;code&gt;docker-compose.yml&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mkdir -p /opt/waline &amp;amp;&amp;amp; cd /opt/waline
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# docker-compose.yml
version: &amp;#39;3&amp;#39;
services:
  waline:
    container_name: waline
    image: lizheming/waline:latest
    restart: always
    ports:
      - 127.0.0.1:8360:8360
    volumes:
      - ./data:/app/data
    environment:
      TZ: Asia/Shanghai
      SQLITE_PATH: /app/data
      JWT_TOKEN: a1b2c3d4...              # ← 替换
      SITE_NAME: &amp;quot;我的博客&amp;quot;                # ← 替换：含特殊字符需引号
      SITE_URL: https://blog.example.com   # ← 替换
      SECURE_DOMAINS: blog.example.com     # ← 替换
      AUTHOR_EMAIL: user@example.com       # ← 替换
      IPQPS: 5
      AKISMET_KEY: &amp;quot;false&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 重启服务
docker stop waline &amp;amp;&amp;amp; docker rm waline
docker-compose up -d
docker ps | grep waline
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后续改配置也是编辑 &lt;code&gt;docker-compose.yml&lt;/code&gt; 然后 &lt;code&gt;docker-compose up -d&lt;/code&gt; 即可。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第五步：Nginx 反代 + HTTPS&lt;/h2&gt;
&lt;p&gt;Waline 只监听了 &lt;code&gt;127.0.0.1:8360&lt;/code&gt;，外部访问不到。需要用 Nginx 反代到公网，并配上 HTTPS。&lt;/p&gt;
&lt;p&gt;用 Certbot 自动化处理证书 —— 比手动配 acme.sh 简单得多，一条命令就能搞定申请 + 配置 + 自动续期。&lt;/p&gt;
&lt;h3&gt;5.1 安装 Nginx 和 Certbot&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.2 创建临时 HTTP 站点&lt;/h3&gt;
&lt;p&gt;Certbot 需要通过 HTTP（80 端口）验证域名所有权，所以先建一个 HTTP-only 的 Nginx 站点。&lt;strong&gt;替换 &lt;code&gt;server_name&lt;/code&gt; 为你的子域名&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo tee /etc/nginx/sites-available/waline &amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;
server {
    listen 80;
    server_name comment.example.com;                       # ← 替换：你的子域名

    location / {
        proxy_pass http://127.0.0.1:8360;
        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_set_header REMOTE-HOST $remote_addr;
    }
}
EOF
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启用站点并重载 Nginx：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ln -sf /etc/nginx/sites-available/waline /etc/nginx/sites-enabled/
sudo nginx -t &amp;amp;&amp;amp; sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.3 Certbot 自动获取 SSL 证书&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;替换域名为你的子域名&lt;/strong&gt;，然后执行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo certbot -nginx -d comment.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Certbot 交互流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;输入邮箱&lt;/strong&gt; — 用于证书到期提醒（填你的真实邮箱）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;同意服务条款&lt;/strong&gt; — 输入 &lt;code&gt;Y&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;是否接收推广邮件&lt;/strong&gt; — 输入 &lt;code&gt;N&lt;/code&gt;（看你自己）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTP 是否自动跳转 HTTPS&lt;/strong&gt; — 选 &lt;strong&gt;2 (Redirect)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;执行完后 certbot 自动做了三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;申请 Let&amp;#39;s Encrypt SSL 证书&lt;/li&gt;
&lt;li&gt;把 Nginx 配置改成 HTTPS + 证书路径&lt;/li&gt;
&lt;li&gt;加了 HTTP→HTTPS 自动跳转&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;证书有效期 90 天，&lt;strong&gt;certbot 安装时会自动注册 systemd timer，到期前自动续期&lt;/strong&gt;。手动检查续期状态：&lt;code&gt;sudo certbot renew -dry-run&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;5.4 验证&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl https://comment.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应该返回一段 JSON：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{&amp;quot;name&amp;quot;:&amp;quot;Waline&amp;quot;,&amp;quot;version&amp;quot;:&amp;quot;x.x.x&amp;quot;,...}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看到这个 JSON 说明后端已经通过 HTTPS 正常提供服了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第六步：注册管理员&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;这一步必须立刻做，免得被别人抢注。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;浏览器打开：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://comment.example.com/ui/register
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输入你的邮箱和密码，点击注册。&lt;strong&gt;第一个注册的用户自动成为管理员&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;注册后就可以登录管理后台了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://comment.example.com/ui
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;管理后台长这样：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;左侧是评论列表，可以审核、回复、删除、标星&lt;/li&gt;
&lt;li&gt;右上角可以配置站点信息、导入导出数据&lt;/li&gt;
&lt;li&gt;支持多用户管理&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.1 服务器目录结构&lt;/h3&gt;
&lt;p&gt;部署完成后，服务器上的文件布局：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/opt/waline/
├── docker-compose.yml         # 容器配置（如果用 Compose）
└── data/
    └── waline.sqlite           # 评论数据（挂了 volume，删容器不丢）

/etc/nginx/sites-available/waline   # Nginx 站点配置
/etc/nginx/sites-enabled/waline     # 已启用的 symlink
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果用 &lt;code&gt;docker run&lt;/code&gt; 而非 Compose，则没有 &lt;code&gt;docker-compose.yml&lt;/code&gt;，容器直接通过 &lt;code&gt;docker run&lt;/code&gt; 命令管理。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第七步：前端接入&lt;/h2&gt;
&lt;p&gt;现在后端已经跑起来了，最后一步是把评论框嵌入你的博客页面。&lt;/p&gt;
&lt;h3&gt;7.1 基础接入&lt;/h3&gt;
&lt;p&gt;在你想显示评论框的位置（通常是文章页底部）加入以下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!- 评论框会渲染到这个 div 里 -&amp;gt;
&amp;lt;div id=&amp;quot;waline&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;
  import { init } from &amp;#39;https://unpkg.com/@waline/client@v3/dist/waline.mjs&amp;#39;;

  init({
    el: &amp;#39;#waline&amp;#39;,                                 // 评论框要挂载的 DOM 元素
    serverURL: &amp;#39;https://comment.example.com&amp;#39;,       // ← 替换：你的 Waline 后端地址
    dark: &amp;#39;auto&amp;#39;,                                   // 自动跟随系统暗黑模式
    lang: &amp;#39;zh-CN&amp;#39;,                                  // 界面语言
    wordLimit: 500,                                 // 评论字数上限
    pageview: true,                                 // 开启文章浏览量统计
    emoji: [
      &amp;#39;https://unpkg.com/@waline/emojis@1.2.0/qq&amp;#39;,   // QQ 表情包
      &amp;#39;https://unpkg.com/@waline/emojis@1.2.0/tieba&amp;#39;, // 贴吧表情包
    ],
  });
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;init()&lt;/code&gt; 返回一个 Waline 实例，可以调用 &lt;code&gt;.update()&lt;/code&gt; 更新配置，或 &lt;code&gt;.destroy()&lt;/code&gt; 销毁。&lt;/p&gt;
&lt;h3&gt;7.2 浏览量统计（可选）&lt;/h3&gt;
&lt;p&gt;如果你想要文章阅读计数，在页面中放一个元素，然后开启 &lt;code&gt;pageview: true&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!- data-path 填当前页面的路径，用于区分不同文章 -&amp;gt;
阅读量：&amp;lt;span class=&amp;quot;waline-pageview-count&amp;quot; data-path=&amp;quot;/posts/hello-world&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; 次
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Waline 会自动查找页面上所有 &lt;code&gt;.waline-pageview-count&lt;/code&gt; 元素并填入计数。&lt;/p&gt;
&lt;h3&gt;7.3 常见博客框架的接入方式&lt;/h3&gt;
&lt;p&gt;不同框架的接入位置略有不同：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;框架&lt;/th&gt;
&lt;th&gt;在哪加代码&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Hugo&lt;/td&gt;
&lt;td&gt;&lt;code&gt;layouts/_default/single.html&lt;/code&gt; 或主题的评论区 partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hexo&lt;/td&gt;
&lt;td&gt;主题的 &lt;code&gt;layout/_partial/comments.ejs&lt;/code&gt; 或文章模板&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jekyll&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_layouts/post.html&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VuePress&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.vuepress/config.js&lt;/code&gt; 的 &lt;code&gt;plugins.comment&lt;/code&gt; 配置&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;纯 HTML&lt;/td&gt;
&lt;td&gt;每篇文章底部直接贴&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;各种框架的详细配置参考 &lt;a href=&quot;https://waline.js.org/guide/get-started/client.html&quot;&gt;Waline 官方文档 - 客户端接入&lt;/a&gt;。&lt;/p&gt;
&lt;h3&gt;7.4 验收&lt;/h3&gt;
&lt;p&gt;部署完前端后，打开你的博客，进入一篇文章，拉到最底：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;能看到评论输入框 ✓&lt;/li&gt;
&lt;li&gt;输入昵称、邮箱、内容，点发送 ✓&lt;/li&gt;
&lt;li&gt;评论成功出现在下方 ✓&lt;/li&gt;
&lt;li&gt;登录管理后台能看到这条评论 ✓&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;恭喜，Waline 已经完整跑起来了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;第八步：邮件通知（可选但建议配）&lt;/h2&gt;
&lt;p&gt;不配邮件的话，有人评论你不知道。配了 SMTP 后，新评论会自动发邮件通知你。&lt;/p&gt;
&lt;h3&gt;QQ 邮箱配置示例&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;登录 QQ 邮箱 → 设置 → 账户 → POP3/IMAP/SMTP 服务&lt;/li&gt;
&lt;li&gt;开启「SMTP 服务」，按提示发送短信验证&lt;/li&gt;
&lt;li&gt;获得一个 16 位的&lt;strong&gt;授权码&lt;/strong&gt;（不是 QQ 密码！）&lt;/li&gt;
&lt;li&gt;把这个授权码填入 &lt;code&gt;SMTP_PASS&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;添加以下环境变量（如果用的 &lt;code&gt;docker run&lt;/code&gt;，加 &lt;code&gt;-e&lt;/code&gt;；如果用 Compose，加在 &lt;code&gt;environment&lt;/code&gt; 下）：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;-e SMTP_SERVICE=QQ \
-e SMTP_USER=你的QQ号@qq.com \
-e SMTP_PASS=授权码 \
-e SENDER_NAME=Waline通知 \
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启容器生效：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker restart waline
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;163 邮箱同理，去邮箱设置里开 SMTP 拿授权码，&lt;code&gt;SMTP_SERVICE&lt;/code&gt; 填 &lt;code&gt;163&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;日常维护&lt;/h2&gt;
&lt;h3&gt;升级 Waline&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Docker Compose 方式
cd /opt/waline
docker compose pull        # 拉最新镜像
docker compose up -d       # 重建容器

# docker run 方式
docker pull lizheming/waline:latest
docker stop waline &amp;amp;&amp;amp; docker rm waline
# 重新执行第四步的 docker run 命令
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;数据在挂载目录里，删容器不会丢。&lt;/p&gt;
&lt;h3&gt;备份&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# SQLite 数据就是一个文件（或几个），直接复制目录
cp -r /opt/waline/data /opt/backup/waline-$(date +%F)

# 建议加个 crontab 每天自动备份
# 0 3 * * * cp -r /opt/waline/data /opt/backup/waline-$(date +\%F)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;恢复&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 把备份目录覆盖回数据目录，重启容器即可
cp -r /opt/backup/waline-2026-01-01 /opt/waline/data
docker restart waline
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查看日志&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker logs waline -tail 50       # 最近 50 行
docker logs waline -f              # 实时跟踪（Ctrl+C 退出）
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;进阶：邮箱提醒失败排查&lt;/h2&gt;
&lt;p&gt;如果你配了 SMTP 但收不到通知邮件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 进入容器内部，手动测试邮件发送
docker exec -it waline sh
# 进容器后用 curl 测试发信（Waline 内部 ThinkJS 框架自带测试端点）
curl -X POST http://127.0.0.1:8360/comment \
  -H &amp;#39;Content-Type: application/json&amp;#39; \
  -d &amp;#39;{&amp;quot;user_id&amp;quot;:&amp;quot;test&amp;quot;,&amp;quot;comment&amp;quot;:&amp;quot;测试评论&amp;quot;}&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;收到评论后，看 Waline 日志有没有 SMTP 相关报错：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker logs waline | grep -i smtp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;常见原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;SMTP_PASS&lt;/code&gt; 填了邮箱密码而不是授权码（QQ/163 必须用授权码）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SMTP 端口不对（QQ 是 465，163 也是 465，Gmail 是 587）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;服务器防火墙屏蔽了 SMTP 端口（出站一般不拦，但如果拦了加白名单）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;实战踩坑记录&lt;/h2&gt;
&lt;p&gt;以下问题来自本次部署的真实踩坑，建议先过一遍。&lt;/p&gt;
&lt;h3&gt;踩坑 1：bash 命令 &lt;code&gt;\&lt;/code&gt; 后面不能跟注释&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# ❌ 错误：注释会让续行断裂，docker 只执行了第一行
docker run -d \
  -name waline \
  -e SITE_NAME=xxx \   # ← 这个注释会破坏续行

# ✓ 正确：注释单独写，\ 必须是行末最后一个字符
#   以下是替换说明:
#     SITE_NAME: 你的网站名称
docker run -d \
  -name waline \
  -e SITE_NAME=xxx
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;踩坑 2：SITE_NAME 含 &lt;code&gt;&amp;amp;&lt;/code&gt; 必须加引号&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;SITE_NAME=Code &amp;amp; Flower&lt;/code&gt; 在 bash 里 &lt;code&gt;&amp;amp;&lt;/code&gt; 会被解释为「放后台运行」，命令就断了。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# ❌ 错误
-e SITE_NAME=Code &amp;amp; Flower

# ✓ 正确：双引号包住整个 key=value
-e &amp;quot;SITE_NAME=Code &amp;amp; Flower&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;YAML（Compose）里同样需要：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;# ✓ 正确
SITE_NAME: &amp;quot;Code &amp;amp; Flower&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;踩坑 3：SECURE_DOMAINS 必须包含管理后台域名&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;SECURE_DOMAINS&lt;/code&gt; 控制的是 Waline API 允许哪些域名跨域访问。&lt;strong&gt;管理后台也在 &lt;code&gt;comment.xxx.com&lt;/code&gt; 上，不加上去的话注册/登录 API 返回 403。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# ❌ 错误：只写了博客域名
-e SECURE_DOMAINS=blog.example.com

# ✓ 正确：博客域名 + 管理后台域名，逗号分隔
-e SECURE_DOMAINS=blog.example.com,comment.example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;踩坑 4：SQLite 文件需要预初始化&lt;/h3&gt;
&lt;p&gt;直接删掉 &lt;code&gt;waline.sqlite&lt;/code&gt; 后重启容器，Waline 会创建一个 0 字节的空文件，&lt;strong&gt;不会自动建表&lt;/strong&gt;，注册时报 &lt;code&gt;no such table: wl_Users&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;解决办法 —— 下载官方预初始化的数据库文件：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# GitHub 直连失败用镜像
wget -O /opt/waline/data/waline.sqlite \
  https://ghproxy.net/https://raw.githubusercontent.com/walinejs/waline/main/assets/waline.sqlite
docker restart waline
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你已经注册了用户再删数据库，数据会丢失。升级或迁移不要直接删文件，用管理后台的导出/导入功能。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;踩坑 5：社交登录（GitHub/QQ）在国内可能 403&lt;/h3&gt;
&lt;p&gt;Waline 的第三方登录走 &lt;code&gt;oauth.lithub.cc&lt;/code&gt; 代理，这个服务在国内访问不稳定，经常返回 403。&lt;strong&gt;这不是你配置的问题&lt;/strong&gt;，是代理服务本身的问题。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;用邮箱注册就够了&lt;/strong&gt;，去 &lt;code&gt;/ui/register&lt;/code&gt; 填邮箱和密码。访客评论也可以用昵称 + 邮箱，不需要登录。&lt;/p&gt;
&lt;h3&gt;账号归属&lt;/h3&gt;
&lt;p&gt;Waline 跑在你自己的服务器上，所有用户注册的账号、评论数据都存在你的 &lt;code&gt;/opt/waline/data/waline.sqlite&lt;/code&gt; 里。&lt;strong&gt;账号只在你的网站有效&lt;/strong&gt;，去别人家用 Waline 的网站需要重新注册——这不是 SaaS 平台。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;h3&gt;评论一直转圈、发送失败&lt;/h3&gt;
&lt;p&gt;99% 是 &lt;code&gt;serverURL&lt;/code&gt; 填错了或者 HTTPS 证书有问题。&lt;/p&gt;
&lt;p&gt;排查步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;浏览器 F12 → Console → 看报错信息&lt;/li&gt;
&lt;li&gt;如果是 &lt;code&gt;Failed to fetch&lt;/code&gt;：检查 &lt;code&gt;serverURL&lt;/code&gt; 是否以 &lt;code&gt;https://&lt;/code&gt; 开头&lt;/li&gt;
&lt;li&gt;直接浏览器访问 &lt;code&gt;https://你的子域名&lt;/code&gt;，看是否返回 JSON&lt;/li&gt;
&lt;li&gt;检查证书是否过期：&lt;code&gt;curl -I https://你的子域名&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;关闭垃圾评论过滤，加快评论速度&lt;/h3&gt;
&lt;p&gt;设置 &lt;code&gt;AKISMET_KEY=false&lt;/code&gt; 关闭 Akismet 检测：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# docker run 方式加这行
-e AKISMET_KEY=false

# 如果已经跑起来了
docker stop waline &amp;amp;&amp;amp; docker rm waline
# 重新 docker run 时加上上面的 -e AKISMET_KEY=false
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Failed to fetch 错误&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;POST https://comment.example.com/comment net::ERR_FAILED
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原因排查：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Waline 没在运行&lt;/strong&gt;：&lt;code&gt;docker ps | grep waline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nginx 没反代对&lt;/strong&gt;：&lt;code&gt;curl http://127.0.0.1:8360&lt;/code&gt; 看看本地能不能访问&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTTPS 证书过期&lt;/strong&gt;：&lt;code&gt;curl -Iv https://comment.example.com&lt;/code&gt; 看证书信息&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;域名 DNS 没解析&lt;/strong&gt;：&lt;code&gt;nslookup comment.example.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;防火墙没开放&lt;/strong&gt;：443 端口要在安全组里放行&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;管理后台打不开 / 404&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;确认 URL 路径是 &lt;code&gt;/ui&lt;/code&gt;，不是 &lt;code&gt;/ui/&lt;/code&gt;（末尾不加斜杠）&lt;/li&gt;
&lt;li&gt;首次访问应该去 &lt;code&gt;/ui/register&lt;/code&gt; 注册&lt;/li&gt;
&lt;li&gt;如果 &lt;code&gt;/ui&lt;/code&gt; 返回页面但是空白：检查浏览器是否有广告拦截插件，暂时关掉&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Docker pull 太慢或失败&lt;/h3&gt;
&lt;p&gt;国内服务器拉 Docker 镜像可能会很慢。配镜像加速：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo tee /etc/docker/daemon.json &amp;lt;&amp;lt;-&amp;#39;EOF&amp;#39;
{
  &amp;quot;registry-mirrors&amp;quot;: [
    &amp;quot;https://docker.xuanyuan.me&amp;quot;,
    &amp;quot;https://docker.1ms.run&amp;quot;,
    &amp;quot;https://docker.m.daocloud.io&amp;quot;
  ]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;LeanCloud 用户迁移到自建&lt;/h3&gt;
&lt;p&gt;如果你以前用 LeanCloud 版 Waline，现在想迁移到自建 SQLite：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;登录 LeanCloud → 找到 Waline 应用 → 导出数据（JSON 或 CSV）&lt;/li&gt;
&lt;li&gt;自建 Waline 跑起来后，在管理后台 &lt;code&gt;/ui&lt;/code&gt; 里有「导入」功能&lt;/li&gt;
&lt;li&gt;把导出的数据导入即可&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;升级 @waline/client v2 → v3&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;@waline/client&lt;/code&gt; v3 的初始化方式从 &lt;code&gt;Waline.init()&lt;/code&gt; 变成了 &lt;code&gt;init()&lt;/code&gt;，API 不兼容。如果你的博客还在用 v2 的 CDN 地址，升级时注意同步改前端代码。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://waline.js.org&quot;&gt;Waline 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://waline.js.org/reference/server/env.html&quot;&gt;服务端环境变量完整列表&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://waline.js.org/reference/client/props.html&quot;&gt;客户端配置参考&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eff-certbot.readthedocs.io&quot;&gt;Certbot 文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.docker.com/engine/daemon/daemon.json/&quot;&gt;Docker 镜像加速&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate></item><item><title>如何部署 Twikoo 评论到自己的服务器 (保姆级教程 2026)</title><link>https://blog.nywerya.xyz/blog/deploy-twikoo-self-host/</link><guid isPermaLink="true">https://blog.nywerya.xyz/blog/deploy-twikoo-self-host/</guid><description>从零部署 Twikoo 评论系统到自有服务器，涵盖 MongoDB、Docker、 Nginx 反代、前端接入、管理后台全流程。</description><content:encoded>&lt;blockquote&gt;This rendering was automatically generated by Ryuchan Feed and may have formatting issues. For the best experience, please visit: &lt;a href=&quot;https://blog.nywerya.xyz/blog/deploy-twikoo-self-host/&quot;&gt;https://blog.nywerya.xyz/blog/deploy-twikoo-self-host/&lt;/a&gt;&lt;/blockquote&gt; &lt;h1&gt;如何部署 Twikoo 评论到自己的服务器 (保姆级教程 2026)&lt;/h1&gt;
&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt; &lt;a href=&quot;https://twikoo.js.org&quot;&gt;Twikoo&lt;/a&gt; 是一个自托管的静态博客评论系统，后端用 Node.js，数据库用 MongoDB。前端通过一个 JS 脚本嵌入页面。&lt;/p&gt;
&lt;h2&gt;需要什么&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一台能装 Docker 的服务器（1c1g 够用）&lt;/li&gt;
&lt;li&gt;MongoDB（可以同一台服务器上跑，也可以用 MongoDB Atlas 免费版）&lt;/li&gt;
&lt;li&gt;一个域名（前端 HTTPS 站点才能正常请求）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;部署步骤&lt;/h2&gt;
&lt;h3&gt;1. MongoDB&lt;/h3&gt;
&lt;p&gt;如果已有 MongoDB 可跳过。没有的话，在服务器上起一个：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 拉镜像
docker pull mongo:7

# 运行，挂载数据目录
docker run -d -name mongo \
  -v /opt/mongo/data:/data/db \
  -p 127.0.0.1:27017:27017 \
  mongo:7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者用 MongoDB Atlas 免费云（512MB 够用了），注册后获取连接字符串。&lt;/p&gt;
&lt;h3&gt;2. 运行 Twikoo&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 拉镜像
docker pull imaegoo/twikoo

# 运行
docker run -d -name twikoo \
  -restart=unless-stopped \
  -e TWIKOO_THROTTLE=1000 \
  -e MONGODB_URI=mongodb://mongo:27017/twikoo \
  -link mongo \
  -p 127.0.0.1:8080:8080 \
  imaegoo/twikoo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 MongoDB 在一台机器上且没用 &lt;code&gt;-link&lt;/code&gt;，直接用宿主机 IP 或者把 &lt;code&gt;-link&lt;/code&gt; 换成 &lt;code&gt;-network host&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 用宿主机网络（Mongo 也同一台机器）
docker run -d -name twikoo \
  -restart=unless-stopped \
  -network host \
  -e TWIKOO_THROTTLE=1000 \
  -e MONGODB_URI=mongodb://127.0.0.1:27017/twikoo \
  imaegoo/twikoo
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用 MongoDB Atlas 的话，&lt;code&gt;MONGODB_URI&lt;/code&gt; 换成 Atlas 连接字符串。&lt;/p&gt;
&lt;h3&gt;3. Nginx 反代&lt;/h3&gt;
&lt;p&gt;假设你的域名是 &lt;code&gt;comment.example.com&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    listen 443 ssl http2;
    server_name comment.example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection &amp;quot;upgrade&amp;quot;;
        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;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 验证并重载
sudo nginx -t &amp;amp;&amp;amp; sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. 前端接入&lt;/h3&gt;
&lt;p&gt;在博客页面引入 Twikoo JS，挂载到指定 DOM 元素：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div id=&amp;quot;twikoo-comments&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/twikoo@1.6.41/dist/twikoo.all.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
twikoo.init({
  envId: &amp;#39;https://comment.example.com&amp;#39;,
  el: &amp;#39;#twikoo-comments&amp;#39;,
  // 可选：语言、表情CDN、管理员鉴权等
  lang: &amp;#39;zh-CN&amp;#39;,
});
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5. 管理面板&lt;/h3&gt;
&lt;p&gt;浏览器打开 &lt;code&gt;https://comment.example.com/#/login&lt;/code&gt;，第一次会设置管理员密码。之后可以在这里管理评论（审核、删除、回复等）。&lt;/p&gt;
&lt;h2&gt;常用环境变量&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;变量&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;默认值&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TWIKOO_THROTTLE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;同一 IP 评论间隔（毫秒）&lt;/td&gt;
&lt;td&gt;60000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TWIKOO_IP_HEADERS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;获取真实 IP 的请求头，逗号分隔&lt;/td&gt;
&lt;td&gt;&lt;code&gt;x-forwarded-for&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MONGODB_URI&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MongoDB 连接字符串&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;评论发送失败 / 一直转圈&lt;/strong&gt; → 检查浏览器控制台，大概率是 HTTPS 证书问题或反代没配 WebSocket 升级头。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;管理面板进不去&lt;/strong&gt; → 确保 URL 以 &lt;code&gt;/#/login&lt;/code&gt; 结尾，域名都能正常访问。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WordPress 能不能用&lt;/strong&gt; → 只适合静态博客，WordPress 不需要这个。&lt;/p&gt;
&lt;h2&gt;备份&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 评论存在 MongoDB 里，备份数据库即可
docker exec mongo mongodump -archive=/tmp/twikoo-$(date +%F).archive
docker cp mongo:/tmp/twikoo-$(date +%F).archive .
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><dc:creator>Nywerya&apos;s blog</dc:creator><pubDate>Fri, 29 May 2026 00:00:00 GMT</pubDate></item></channel></rss>