作为一个把自己折腾到深夜的树莓派玩家,我太清楚这种感受了:树莓派4B上的服务全都跑通了,Docker、Nginx、各种自用应用都稳稳的,可一旦想把服务正式开放出去给朋友或者自己随时访问,问题就来了——端口号太长记不住、家庭宽带的IP一重启路由器就变、更麻烦的是,如果你在国内的环境里想把网站正规地跑起来,80和443端口不是你想开就能开。
这一篇是整个“树莓派4B自建网页服务器指南”的第七篇,也是主线部分的收尾篇。前面几篇我们把系统、内网服务、HTTPS证书、域名解析都弄完了,这一篇解决的是从“内网玩具”到“公网生产可用”的最后一公里:用一台最便宜的阿里云ECS做反向代理,把流量安全地转发到家里的树莓派上。这样你最终访问的地址是干干净净的域名,不带任何端口号,同时服务的真实入口被藏在代理后面,还顺手把备案这个绕不开的问题一并解决了。
这篇文章适合已经有一台跑着服务的树莓派4B、想把它变成真正对外可用Web服务器的朋友。哪怕你对Nginx不太熟,跟着配置一步步走,也能在一小时内搭完整个链路。咱们直接开始。
1. 整体方案设计:为什么ECS反向代理是“生产级”的最优解
1.1 不带端口、保备案、藏入口,一个入口解决三件事
先说用户访问的真实体验。在没有ECS反向代理之前,如果你想把树莓派上的WordPress或者某个自建应用分享出去,通常会这么做:路由器上做端口映射,把公网的某个端口(比如8080)转发到树莓派的80端口,然后朋友访问的时候就得输入“http://你的IP:8080”。这事儿内网穿透或者路由器映射不是不能干,但问题很明显:
第一个问题是端口太丑。带端口号的URL,既难记,分享给别人的时候还容易漏掉冒号和数字。第二个问题是服务暴露面太大。一旦公网端口被扫描器发现,你的服务就会直接暴露在各种探测和攻击之下,而家里的路由器日志里每天全是扫描记录。第三个问题就是备案。在国内的云服务商那里,凡是绑定域名并通过80/443端口对外提供Web服务,域名和服务器都必须完成ICP备案,否则运营商和云厂商都会拦截访问,这是硬性要求。
ECS反向代理把这几个问题一次解决:用户请求先到阿里云ECS的80/443端口,ECS上的Nginx再把请求转发给家里的树莓派。用户地址栏始终是域名,不露端口;树莓派对外部网络无感知,真正被扫描器看到的只有ECS这台“跳板机”;而ECS作为正规云服务器,域名在它上面完成备案后,整条访问链路就是合规的。用一个低配ECS换来正式的生产入口,这笔账怎么算都划算。
1.2 家庭网络环境自检:有公网IP和无公网IP两条路线
做反向代理之前,先搞清楚自己家宽带的网络环境,这决定了ECS到树莓派之间走的是“直连”还是“隧道”。
查公网IP的方法很简单:登录路由器后台,看WAN口IP,如果你的WAN口IP和百度搜索“IP地址”显示的公网IP一模一样,说明你拿到了公网IPv4地址;如果不一样,说明运营商给你分配的是内网NAT地址,你被套了一层。另外还有一种情况是IPv6公网可用,但反向代理走IPv4更省事,所以这里先默认看IPv4。
有公网IP的情况下,ECS可以直接通过Nginx的proxy_pass指向“你家公网IP:内网映射端口”,再在路由器上把对应端口转发到树莓派,路径短、延迟低、配置简单。没有公网IP的话,就需要一条内网穿透隧道,推荐用frp:在ECS上跑一个frps服务端,树莓派上跑frpc客户端,frpc主动连上ECS的frp端口,ECS的Nginx再把这路隧道当作本机端口来转发,等于在ECS和树莓派之间建立了一条加密的私有通道,公网IP有没有都不影响。
两条路线各有优势,我个人的建议是:有公网IP就直连,省掉frp这一层,出问题也好排查;没有公网IP也别慌,frp很成熟,稳定性也够用,本文会把两条路线的配置都讲清楚,你按自己的情况选一条走就行。
1.3 为什么这一层不用CDN、不用云防火墙
有人可能会问:既然要保备案,为什么不干脆直接在阿里云上买一台带公网带宽的轻量服务器,把服务整个迁上去,还要费劲反向代理回树莓派?
这里有一个很实际的取舍。树莓派在家的价值在于它是“私有”的:存储空间可以自己扩,数据完全握在自己手里,跑点什么小脚本、定时任务、内网服务都不受限。而云服务器虽然稳定,但存储带宽都要持续花钱,随便一台带个几十G数据盘和一定带宽的ECS,一年的费用就能买好几块树莓派了。反向代理这种方式相当于“云上只是门卫,家才是真正的仓库”,门卫便宜,仓库自由。
至于CDN,主要用来加速静态资源分发,不适合作为动态请求反向代理的主入口;云WAF则属于增强安全能力,不解决端口暴露和备案问题。所以对个人生产级自建站来说,ECS+Nginx反向代理就是最贴合成本与技术需求的方案——这也是为什么这方案几乎成了自建站玩家默认的“主线终章”配置。
2. 阿里云ECS与域名基础配置
2.1 实例选型:1核2G跑Nginx绰绰有余
反向代理本身只是一个流量转发层,对CPU和内存的要求极低。阿里云ECS我实测下来,最低配的1核2G实例就完全够用了,甚至你选用经济型或者轻量应用服务器也没问题。为什么强调2G内存?因为Nginx虽然本身很轻,但如果你后面加上fail2ban、监控脚本、日志切割等辅助工具,内存充裕一点的机器用起来会舒服得多。
带宽方面,我建议按实际需求买。反向代理会承载所有的公网流量,如果你是给自用博客站做代理,1Mbps到3Mbps够看文字了;如果你想分享图片、视频或者软件包,3Mbps以上会更从容。阿里云ECS的特点是带宽可以按天升级,遇到推广期也能临时提升,所以初期不用买太高。
系统镜像我推荐Alibaba Cloud Linux 3或者Ubuntu 22.04 LTS。Alibaba Cloud Linux在阿里云上优化做得好,yum/dnf装软件很方便;Ubuntu对我来说更熟悉,文档也多,出了问题好查。本文下面的命令以Ubuntu/Debian系为例,CentOS系把apt换成yum/dnf即可。
2.2 安全组规则:只放行必要的端口
安全组是阿里云ECS的第一道防火墙,这个配置如果做不好,后面Nginx配得再完美也是白搭。我的原则是“默认拒绝,按需放行”:入方向只放行三个端口,其他全部关掉。
| 端口 | 协议 | 授权对象 | 用途 |
|---|---|---|---|
| 22 | TCP | 你的家庭/常用IP(或0.0.0.0/0但建议限定) | SSH远程管理ECS |
| 80 | TCP | 0.0.0.0/0 | HTTP访问入口 |
| 443 | TCP | 0.0.0.0/0 | HTTPS访问入口 |
这里有个小细节,SSH的22端口我强烈建议把授权对象限定为你自己的固定IP,或者至少开启ECS的安全加固功能。因为切忌把22端口完全暴露给整个互联网,否则会遭到大批量的暴力破解尝试,我见过太多因为22端口裸奔然后被种挖矿木马的案例了。
另外特别注意一点:frp隧道用的端口(比如7000)不要加到安全组里,因为frp是树莓派主动连到ECS的,这个连接方向是“出方向”,不属于入方向的端口放行范围。你只需要在ECS本地防火墙(如果有的话)放行frp监听端口,或确认阿里云安全组不影响出方向连接即可。这个细节很多人会搞混,地址一配错就连不上。
2.3 域名解析与ICP备案检查清单
接下来是域名。如果你打算把网站正式跑起来,请务必在阿里云或国内其它有资质的服务商处完成ICP备案。我把备案相关的事项列成一份简单的检查清单,照着做就不会卡壳:
- 域名注册信息必须真实,备案需要提交身份证、手机号、核验照片等资料。
- 如果域名已经解析到其他境外服务器,先将解析记录暂时调整或删除,否则备案审核可能会因为“域名已在使用”而驳回。
- 备案过程中,阿里云会提供一个备案服务号,需要绑定在你的ECS实例上,一台ECS默认可以生成5个服务号,足够折腾。
- 备案审核时间大约7到20天不等,期间网站不能通过80/443端口访问,这是硬性要求。
备案这件事,心态上把它当作一次“网络实名登记”就好,资料齐全通过率很高。等备案通过后,你就可以大胆地在域名解析里添加A记录,把域名指向ECS的IP地址了。
解析记录建议至少添加两条:一条是根域名(example.com)的A记录,一条是www子域名(www.example.com)的A记录,都指向ECS的公网IP。如果以后想加别的子域名,再按需添加,解析生效时间一般几分钟到半小时。
3. Nginx反向代理核心配置与参数详解
3.1 在ECS上安装Nginx
ECS上安装Nginx很简单。Ubuntu系统先更新软件源,然后直接装:
sudo apt update sudo apt install -y nginx sudo systemctl enable nginx sudo systemctl start nginx装完先别急着改配置,先用浏览器访问一下ECS的公网IP,如果看到Nginx默认欢迎页,说明80端口的链路已经通了,后面所有工作都在这个基础上做。
我习惯在改配置前先复制一份默认配置留底:
sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak这样万一配置改错了还能迅速恢复。接下来就是编辑这个配置文件,把默认的server块内容替换成我们的反向代理配置。
3.2 核心反向代理配置:一行proxy_pass解决“隐端口”
先以最简单的情况为例:树莓派上已经有一个Nginx服务跑在8080端口,ECS通过公网访问树莓派的某个地址。我们先把ECS上的Nginx配置成HTTP反向代理:
server { listen 80; server_name www.example.com; location / { proxy_pass http://你的树莓派地址:8080; 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_pass。它把从公网80端口进来的请求,原封不动地转发给http://树莓派地址:8080。用户访问http://www.example.com的时候,地址栏里看不到8080端口,服务实际跑在8080也完全被隐藏了,这就是“隐端口”的核心原理。
有公网IP的情况下,树莓派地址就直接填你家宽带的公网IP,同时记得在路由器上把8080端口的入站转发指到树莓派的内网IP上。没有公网IP的情况下,这里要填的是一个特殊地址——frp隧道暴露在本机的端口,比如http://127.0.0.1:8080,后面第四节会详细讲。
关于proxy_set_header这四行,很多人会省略,但真不能偷懒。Host字段如果不重写,后端树莓派上的其他服务(比如虚拟主机)就无法正确判断域名;X-Real-IP和X-Forwarded-For是把用户真实IP传给后端,否则树莓派上看到的全是ECS的IP,日志里全是代理地址,排查问题会想哭;X-Forwarded-Proto则是让后端知道用户用的是HTTP还是HTTPS,做跳转和防盗链的时候很关键。
3.3 HTTPS配置:证书申请与自动续期
HTTP裸奔肯定不行,公网上数据明文传输等于把密码和隐私拱手送人。阿里云ECS可以直接申请免费SSL证书,每个月能领20张单域名证书,配合域名在一个号上。如果你用的是Let's Encrypt,certbot工具会更自动化,我以certbot为例讲实现:
sudo apt install -y certbot python3-certbot-nginx sudo certbot --nginx -d www.example.com -d example.comcertbot会自动修改Nginx配置,把证书路径填进去,并添加80跳443的重定向规则。你在终端里跟着提示输入邮箱、同意协议就行。成功后,certbot还会自动添加一个定时任务,证书到期前自动续期,基本不用管。
证书配好之后,完整的HTTPS反向代理配置大概是这个样子:
server { listen 443 ssl http2; server_name www.example.com; ssl_certificate /etc/letsencrypt/live/www.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; location / { proxy_pass http://你的树莓派地址:8080; 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_read_timeout 300s; proxy_send_timeout 300s; } } server { listen 80; server_name www.example.com example.com; return 301 https://$host$request_uri; }配置完成后,依次执行sudo nginx -t检查语法,再sudo systemctl reload nginx重新加载配置,HTTPS链路就通了。这里提一个我踩过坑的地方:proxy_read_timeout默认是60秒,如果你的站点有后台导出、长轮询、大文件上传这类请求,很容易到60秒就被掐断,先把超时时间拉到300秒,后面再按需调。
3.4 传递WebSocket长连接:反向代理最容易忽略的配置
如果你打算在树莓派上跑一些带实时功能的应用——比如在线聊天、终端WebSSH、监控面板——那么它们大概率用了WebSocket协议。WebSocket和普通HTTP请求不一样,它需要先进行一次HTTP升级握手,再建立长连接。如果Nginx不特别处理,升级请求会被挡下来,前端就会报连接失败。
在location块里额外加上这两行,就能放行WebSocket升级:
proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";同时把proxy_read_timeout调大一些,比如3600秒,防止长时间没有数据交换时被代理层切断。我最初部署一个实时日志面板的时候,就是因为漏了这两行,页面一直显示“WebSocket连接失败”,排查了很久才意识到是Nginx代理层拦了协议升级,加上之后问题立刻消失。如果你确认后端服务本身支持WebSocket,那大概率就是代理层没放行。
4. 内网穿透方案:无公网IP时的frp部署
4.1 frps服务端部署(ECS)
家里没有公网IP的朋友,ECS到树莓派之间就要靠frp隧道撑起来。frp的架构很清晰:ECS上跑frps(服务端),树莓派上跑frpc(客户端),客户端主动向外连接服务端,建立一条双向隧道。
先在ECS上下载frp。到frp的GitHub Release页面找到最新的linux_amd64版本(ECS如果是x86架构),然后在终端里执行:
cd /opt sudo wget https://github.com/fatedier/frp/releases/download/v0.58.1/frp_0.58.1_linux_amd64.tar.gz sudo tar -zxvf frp_0.58.1_linux_amd64.tar.gz sudo mv frp_0.58.1_linux_amd64 frpfrps服务端配置文件在/opt/frp/frps.toml(新版本是TOML格式,老版本是ini格式,如果你用的旧版就改frps.ini)。写入:
bindPort = 7000 auth.token = "这里换成你自己的超长随机字符串" # 可选:开启仪表盘方便查看在线连接 webServer.addr = "127.0.0.1" webServer.port = 7500 webServer.user = "admin" webServer.password = "改成强密码"启动frps:
sudo /opt/frp/frps -c /opt/frp/frps.toml如果想让frps开机自启,建议写一个systemd服务文件放到/etc/systemd/system/frps.service,内容大致是ExecStart指向frps和配置文件路径,然后systemctl enable frps。这一套配置完之后,ECS上的frps就在7000端口等待树莓派连接了。
4.2 frpc客户端部署(树莓派)
树莓派是ARM架构,下载frp的时候要选linux_arm64版本。解压后编辑frpc.toml:
serverAddr = "你的ECS公网IP" serverPort = 7000 auth.token = "和frps配置里的token保持一致" [[proxies]] name = "web" type = "tcp" localIP = "127.0.0.1" localPort = 8080 remotePort = 8080这段配置的含义是:让树莓派主动连接ECS的7000端口,成功后在ECS上监听8080端口,所有发送到ECS 8080端口的流量,都通过隧道转发到树莓派本机的8080端口。启动frpc之后,在ECS上curl http://127.0.0.1:8080如果能拿到树莓派上服务的响应,说明隧道通了。
这里有个重要细节:frpc连接的方向是“树莓派 → ECS”,所以不需要在ECS的安全组里专门放行7000端口的入站规则,因为连接是frpc发起的“出站”请求。这一点我实测过,如果误把7000加进安全组反而多暴露一个端口,没有必要。
4.3 把frp隧道挂到Nginx上,形成完整链路
当frp隧道打通之后,ECS上的Nginx反代目标就不再是外网地址,而是指向frp映射的本机端口:
location / { proxy_pass http://127.0.0.1:8080; 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; }此时完整的访问链路就变成了:用户浏览器 → 域名 → 阿里云ECS 80/443端口 → Nginx反向代理 → 127.0.0.1:8080(frp隧道) → 树莓派frpc → 树莓派本机服务。这条链路里,所有数据都经过了ECS这一层“跳板”,用户完全感知不到树莓派的存在,ECS的公网端口只有80和443对用户开放,其他端口全部藏在内网。
我实际用frp跑了半年的生产服务(一个家庭相册加一个个人笔记),稳定性是够的。需要注意的两点:一是frp隧道是TCP层的,如果有大量小文件请求,Nginx到frp再到树莓派会有一定的额外延迟,但个人站完全体感不到;二是frp版本迭代较快,升级的时候要同时升级ECS和树莓派两端,否则可能出现协议不兼容导致连不上。
5. 安全加固与性能调优
5.1 限制后端访问:只允许ECS的IP连进来
反向代理配好之后,这一层虽然能防住用户侧对端口的直接访问,但树莓派本身可能还暴露在家庭局域网里。如果树莓派的8080端口在路由器上做了端口映射,那外网扫描器依然有可能扫到你的8080端口,绕过了ECS这一层直接打树莓派,那就白搞了。
我的习惯是:禁止在路由器上把任何端口直接映射到树莓派,所有公网对树莓派的访问一律通过ECS转发。如果非要在路由器上开端口做调试,也请把端口映射的“允许来源IP”限定为ECS的公网IP,这样其他任何IP都无法访问树莓派。
如果你走的是frp路线,这一点基本天然满足——frp隧道是树莓派主动外连建立的,外网根本扫不到树莓派。但要注意frpc的remotePort不要和ECS上已有的端口冲突,否则frps会启动失败。
5.2 Nginx层限流与连接安全
虽然反向代理把服务入口收拢到了ECS,但公网攻击流量也会先打在这一层。我建议在Nginx上做三层基本防护:
第一层是限制单IP的请求速率。对于博客站这种读多写少的场景,在server块或者location块里配上:
limit_req_zone $binary_remote_addr zone=per_ip:10m rate=20r/s;然后在location块里引用:
limit_req zone=per_ip burst=40 nodelay;这个配置的含义是每个IP每秒最多20个请求,突发的40个请求会被排队处理,超过的直接拒绝。设成这个值正常用户不会受影响,但CC攻击和爬虫会明显被压制。
第二层是超时控制。proxy_connect_timeout建议设成10秒以内,后端树莓派或frp隧道一旦异常,Nginx能快速返回504而不是让用户一直转圈;proxy_read_timeout和proxy_send_timeout根据业务类型去调整,静态站60秒够,涉及上传下载的拉到300到600秒。
第三层是顺手安装fail2ban。如果ECS的SSH端口一直暴露在公网,暴力破解日志几乎每天都有。fail2ban会自动读取日志,同一个IP失败密码次数超过阈值就自动封禁一段时间。配置好之后,日志里那些垃圾扫描会明显少很多,ECS的系统负载也降下来了。
5.3 缓存策略与日志切割
反向代理不只是无脑转发。对于静态资源,可以在ECS的Nginx上加一层proxy_cache,减少对树莓派的重复请求。简单做法是在http块里定义缓存目录:
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=ss_cache:10m max_size=1g inactive=60m;然后在location块里对静态文件类型启用缓存:
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?)$ { proxy_pass http://127.0.0.1:8080; proxy_cache ss_cache; proxy_cache_valid 200 60m; proxy_cache_valid 404 1m; add_header X-Cache-Status $upstream_cache_status; }add_header X-Cache-Status这句是为调试用的,第一次请求看到的是MISS,第二次是HIT,说明缓存已经在生效了。静态资源被ECS缓存住之后,树莓派的CPU占用会明显下降,同时页面打开速度也会有提升。
日志方面,Nginx默认会把访问日志写到/var/log/nginx/access.log,时间久了文件会很大。最简单的方式是安装logrotate,系统一般自带,Nginx日志切割规则也会随Nginx安装自动生成,按天切分成access.log.1、access.log.2.gz这种格式,保存30天。如果想省磁盘空间,可以把access_log级别调低或者关闭静态资源的日志记录:access_log off;放在静态资源的location块里就行。
6. 常见问题与排查速查表
6.1 502 Bad Gateway:后端连接不上
首先确认是链路哪一段断了。登录到ECS上手动测试:如果Nginx配置的代理目标是http://127.0.0.1:8080,就先在ECS本地执行curl http://127.0.0.1:8080,看有没有响应。有响应说明是Nginx到frp隧道之间的问题;没响应说明frp隧道断了,去树莓派上检查frpc进程是否存活:systemctl status frpc,看看服务端日志里有没有报错。如果是公网直连的情况,则测试ECS能不能访问到你家的公网IP和路由器的映射端口,注意有些运营商封掉了常见端口的入站转发(尤其是80/8080),这种情况只能换高位端口或者改用frp。
6.2 80端口无法访问但443可以
这个现象通常不是Nginx的问题,而是备案拦截。国内云厂商和运营商会对未备案域名的80端口做阻断,403提示或ERR_CONNECTION_REFUSED都有可能出现。解决办法就是完成ICP备案并在云控制台提交备案号,没有其他捷径。另外要检查ECS安全组里80端口入方向是否放行,安全组规则改了之后一般立刻生效,但如果之前做过安全组级别的拦截,需要确认没有优先级更高的拒绝规则。
6.3 访问网站发现后端拿到的用户IP全是ECS的IP
这说明X-Forwarded-For头没有正确传递给后端。检查Nginx的proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;是否配置。配置了还不行,说明后端接收端可能没有读取这个头,比如某些框架默认只信任来自本机代理的转发头,需要在后端应用里额外配置信任代理。这个坑在Nginx反代后面接Tomcat或者Node.js时特别常见,建议把Nginx和后端应用的Proxy Trust配置一起检查。
6.4 树莓派4B红灯常亮、绿灯不亮
这个热搜词看起来很吓人,但跟反向代理没关系,属于树莓派硬件层面的故障排查范围。红灯常亮说明供电正常,绿灯不亮通常是系统启动卡死或者SD卡读取异常。解决办法依次是:检查SD卡接触是否良好、重新插拔电源、用官方烧录工具重刷系统,或者换一张卡试试。主文章里顺便提一句就当给大家避雷,毕竟树莓派挂了,反向代理配得再好也没用。
6.5 一个我建议在文本编辑时避开的坑:特殊字符替换
搜关键词时看到有人问“nginx反向代理显示字符替换”。这里要提醒一句:如果你用命令行直接编辑配置文件,注意引号和反引号。Nginx配置里如果出现中文引号或者特殊Unicode字符,nginx -t的时候会直接报错。我用vim的时候曾经粘贴网上的配置,把英文冒号粘贴成了中文全角冒号,结果服务一直起不来,排查了半天才发现是字符编码的问题。推荐用编辑器先写好配置再上传,或者粘贴后立刻执行nginx -t检查,能快速发现问题。
写在最后的实操心得
整套链路走下来,我自己的体会是:树莓派自建站最难的不是树莓派本身,而是如何把“家”和“公网”优雅地连起来。ECS反向代理恰好是这条线路里最划算、最稳定的粘合剂——它把端口隐藏、合规备案、流量转发三个需求一次解决,而且给以后扩容留足了空间。以后你想给朋友开个临时页面,只需要在Nginx里多加一个server_name和location块,后半段都不用动。
最后再分享一个生产环境的小技巧:给ECS的Nginx配置加一个维护页面。当你在家调试树莓派上的服务、需要重启Docker容器或者升级系统的时候,直接在ECS上把反代目标临时切换到一块静态HTML页面,用户访问时看到的是“网站维护中”而不是502,体验会好很多。这个操作非常轻量,只需要多写一个location块的注释切换就行。等你把主线这几篇全部跑通,你的树莓派就算真正从“开发板玩具”毕业了,接下来可以放心地在上面跑各种服务,所有的访问入口都由你说了算。