上个月有个内部项目收尾,运维同事提了个需求:公司某台Windows主机专门收各部门上传的报表和素材,平时大家用FTP工具往里传文件,但总部那边一些同事连FTP客户端都懒得装,直接问能不能开个网址,像点网页链接一样把FTP里传上去的文件打开下载。
这个需求听起来简单,真做起来有几个认知坑。Nginx本身只能代理HTTP协议,拿它去“反代一个FTP服务”属于鸡同鸭讲,所以不能指望写一行配置就把ftp://变成http://。最终我用的是最简单也最稳的一套组合:Windows下用FileZilla Server做FTP服务,把用户上传目录固定在磁盘某个文件夹,再用Nginx开一个带目录列表的静态站点,让那个文件夹成为站点的站点根目录。这样大家访问内网IP加端口,就能在网页上看到FTP里的全部文件,点击即可下载。
下面把这套方案的选型逻辑、完整配置、实操步骤和踩过的坑一次讲清楚。
1. 方案设计:为什么Nginx不能直接反代FTP,以及最终选型
1.1 这个需求到底在解决什么问题
先说清楚业务场景。内部资料收集,文件从几十兆到几个G都有,团队里有的人习惯用FTP客户端,有的人就想要一个网页链接,点开就能看。要解决的是两个问题:一是把文件从大家本机收拢到服务器,二是把这些文件以Web形式对外展示。前者用FTP,后者用Nginx挂一个目录列表,逻辑上就是“FTP负责收,Nginx负责展示”。
最忌讳的做法是把FTP和网页访问搅在一起混做。有人一听到“在网页上打开FTP文件”,第一反应是给FTP套一层Web UI,或者去找一个支持FTP协议的浏览器插件。这些方案不是不行,而是引入了额外的复杂组件,配置成本高,出了问题也不好排查。早在动手之前,把“文件上载”和“文件展示”拆成两个独立环节,后面每一步都会清爽很多。
1.2 Nginx与FTP的协议差异,以及“反代”的正确定位
关键点: Nginx是一个HTTP反向代理服务器,它支持http、https,支持mail类协议,但FTP是另一个独立协议,Nginx本身完全没有FTP协议模块。标题里说的“通过nginx服务反向代理方式”更准确的理解是:Nginx作为一个统一的HTTP入口,把FTP目录里的文件以Web资源的形式暴露出来。最终用户看见的是http://IP:端口/xxx.zip,而不是ftp://IP/xxx.zip。
这里必须承认一个细节:想让Nginx能发布FTP里的文件,Nginx必须能读到FTP目录的实际文件路径。换句话说,FTP服务端和Nginx静态站点之间,必须有一个共同的文件系统视图。搞清楚了这一层,后面所有配置都能顺着“同一个目录,两个入口”的思路来理解。
1.3 三种落地方式的对比与最终选择
| 方案 | 链路构成 | 适用场景 | 主要短板 |
|---|---|---|---|
| 同机共享目录 | FileZilla Server + Nginx autoindex,FTP主目录直接做Nginx的root | FTP和Web都在一台Windows主机,且Web端允许只读浏览 | 文件只能按原样列出,不能做移动重删等管理 |
| 挂载FTP为盘符 | FTP在远端,用RaiDrive等把FTP映射为本地盘,Nginx指向该盘 | FTP服务在另一台机,Web发布机是Windows | 依赖第三方挂载工具,挂载盘符的权限问题比较隐蔽 |
| 文件管理Web工具 | 部署FileBrowser,Nginx反代到FileBrowser的HTTP端口 | 需要在线删除、重命名、多用户权限控制 | 功能偏重,新手配置有一定门槛 |
我最终选了方案一。这台Windows主机本来就是文件落脚点,FTP根目录和Web目录天然重合,等于零额外协议转换,稳定性和排查难度都最可控。后面会单独说方案二的跨机用法,因为在实际交付中跨服务器部署也很常见。
2. Windows下FTP服务器搭建与目录规划
2.1 服务端选型:为什么用FileZilla Server而不是IIS
Windows自带的IIS可以开FTP,但在用户配额、目录权限精细度、被动模式配置上都不如FileZilla Server顺手。IIS的FTP对中文文件名支持也更烦,权限模型偏“站点”思维,不适合做简单的多用户上传目录。FileZilla Server开源免费,Windows版是原生服务,装完就是一个后台服务,重启自启,配置界面清晰。
如果你对数据安全性有更多要求,也能用Windows自带的IIS FTP配合文件和存储服务功能,或者用其他开源FTP服务端,但FileZilla Server是在小团队场景里我自己用得最顺手的一个,下面所有操作都基于它。
2.2 安装要点与初始参数
下载标准Windows版,安装时一定要选“Install as service, start with Windows”,这样主机重启后FTP服务不用手动拉。
安装完成后默认监听21端口,管理界面默认端口是14147,只在本机回环地址访问。这里有个细节:很多教程会让你把14147也指到服务器公网,千万不要这么做,管理端口应该保持仅限本机连接,否则等于给外部开了一扇管理后门。
FileZilla Server的主界面左侧是服务器配置树,右侧是对当前连接会话的监控。装好以后,第一件事就是进入“Passive mode settings”确认被动模式配置,这个我在2.4里会单独展开。
2.3 用户与目录规划:目录结构决定了后面所有路径
这一节值得多看几遍,因为目录结构直接影响Nginx配置。我建议FTP根目录不要放在C盘某个角落,最好单独规划一个数据分区,路径结构做成:
D:\FTPRoot\ ├─ pub\ # 公共读目录 └─ upload\ # 各用户上传主目录在FileZilla Server里新建用户,比如ftpuser,主目录指向D:\FTPRoot\upload,权限给文件读写和目录列表。注意:不建议给删除权限,除非明确需要用户自己清理过期文件。这样即使Nginx把整个目录暴露成网页,对外也只读,配合Windows目录ACL双重限制,能防止在线数据被误删。
后续如果某个文件需要被大家看到,就统一放到upload目录下的一个“发布”子目录,其他人从网页上点开就能下载,这个习惯比较实用。
2.4 被动模式与防火墙准备
FTP主动模式和被动模式的区别不展开太多,简单说:Nginx的autoindex目录列表和网页下载都是只读操作,但FTP上传过程必然要使用被动模式。主动模式要求服务器主动去连客户端的高位端口,在NAT和Windows防火墙下十有八九会被拦,所以FileZilla Server里要把被动模式设置成使用自定义端口段,比如50000到50100,然后在Windows防火墙里放行TCP 21和TCP 50000-50100。
这一步卡了很多人:21端口放行了,一传文件就卡死在95%附近,最后查出来是50000那一截没有放行。FTP的控制连接和数据连接是两回事,少了一个端口段,上传这一步就会反复失败。第一次搭建时别省事,连着一起配好最稳。
3. Nginx接入:完成网页访问的最后一公里
3.1 “反向代理”在这一场景中的落地方式
Nginx的“反代”对象是HTTP服务,不是FTP。所以最终落地时,我把FTP上传目录直接作为Nginx静态站点的root,然后开启autoindex。这样用户访问的就是一个普通网站,而这个网站的根目录就是FTP能写入的同一个物理路径。整个过程没有在协议层转FTP,但效果上已经实现了“网页打开FTP文件”。
如果不想把整个目录全量暴露,也可以用alias做局部映射,比如:
location /files/ { alias D:/FTPRoot/upload/; autoindex on; }这种情况下,访问URL带上/files/前缀,目录列表会更友好,路径语义也更清晰。用alias的另一个好处是将来可以同时发布多个FTP用户目录,每个用户一个location。
3.2 在Windows上安装与运行Nginx
Nginx没有Windows安装包,官方提供的是zip压缩包。下载对应Windows版本,解压到C盘某目录,比如C:\nginx-1.2x.x,注意路径不要带中文。运行nginx.exe就能启动,默认监听80端口,如果80被IIS或其它服务占用了,改配置换端口。
常用命令:
# 进入Nginx目录后执行 cd C:\nginx-1.2x.x nginx.exe # 修改配置后测试语法并重载 nginx.exe -t nginx.exe -s reload # 停止服务 nginx.exe -s stop很多朋友习惯每次解压后双击exe就不用管了,其实在cmd窗口里用命令更好。原因很简单,改配置后nginx -t能立刻提示语法错误,避免带病启动。
3.3 核心配置解析:一份可以直接抄的server块
下面这份配置放进conf/nginx.conf的http块里就能用:
server { listen 8088; server_name _; root D:/FTPRoot/upload; autoindex on; autoindex_exact_size off; autoindex_localtime on; charset utf-8; access_log logs/ftpweb_access.log; }逐项说明:root指向的是FTP用户的主目录,路径分隔符用正斜杠;autoindex on就是开启目录列表,没有它访问目录时大概率直接403;autoindex_exact_size off让文件大小显示成更易读的MB/GB格式;autoindex_localtime on让文件时间显示为服务器本地时间,否则Nginx默认输出UTC时间,看上去差8个小时;charset utf-8控制页面编码,配合英文或UTF-8文件名时,文件夹名称能正常显示。
改动配置后,先执行nginx.exe -t确认语法,再reload。这个习惯能让你少走很多弯路。
3.4 根据IP访问与多种访问形式
配置完成后,同网段内的机器直接访问http://服务器内网IP:8088就能看到FTP目录里所有文件,子目录会顺着层级继续往下展开。如果想省略8088端口,可以将listen改成80,前提是80没被占用。真要这样做,记得先把IIS或其它占用80的服务停掉,否则Nginx起不来。
大一点的局域网里,建议在内部DNS服务器加一条A记录,把某个内部域名指到这台服务器的内网IP,这样同事访问http://files内部域名就能进站,比记IP加端口直观很多。没有DNS的话,直接发http://IP:8088最省事。
4. 端到端实操:从上传到网页打开的完整链路
4.1 搭建步骤总览
把整条链路的步骤一次性列出来,照着走基本不会漏:
- 安装FileZilla Server,确认21端口监听正常
- 新建FTP用户,主目录指向
D:\FTPRoot\upload - Windows防火墙放行TCP 21和TCP 50000-50100
- 下载并解压Windows版Nginx
- 修改
nginx.conf,加server块,root指向D:/FTPRoot/upload - 执行
nginx.exe -t校验语法,启动nginx.exe - 本机浏览器访问
http://127.0.0.1:8088验证目录列表 - 局域网其它机器通过
http://服务器IP:8088验证
如果第6步启动后发现页面打不开,多半是listen端口被占用,用netstat -ano | findstr 8088看看有没有进程在监听。
4.2 从FTP上传到网页出现的完整验证
用FileZilla客户端验证:新建站点,协议选FTP,服务器填本机IP,端口21,账号ftpuser。连接后把一份压好的项目包拖进服务器目录,传输完成后到浏览器刷新页面,刚才的压缩包会出现在目录列表里,点一下就能直接下载。
这里有一个隐蔽的坑:Nginx读取文件依赖NTFS权限。如果FTP用户把文件传进了D:\FTPRoot\upload,但该目录的ACL没有给Nginx worker进程读取权,网页上看到的可能是空列表或者403。Windows版Nginx默认以普通账号运行,但只要该账号对该目录有读取权限就没问题,大多数场景直接给Users读取权限即可。
4.3 跨机场景:把FTP挂载成盘符再交给Nginx服务
如果FTP服务器和Nginx不在同一台Windows机器,那就需要挂载手段。我试过用RaiDrive这类工具,把远端FTP挂载成一个本地盘符,比如Z盘。挂载好之后,Nginx的root直接写成Z:/,再套用autoindex配置,效果和同机目录一样。
挂载方案有两个坑要留意:一是FTP被动模式和防火墙同样要放行,否则挂载后的目录时好时坏,刷新几下就空列表;二是挂载的盘符可能在重连后变化,Nginx配置里写死Z盘,重连前要先确认盘符是否恢复。如果发现页面404,优先去“我的电脑”里看看Z盘还在不在。
4.4 这一场景下的权限与安全边界
把FTP目录同时交给FTP写和Nginx读,等于把这个目录的读写权分开给了两个系统。我的经验是严格按最小权限来:FTP用户只给上传根目录的写和列目录权限,不给删除、不给改名;Nginx站点只读目录,不执行任何脚本,不解析动态内容,彻底断了Web访问时把上传文件当脚本执行的可能。
如果部分文件有时效,比如发布会物料过了日期要撤掉,可以配合Windows计划任务定时清理上传目录下的过期内容。用批处理按文件日期或文件名规则删除,每天跑一次,非常稳定。这个思路适合长期维护场景,比每次手动去FTP里清理可靠得多。
5. 常见问题与排查技巧实录
5.1 症状速查表
下面这些问题是实际部署时反复遇到的,整理成速查表方便快速定位:
| 现象 | 常见原因 | 处理动作 |
|---|---|---|
| FTP能连接,但上传卡在95%左右 | 被动模式端口段未放行 | 检查防火墙TCP 50000-50100 |
| 网页访问时403 Forbidden | autoindex未开启,或Nginx无读取权限 | 配置autoindex on,检查NTFS权限 |
| 目录列表能看到,点文件404 | root路径和实际路径不一致 | 确认root/alias里的正斜杠路径和真实目录一致 |
| 中文文件名显示乱码 | 文件名编码与输出编码不一致 | 统一用UTF-8命名,或调整charset |
| 8088端口访问不通 | 防火墙未放行,或listen被注释 | netstat -ano确认监听,再放行入站规则 |
5.2 中文文件名乱码的根源与解决方案
Windows上的FTP默认文件名编码是GBK,而Nginx的autoindex输出页面默认UTF-8,两边一冲突,网页目录列表里的中文就全变成乱码。这是这个方案里最影响体验的问题,没有之一。
我试过几种做法:把Nginx的charset改成gbk,列表恢复正常,但浏览器里的文件名和FTP客户端名称对不上,反而制造了新混乱;让FileZilla Server强制走UTF-8,但老FTP客户端依然乱,因为客户端用的是本地编码。最稳妥的还是约定:上传文件统一用英文字母和拼音命名。如果必须要中文,就先用英文传上去,再通过脚本把文件名统一转换为UTF-8。实践下来,约定命名规范比事后折腾编码省心得多。
5.3 被动模式下上传不稳定怎么办
之前遇到一个案例,同一个WiFi下的同事往FTP传大文件,传几个G就断一次。表面看是网络问题,实际上是路由器或防火墙对长连接的表项老化过快,FTP数据连接空闲一段时间后就被清了。解决思路很简单:一是把FileZilla Server被动模式端口段扩大,比如50000-50500,避免多用户并发时端口池耗尽;二是传超大文件时建议先压成一个包再传,减少会话数量;三是如果内网设备都归网络管理员管,直接把现象反馈过去,请他们把会话老化时间调长,往往立刻见效。
这类问题在文件服务器场景很典型,不能只盯Windows配置,还要考虑网络设备。
5.4 关于安全实践的几条心得和补充经验
最后聊安全。内网场景虽小,但FTP加Web目录暴露时,至少要做到以下几点:
- FTP服务端关闭匿名访问,只保留明确授权的账号;
- FileZilla Server管理端口14147只监听127.0.0.1,绝不暴露到网络;
- Nginx站点只读发布,不开放写权限;
- 定期查看access_log,留意是否有访问异常路径的记录;
- 如果站点对外网开放,Web侧必须加HTTP Basic认证,最简单的做法是在server块里加
auth_basic。
我见过有些团队图省事把FTP和Web全部裸奔在公网,没几天就被扫描器刷出敏感目录。即便只是内网使用,也建议把端口从默认改成非标准,比如FTP改成2121,Web改成8088,至少让误扫描的难度高一点。注意改FTP端口后,客户端连接时也要填对应端口,别只记着一个默认21就到处连。
写在最后
这套组合我在好几个内部项目里都部署过,每次从装FileZilla Server到Nginx目录列表上线,大概半小时搞定。我个人体会是,这类需求真正难的不是单点技术,而是把“文件上传”和“网页访问”两条链路想清楚。Nginx不能代理FTP,是很多初学者最容易卡住的认知点,一旦你把它理解成“Nginx只是把物理目录公开成Web目录”,后面所有配置都会顺理成章地一次通过。
最后再分享一个扩展思路:如果以后用户多了,或者大家希望网页端能搜索、能在线预览,不用推翻现有结构,直接在前面加一层FileBrowser做文件管理界面,Nginx反代到FileBrowser的HTTP端口,数据源还是那个FTP目录。老用户继续用FTP工具,新用户走网页,两条路互不影响。反正底层文件都放在同一个目录里,想怎么接都能接,方案就能一直演进。