惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
小众软件
小众软件
MongoDB | Blog
MongoDB | Blog
Hugging Face - Blog
Hugging Face - Blog
Jina AI
Jina AI
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
L
LangChain Blog
大猫的无限游戏
大猫的无限游戏
量子位
A
About on SuperTechFans
G
Google Developers Blog
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
IT之家
IT之家
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园_首页
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Vercel News
Vercel News
V
Visual Studio Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
U
Unit 42
Apple Machine Learning Research
Apple Machine Learning Research

SamHou's Blog

水星冲浪日志 5 傲娇、后宫和党争——零之使魔动画点评 人生的意义是什么?——脏翅膀点评 水星冲浪日志 4 总感觉有什么不对——Anemoi 全线点评 Hexo 博客接入 Fediverse —— Hatsu + Vercel 踩坑记 水星冲浪日志 3 —— Fediverse、Arch Linux 和写作风格 DroidSpaces 在安卓上跑 Linux 发行版踩坑实录 从零开始配置 VPS —— 主机名、用户组和远程权限安全实践 奶奶都能看懂的 C++ —— 类、初始化、预处理和分离式编译 让你验证请求来自 CF —— Authenticated Origin Pulls 攻略 水星冲浪日志 2 —— PT、换域名、约稿和建站机 奶奶都能看懂的 C++ —— 函数指针、decltype、类型别名和尾置返回 奶奶都能看懂的 C# —— LINQ、 Lambda 和 IEnumerable
tailscale 进阶用法 一个服务绑一个域名并启用 SSL
Sam Hou · 2026-09-05 · via SamHou's Blog

引入

众所周知,tailscale 这个工具可以把位于防火墙/ NAT 后方的机器进行组网,并且可以自动处理打洞问题尽量获得直连的效果。

但是有一个非常大的问题,tailscale 官方不支持用你自己的域名!而且,即使使用官方的域名服务,启用 MagicDNS 时会造成系统 DNS 被抢夺,造成各种奇奇怪怪的网络问题……因此,我们通常都会这么做:

1
sudo tailscale set --accept-dns=false

来禁用 DNS 抢夺。

那么问题来了,我们难道真的束手无策吗?不能用上自己的域名,来访问内网 tailscale 的服务吗?

当然是可以的!但是,我们需要一点小 trick……

为了让你有个直观的印象,先来看看成品吧。

试试在你的浏览器里键入这个域名?

如果你试着键入这个域名,是打不开的——因为这是一个运行在我的 tailnet 上的网站,只有安装了 tailscale 并加入我的 tailnet 才能访问这个网站。你可以看到,ssl 完全正常,也没有用自签名证书,并且域名后面什么端口都不用加。

服务本身的要求

本文仅涉及「http 反向代理」的服务。ssh / rdp 这些需要用 Nginx 的 stream 进行代理,可自行探究,本文暂时不涉及

但是,在我们开始之前,必须先了解一下域名、DNS、反向代理、SSL 证书的知识。如果你已经知道这部分知识了,可以直接跳过下一章~

前置准备

放心,你不需要精通计算机网络,本文只是简略地介绍这些内容来辅助零基础的读者。比如,你不需要看下面这本书。

放心,你不需要这个

让我们开始吧!

域名和 DNS

我们先来看公网的情况。

你有没有想过,你键入一个域名之后,你的浏览器到底干了些什么?换句话说,它到底上哪里找网页递给你的?

我们当然知道网页和 Web 程序位于一台服务器上,而每台服务器都有一个 IP 地址,浏览器对这个 IP 发出请求,服务器再把网页返回给你。

而将「域名」解析为「IP 地址」的过程,就需要 DNS 服务的参与啦。当你给自己的域名设定好解析之后,全世界的 DNS 服务器就会通过逐层接力的方式,找到你设置的 IP 地址,并把它返回给请求你的网站的客户端。

反向代理

现在我们来看另外一个问题。你有台服务器,但是一台服务器上服务太多了,你想要一次开一堆网站怎么办?

当然是可行的了!

你既然都来到这里了,肯定知道服务可以绑定端口(tailscale ip + 端口,你肯定用过)。而给不同服务绑定不同端口,就可以实现在一个网站上开多个服务了。

但是我们现在绑定了域名。如果每次都要让用户在域名后面加端口,既不美观也不方便。那么,能否通过「请求的域名」来找到对应的服务呢?比如说,请求 a.example.com 就返回 1145 端口上运行的服务,请求 b.example.com 就返回1919 端口上运行的服务,请求 samhou.moe 的时候,就返回 4433 端口上运行的服务,等等。

小提示

不同二级域名也可以绑定同一个 ip。只要域名有任何字符不一样,绑定的网站就可以不同。

让我们隆重介绍——「反向代理」!

当我们键入域名,浏览器通过 DNS 请求到服务器 ip 时,会自动在请求头中带一个 Host。你可以动手用 curl 实操试试:

1
curl -v https://example.com

输出中肯定有类似这样的信息……

1
2
3
4
5
6
(省略)
> GET / HTTP/2
> Host: example.com
> User-Agent: curl/8.14.1
> Accept: */*
(省略)

curl 已经自动帮你把 Host 放到请求头里面了!

这是一个非常、非常重要的性质——

反向代理可以通过 Host 头,来判断你要访问的是服务器上的哪一个网站。

反向代理通常运行在 80/443 端口上,根据 Host 头响应请求,将这些端口上的请求路由到对应设定的服务器。

默认端口

80 443 是默认端口,因此输入域名时不需要指定它们。这就让域名变得美观了!

Nginx 是一个老牌的 web 服务器,性能非常高,通过 config 文件的方式可以配置为反向代理。

那问题来了,Nginx config 怎么配呢?

先根据官方文档装好,然后打开 /etc/nginx,看看默认的 nginx.conf 怎么写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
user  nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

#gzip on;

include /etc/nginx/conf.d/*.conf;
}

望文生义一下,默认配置文件配置了user / log 文件 / pid 等东西,而 http 块内则有最重要的一行:

1
include /etc/nginx/conf.d/*.conf;

这告诉我们,真正的配置在 conf.d 文件夹中。可以这么理解——该文件夹中的所有内容会「按原样复制」到这个配置文件中,生成最终的配置。

而关于具体的配置,在下文 tailscale 内网配置中会涉及到,现在你只需要记住配置在这个文件夹里面写就可以了。

现在嘛,我们已经扯了很久前置准备了,回到正题,tailscale 到底怎么绑定域名?

解析到 ts ip

tailscale 在你连接上 tailnet 的时候,会自动给这台设备分配一个 tailscale ip。它是一个私有地址,在安装了 ts 的设备上,访问这个 ip 时 tailscale 会接管对这个 ip 的请求,然后把请求发送到对应的机器上。

去你的域名后台增加一个解析,解析到你想要运行网站的那台设备的 tailscale ip:

添加域名解析

看到了吗?如果你用的是 cloudflare,会直接提示「保留 IP」,说明这个地址是保留的私网地址,小黄云代理会自动关闭。

就这么简单!如果你的设备上已经在某个端口运行了服务,可以直接用域名 + 端口的方式访问这台设备上的对应服务。

如果你依然打不开,可以检查一下自己的路由器是否会丢弃解析到私网地址的 DNS 请求。比如,openwrt 中的「网络 - DNS - 过滤器 - 重绑定保护」选项,如果打开了,会直接造成私网 DNS 解析为空的问题,需要关闭:

关闭重绑定保护

不用记下 ip 用域名已经是巨大的进步了。但是,问题是它没有 SSL 支持,虽然 tailscale 本身就已经有一层加密,但是某些程序/网页可能必须要求安全的 https 连接。而且,每次都要记下哪个程序对应哪个端口,是一件非常废脑子的事情。

反向代理

所以,该怎么解决上面两个问题?

不知道你还记不记得前文提到的反向代理,如果在公网上可以这么干,为什么不直接应用到 tailscale 上呢!

也就是说,访问链路是这样的:

键入域名 - 请求域名 DNS 返回 ts ip - 请求 ts ip 上的 80/443 默认端口 - 目标机器上的 nginx 根据 host 头判断该交给哪个服务 - 对应服务收到你的请求,并返回内容。对,和前文提到的公网几乎就是一个道理,tailscale 只是影响了请求 ip 这一段,当你请求 ts ip 的时候代理你的请求,其余完全不变。

这就简单了,我们刚刚提过反向代理的配置,现在来看看具体怎么写 config。

化繁为简

本文受限于篇幅,无法讲清楚 Nginx 的所有配置。因此,只会丢出一些配置文件并指出重要的配置项。因为是 tailscale 内网比较安全,你可以抄作业。

看看我这里的成品 misskey.conf,它将 samhou.de 域名绑定到了 3000 端口的服务——

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
upstream misskey {
server 127.0.0.1:3000;
}

server {
listen 80;
listen [::]:80;
server_name samhou.de;

return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name samhou.de;

ssl_certificate /etc/nginx/samhou.de.cert.pem;
ssl_certificate_key /etc/nginx/samhou.de.key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

# Misskey default.yml maxFileSize: 250MB
client_max_body_size 256m;

location / {
proxy_pass http://misskey;
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 Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 90s;
proxy_send_timeout 90s;
}
}

它做了以下事情:

  • 设定反向代理目标是 3000 端口的 misskey 服务
  • 80 端口绑定域名 server_name samhou.de;(对,这里 server_name 就是前文所述匹配 Host 头的地方),然后 301 重定向到 443 端口(自动 http 到 https 跳转)
  • [::] 表示 IPv6 也监听。
  • 443 端口 ssl 监听,设定证书 ssl_certificate /etc/nginx/samhou.de.cert.pem; ,然后反向代理 /(也就是域名根目录)到 misskey 服务 proxy_pass http://misskey;

那问题来了,我们该配置什么证书呢?

当然是和域名对应的有效证书啦。浏览器才不管你请求了哪个 ip,只要远程服务器返回的证书和域名匹配,那么就可以证明这台服务器的身份。

对,获取证书的免费方法就是 acme。

但是,由于是私网 ip,公网验证是做不到了,只能通过添加 dns 解析来验证。

这部分大家应该都会,所以这里只放出几条命令,每个人的域名解析提供商都不同,可以参阅 acme 的文档来配置。

1
2
3
4
5
6
7
export CF_Token="token"
export CF_Account_ID="ID"
acme.sh --issue --dns dns_cf --server letsencrypt -d example.com -d '*.example.com'
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/example.com.key.pem \
--fullchain-file /etc/nginx/example.com.cert.pem \
--reloadcmd "sudo service nginx reload"

证书签发之后,重启 nginx,现在访问网站就可以看到已经升级为安全连接了。

多个网站

我们已经看了一个示例网站,那么多个网站也不在话下了,只要简单地再创建一个 other_service.conf 反向代理配置文件,然后稍微复制改改就可以了(注意证书覆盖哦):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
upstream service {
server 127.0.0.1:1145;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name a.example.com;

ssl_certificate /etc/nginx/example.com.cert.pem;
ssl_certificate_key /etc/nginx/example.com.key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;

location / {
proxy_pass http://service;
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 Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 90s;
proxy_send_timeout 90s;
}
}

保存,重载 Nginx,你会发现访问 a.example.comsamhou.de 这两个网站,会分别出现 1145 和 3000 端口上运行的服务。

后记

在这篇文章里,我们把反向代理和 tailscale 结合起来用,让你可以把域名绑定到 ts 上的服务。其实这整个过程和公网并无太大区别,所以本作也可以说是「部署公网服务」的教程(笑)。

啰啰唆唆了一大堆,其实和 ts 关系不大嘛。

总之,食用愉快~