上周帮一位朋友搭了一套文件分发系统,需求很直白:公司内部有人用 FTP 上传文件,外部不同网段的人通过浏览器访问一个网址,各自只能看到和自己相关的那部分文件。标题里的关键词是“Windows 上搭建 FTP 服务器”和“Nginx 反向代理按 IP 分发网页访问”,但真正做起来,这两个环节之间还夹着不少细节。这篇文章我就把这套方案的完整搭建过程、配置文件和我踩过的坑都记录一遍,顺便也说清楚一个很多人会搞错的点:Nginx 在这里并不是去代理 FTP 协议本身。
这套方案适合谁参考呢?我猜多半是手里有一台 Windows 服务器,不想折腾 Linux,也不想买付费 NAS,想快速把“FTP 上传 + 网页按 IP 访问文件”跑起来的朋友。下面内容我会尽量写得让新手也能跟着操作,同时把原理讲透,免得哪天换了个 IP 段或者目录结构调整,你都不知道从哪里下手改。
1. 需求拆解:FTP 负责收文件,Nginx 负责发文件,两个角色不能搞混
1.1 一个容易搞混的点:Nginx 不是 FTP 协议代理
先说最核心的认知。很多人看到“通过 Nginx 反向代理方式根据 IP 在网页上打开上传到 FTP 文件”这句话,会以为 Nginx 能把ftp://的流量代理成 HTTP。实际上不是这么回事。FTP 和 HTTP 是两套完全不同的协议,Nginx 的 HTTP 模块没法直接把 FTP 数据流转成网页内容,而且现在主流浏览器对ftp://协议的支持也越来越弱,甚至有的直接移除了,让访问者去浏览器里输 FTP 地址根本不现实。
所以这个需求真正落地的方式是:FTP 服务器只管接收文件,把文件写入指定磁盘目录;Nginx 呢,负责把同一个磁盘目录当作静态网站目录暴露出去。访问者在浏览器里打开的是普通的 HTTP 地址,看到的是文件列表、点开是文件下载。FTP 在这里是“写入通道”,Nginx 是“读取通道”,两条通道都指向同一个文件夹。
你甚至可以理解为:FTP 是这个系统的仓库入库口,Nginx 是仓库的出库窗口,本来就不是一个程序在干两件事。
1.2 按 IP 区分访问者的两种方案,我选了哪种
按 IP 区分网页内容,有两种常见做法。第一种是给不同 IP 段分配不同的端口,比如 192.168.1.0/24 访问http://服务器IP:8081,10.0.0.0/8 访问http://服务器IP:8082。配置很简单,多个server块各监听一个端口就行。缺点是访问者要记端口号,端口一多你自己也容易乱。
第二种是同一端口、同一个网址,Nginx 拿到客户端 IP 后自动判断它属于哪个分组,然后把请求落到对应的 FTP 目录上。访问者不需要关心任何端口和路径,打开网址看到什么就是什么。这个体验自然得多,也更贴近“根据 IP 打开对应文件”的真实诉求。
我最后选的是第二种。实现它靠的是 Nginx 的geo模块加map,后面第四章会给出完整配置,这里只需要先理解思路:geo把 IP 翻译成一个分组名,map把分组名翻译成一个磁盘路径,最终root指令指向这个路径。
1.3 整体数据流和目录规划
在动手之前,我先规划了目录结构和账号体系。假设有部门 A、部门 B 和一个公共访问组,那磁盘目录就这样安排:
D:\ftp_root\ ├── office_a\ # 部门A上传和访问的目录 ├── office_b\ # 部门B上传和访问的目录 └── public\ # 默认分组,谁都能看的公共文件FTP 这边,我给每个部门建一个独立账号,账号只能看到并写入自己的目录。Nginx 这边,三个目录分别对应三个 IP 分组。整个流程是这样的:
FTP客户端 → FileZilla Server → 写入 D:\ftp_root\office_a 访问者浏览器 → Nginx(80端口) → 根据IP分组 → root指向 D:\ftp_root\office_a这个规划清楚之后,剩下的事情就是按部就班地搭了。千万不要跳过这一步,我见过有人装完 FTP 才发现目录设计不合理,十几个用户全堆在一个目录里,权限根本没法管。
2. Windows 上搭建 FTP 服务端:FileZilla Server 的安装与用户目录规划
2.1 为什么选 FileZilla Server,而不是 Windows 自带的 IIS FTP
Windows 本身自带 IIS,里面也带 FTP 服务,不需要额外装东西。但实际用过就会发现,IIS 的 FTP 配置界面偏老,用户隔离、虚拟目录、权限控制这些概念搞起来绕。它更适合规规矩矩的 Windows 管理员,不适合想快速解决问题的人。
我选的是 FileZilla Server,免费、轻量、界面直观。它的用户和组管理很清楚,给谁分配哪个目录、哪些权限,鼠标点几下就完成。被动模式端口也能直接设置,防火墙规则照着填就行,日志和连接状态一眼能看明白。对于“几个部门、几个账号”这种规模的应用来说,FileZilla Server 比 IIS FTP 省心得多。
安装的时候有一点需要注意,新版 FileZilla Server 分为服务端和管理界面两个部分,装完后桌面上是一个 Admin 管理界面,连接的是本机后台服务。千万别以为那个管理界面就是 FTP 服务本身,关了它服务还在跑。
2.2 用户、目录和权限设置
打开 FileZilla Server 的 Admin 管理界面,连接本机服务之后,我按下面几步配置:
- 在左侧找到 Users,添加用户。名字直接用部门名或者用途命名,比如
office_a。 - 在用户的 Mount points 里添加目录虚拟路径,把实际路径
D:\ftp_root\office_a映射成虚拟路径/。 - 设置权限。读写和目录列表权限默认可以勾上,删除和重命名权限我建议默认不给,防止有人误删共享文件。
- 如果有多个用户需要同时访问一个目录,比如部分同事要一起维护
office_a,可以在 Groups 里建一个组,把目录和权限挂在组上,再把用户拉进组里。这样后期改权限只需要动组,不用挨个用户去改。
权限这里多说一句,业务上如果只是“上传文件然后供网页下载”,FTP 账号甚至不需要删除权限。限制越多,出事故的概率越低。你永远没法保证某个同事不会手滑把整个目录拖进回收站。
我还顺手在 Speed Limits 里给每个账号限了速。内部文件上传场景不限制的话,一个人传大文件会把出网带宽占满,别的同事网页访问文件也跟着卡。限速不是防谁,是保底。
2.3 被动模式、防火墙和端口规划
FTP 有主动模式和被动模式之分,外网和内网跨网段访问,十有八九要用被动模式。被动模式的意思是:客户端连上服务器的 21 端口之后,服务器再额外开放一段数据端口,客户端主动连接这些端口传输数据。
我在 FileZilla Server 里把被动端口固定为50000-50100这一段,然后到 Windows 防火墙里放行。命令如下:
netsh advfirewall firewall add rule name="FTP Port 21" dir=in action=allow protocol=TCP localport=21 netsh advfirewall firewall add rule name="FTP Passive Ports" dir=in action=allow protocol=TCP localport=50000-50100如果这台服务器本身在 NAT 后面,也就是说外部访问要经过路由器端口映射,那被动模式的公网 IP 地址必须填路由器的公网 IP,否则客户端能连上 21 端口,但后面数据传输的时候拿到一个内网地址,连接就断了。这是 FTP 场景里最常见的“能登录但列不出目录”的原因,遇到这种问题先检查这里。
3. Nginx 在 Windows 上跑起来:静态站点与 autoindex 配置
3.1 Nginx on Windows 的准备工作
Nginx 官方提供了 Windows 版本,解压就能跑,依赖很少。我把压缩包解压到了D:\nginx-1.26.x,目录结构谁看谁知道:conf放配置,html是默认站点目录,logs放日志,nginx.exe就是主程序。
Windows 下启动 Nginx 有几个习惯要改一下。第一,不要双击nginx.exe启动,因为双击之后那个进程会一直挂在窗口里,关掉窗口服务就没了。正确姿势是在命令行切到 Nginx 目录,然后执行:
start nginx第二,改完配置不要重启进程,直接执行:
nginx.exe -t nginx.exe -s reload-t是先测试配置语法,有错会直接报出来,不至于把正在运行的服务搞挂。reload是平滑重载配置,不中断现有连接。这对我这种要反复改 IP 段的场景太重要了。
第三,Windows 版的 worker_processes 不要设太高,一般保持默认 1 就够了。Windows 下多进程模型的收益不像 Linux 那么明显,反而容易带来文件句柄和磁盘权限问题。
3.2 把 FTP 上传目录变成 HTTP 网页目录
先把最简单的静态站点跑通。我的目标是让 Nginx 直接为D:\ftp_root\office_a目录提供网页访问。
打开conf\nginx.conf,在http块里添加一个server:
server { listen 80; server_name _; charset utf-8; location / { root D:/ftp_root/office_a; autoindex on; autoindex_exact_size off; autoindex_localtime on; } }这里有三个点要注意。
root指令里的路径,Windows 上要用正斜杠D:/ftp_root/office_a,不要写成反斜杠,Nginx 解析反斜杠容易出幺蛾子。
autoindex on是开启目录列表。没有这一行,或者目录下存在index.html,访问者打开网址看到的可能是空白、403 或者被强制跳到默认首页。
autoindex_exact_size off让文件大小显示成人类可读的 KB/MB/GB,autoindex_localtime on让文件时间显示成服务器本地时间而不是 UTC。这俩配置纯属体验优化,但建议直接写上。
配置保存后先nginx.exe -t测语法,然后 reload。我在浏览器访问http://本机IP/,能看到office_a目录下的文件列表,说明静态服务已经通了。
3.3 autoindex 文件列表的中文乱码问题
第一次跑通之后我很快撞上一个问题:FTP 上传的中文文件名,在网页列表里显示为一串乱码。原因在于 Windows 本地文件名默认编码和网页的 UTF-8 编码不一致,Nginx 的 autoindex 输出页面上明确标注了charset utf-8,但文件名本身可能是 GBK 编码,两边对不上就乱了。
解决办法有两个方向。如果文件上传的人可控,最好统一要求 FTP 客户端使用 UTF-8 编码上传。FileZilla Client 里可以在站点管理器-字符集里选择“强制 UTF-8”,这样文件名落地就是 UTF-8,网页显示不会乱。
如果上传的客户端五花八门,没法强制,另一个办法是不依赖 Nginx 自动生成的列表,而是做一个简单的静态入口页面,手动维护文件名链接。小规模场景可以接受,文件一多就不太现实。所以我的建议是:能统一 UTF-8 就统一 UTF-8,这问题越早规范越省事。
4. 关键环节:用 geo 按访客 IP 把请求分到不同 FTP 目录
4.1 geo 模块:把 IP 翻译成分组名
到这里,整篇文章的核心来了。Nginx 的内置模块geo专门用来做这种“查 IP 给变量”的事情。它的基本用法很简单:
geo $area { default public; 192.168.1.0/24 office_a; 10.0.0.0/8 office_b; 203.0.113.0/24 office_a; }这段配置的意思是说,变量$area的值根据客户端 IP 来判断。来源 IP 属于192.168.1.0/24网段时,$area就是office_a;属于10.0.0.0/8时,就是office_b;其他所有没匹配上的 IP,走default,归为public。
geo块必须写在http上下文里,不能写进server。它支持精确 IP,也支持 CIDR 网段。实际业务中,我建议把所有能想到的网段都列进去,宁可多分几个组,也不要让内部人员一不小心落到 public 里看了不该看的文件。
顺便说一个常见的坑:Nginx 的geo默认只认客户端原始 IP,也就是 TCP 连接来源地址。如果你前面还有一层七层代理(比如又套了一层 Nginx),那么拿到的是代理的 IP,不是真实访客 IP。这时候需要配合set_real_ip_from和real_ip_header去恢复真实地址。如果只是单台 Nginx 直连外部请求,不用管这些。
4.2 用 map 把分组名映射成磁盘路径
geo只生成了一个分组名,Nginx 的root指令并不知道office_a对应哪个文件夹。所以这一步要用map把分组名翻译成路径:
map $area $web_root { default D:/ftp_root/public; office_a D:/ftp_root/office_a; office_b D:/ftp_root/office_b; }然后修改server里的root,直接引用这个变量:
server { listen 80; server_name _; charset utf-8; root $web_root; location / { autoindex on; autoindex_exact_size off; autoindex_localtime on; } }到这里,整个按 IP 路由的链路就通了。浏览器请求 Nginx → Nginx 通过geo判断$area→map得到$web_root→root指向 FTP 上传目录 → 返回文件列表。
这一段配置是我反复测过的。如果你在 Windows 上遇到root使用变量后路径不生效、始终返回 404 的情况,可以试一下把sendfile off;加上,Windows 版 Nginx 对 sendfile 的处理偶尔有点问题。另外一个保底方案是放弃变量 root,改用 location 里的if去设置不同的 root:
location / { if ($area = office_a) { root D:/ftp_root/office_a; } if ($area = office_b) { root D:/ftp_root/office_b; } root D:/ftp_root/public; autoindex on; }不过能用变量 root 就优先用变量 root,if方式看起来直观,但 Nginx 对if的使用有很多限制,我只把它当备用方案。
4.3 动态 IP 访问者怎么办:默认分组与兜底逻辑
“按 IP 访问”最怕遇到没有固定 IP 的访问者。比如家里宽带拨号上网,IP 天天变,你没法在geo里写死一个动态地址。
我的处理方式是:把这类访问者统一踢到default分组,也就是public目录。默认分组里放一些不敏感、人人都能看的公共文件;如果业务上确实需要给某些人单独权限,再让他们通过其他方式访问,比如单独建一个静态入口链接或者账号密码认证。
还有一个更实际的问题:同一个办公室出口,所有人共享一个公网 IP,这种情况下按 IP 区分是区分不出个人的。geo能做到的是“这个网段能看到什么”,而不是“这个人能看到什么”。如果你业务上要求的是精确到个人的权限控制,那就得在 Nginx 上叠加 Basic Auth 或者别的认证手段,光靠 IP 是不行的。这一点在方案设计阶段就要想清楚,否则做完才发现误伤一片。
5. 端到端联调与排错:从 FTP 上传到网页打开的完整验证
5.1 一次完整的端到端验证流程
配置完成不代表系统能跑,我习惯按照下面的流程做一轮端到端验证:
- 用 FileZilla Client 连接 FTP,用户
office_a,确认能登录并看到自己的目录。 - 在
office_a目录下新建一个测试文件,比如测试文件.txt,内容随意。 - 回到网页,用一台位于
192.168.1.0/24网段的机器访问 Nginx 地址http://服务器IP/,确认能看到测试文件.txt并且可以点击下载。 - 换一台不属于任何内网网段的设备,比如手机流量访问同样的网址,确认看到的是
public目录而不是office_a目录。 - 再检查文件下载的完整性,尤其是稍微大一点的文件,比对一下大小或者计算哈希。
这五步走完,基本可以确认 FTP 上传、落盘、Nginx 按 IP 路由、浏览器访问下载整条链路是通的。验证的时候别偷懒,一定要换一个客户端 IP 测试默认分组,我见过不少人只验证了内网能访问,忘了外网会落到哪个目录。
5.2 高频故障清单:403、404、连接被拒、中文乱码
下面这个表是我实际踩过和帮别人排查过的高频问题,建议直接收藏。
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 网页打开是 403 Forbidden | autoindex 没开;Nginx 进程对目录没有读权限 | 确认autoindex on;;检查目录权限,给运行 Nginx 的用户加读取权限 |
| 网页打开是 404 Not Found | root路径写错;路径用了反斜杠;变量 root 没生效 | 用正斜杠写路径;用nginx.exe -t测试;尝试加sendfile off; |
| 浏览器无法访问 Nginx | Windows 防火墙没放行 80 端口;端口被占用 | 添加入站规则放行 TCP 80;用 `netstat -ano |
| FTP 能登录但列不出目录 | 被动模式端口没放行;NAT 下被动 IP 没填公网地址 | 检查防火墙 50000-50100;检查 FileZilla Server 被动设置 |
| FTP 上传后网页看不到 | FTP 目录和 Nginx root 指向不一致 | 核对两边路径,FTP 的挂载目录和 Nginx 的 root 必须指向同一个物理目录 |
| 中文文件名乱码 | 上传客户端编码和网页编码不一致 | 统一 UTF-8;或放弃 autoindex 做静态入口 |
| 改配置不生效 | 改完没 reload;配置语法有错 | 每次都执行nginx.exe -t && nginx.exe -s reload |
这里强调一个习惯:改 Nginx 配置之后,一定先执行nginx.exe -t再 reload。有一次我漏了autoindex on;,配置语法没问题,但页面一直 403,查了半天才发现是少开了一个开关,这类问题靠语法测试是查不出来的,只能靠目录权限和指令逐项核对。
5.3 几个实用优化:限速、日志和 HTTPS
系统跑通之后,还可以顺手做三个优化,都不复杂,但对长期维护很有帮助。
第一是限速。如果文件服务器会被外部大量下载,我建议在location里加一句:
limit_rate 2m;意思是单个连接下载速度上限 2MB/s。限速既能保护服务器带宽,也不至于让文件完全打不开。
第二是访问日志。默认情况下 Windows 版 Nginx 的访问日志写在logs\access.log,日子久了单个文件会很大。可以简单用 Windows 计划任务定期把超过一定大小的日志文件改名归档,或者干脆把access_log配置到别的位置。文件分发系统不会有多么巨大的流量,但日志还是要能查。
第三是 HTTPS。通过 Nginx 给文件服务套一个 SSL 证书,浏览器访问就不会出现“不安全”的提示,文件在传输过程也是加密的。Windows 下配置方式和 Linux 几乎一样,证书可以找正规证书签发机构申请,如果只是内网用也可以在内部搭建 CA,但那样客户端需要额外安装信任证书,非内网场景不建议折腾。
最后,说两个我在实际使用中的体会
第一件,IP 属于“弱认证”,它适合做网段级的隔离,不适合做个人级的权限控制。如果你这套系统只是给不同部门、不同合作方相互隔开,按 IP 完全够用;一旦涉及到“某个特定的人只能看某个文件”,那就要考虑叠加账号密码认证。别等到录入了敏感文件才想起这个问题。
第二件,这套方案里 FTP 和 Nginx 的路径规划一定要提前统一。我自己的习惯是 FTP 目录结构决定 Nginx 的 root 映射,反过来 Nginx 的映射也会影响 FTP 账号的挂载方式。谁先谁后不重要,重要的是两边指向同一个物理路径,并且所有目录都落到一个根下面,比如D:\ftp_root\。这样备份、迁移、加分组,都只需要在一个地方改配置。整套系统跑起来之后,日常维护量其实很小,偶尔看看日志、检查一下磁盘空间就足够了。