把Stable Diffusion WebUI部署到远程服务器或者家里的主机上,再通过Nginx反向代理从外部访问,是很多人搭建私人绘画站的常见做法。但只要用过一段时间,几乎都会撞上同一个问题:提示词复杂一点、分辨率调高一点、采样步数多一些,页面右上角的进度条还没走完,浏览器就先弹出一个504 Gateway Timeout。更让人郁闷的是,这时候去服务器上看一眼日志,发现生成任务其实还在正常跑,纯粹是代理层嫌等待时间太长,主动把连接掐断了。要解决这个问题,需要真正理解反向代理的超时机制和缓冲区行为,而不是盲目照抄网上的配置片段。

504错误的产生原理:代理与服务端之间发生了什么
先厘清一件事:504 Gateway Timeout不是Stable Diffusion WebUI报的错,而是反向代理(通常是Nginx)报的。整个链路是浏览器到Nginx,Nginx到后端的7860端口。当Nginx把请求转发给后端之后,它会带着一个计时器等待后端的响应,如果在这个时间内后端一直没有吐出可用的数据,Nginx就会认为上游挂了,向浏览器返回504,同时可能切断与后端的连接。
SD WebUI的特殊之处在于它的出图接口是一个典型的长耗时请求。一张1024x1024、步数30的图在中端显卡上跑二三十秒很正常,如果显存吃紧还会触发内存交换,耗时直接翻倍。而Nginx的默认proxy_read_timeout只有60秒,proxy_send_timeout也是60秒,一旦后端在这个窗口内没有返回响应头或者持续可读的数据,504就来了。
还有一个容易被忽视的细节:SD WebUI在生成过程中会通过SSE(Server-Sent Events)或者内部进度接口持续推送进度信息,如果你开启了代理缓冲,Nginx会把这些零碎的小数据包攒在缓冲区里,不及时转发给浏览器。对Nginx来说,只要缓冲区里的数据没有被读走的时间超过read timeout,同样会触发超时判定。所以有些情况下即使出图只要40秒,照样报504,根源就在缓冲行为上。
Nginx核心超时参数配置详解
先看一份经过验证的完整配置,再逐个解释每个参数为什么这样设:
server {
listen 80;
server_name drawing.ipipp.com;
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:7860;
# 连接后端的超时,本地转发给大一点即可
proxy_connect_timeout 60s;
# 向后端传输请求体的超时
proxy_send_timeout 600s;
# 等待后端响应以及两次读取之间的超时,重点参数
proxy_read_timeout 600s;
# 关闭响应缓冲,让进度信息实时到达浏览器
proxy_buffering off;
# 保持长连接,避免频繁握手
proxy_http_version 1.1;
proxy_set_header Connection "";
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是最关键的参数,它控制的不是整个请求的总时长,而是两次成功读操作之间的最大间隔。也就是说,只要后端每隔一段时间有数据发过来(比如进度推送),计时器就会重置。把它设成600秒,意味着后端最多可以安静10分钟。如果你的模型特别重,比如跑SDXL加ControlNet加高清放大,建议直接设成1800s。
proxy_buffering off是第二个关键点。关掉缓冲后,Nginx收到后端的任何字节都立刻转发给浏览器,既保证了进度条实时刷新,也让read timeout的计时器持续被进度数据重置,从机制上进一步降低504概率。代价是失去缓存优化,但对这种一对一的出图场景没有影响。如果出于某些原因必须开缓冲,那就要同时调整proxy_buffers和proxy_busy_buffers_size,并把read timeout再加长,得不偿失。
client_max_body_size也顺带提一下,上传大模型、超大的图片做图生图时,默认1MB的限制会直接返回413错误,很多人把它和504搞混,这里一并设成100m以上可以省去后续麻烦。
其他远程访问场景的超时处理
不是所有人都用Nginx,常见的远程方案还有Frpc内网穿透、Cloudflare Tunnel、Apache等,它们的超时位置各不相同。
用Frpc穿透时,需要在frpc.ini(或新版frpc.toml)里加上 transport 配置。Frpc默认的心跳和连接超时相对宽松,但如果你前面还套了一层有HTTP代理的隧道服务,比如frps部署在带Nginx的机器上,那么Nginx那一层的超时仍然要按上面的方法改,穿透工具本身只负责转发字节流,一般不会主动掐断长请求:
# frpc.toml 新版写法 [transport] heartbeatInterval = 30 heartbeatTimeout = 90 poolCount = 5
用Apache做代理的话,对应的参数是ProxyTimeout和Timeout,在虚拟主机配置或者httpd.conf里加上一行ProxyTimeout 1800即可,含义与Nginx的read timeout类似。另外Apache 2.4的事件模式下还要注意ProxyPass的flushpackets=on选项,它等价于关闭Nginx缓冲,能让进度信息实时送达。
还有一个偷懒但有效的思路:SD WebUI自带的--api和--cors-allow-origins参数配合Gradio本身的队列机制,可以让浏览器请求变成异步轮询,而不是一直挂着等结果。在launch参数里加上--queue-concurrency 1之类的设置,配合前端的队列等待,即使一次出图超过代理超时时间,页面也能通过后续轮询拿到结果。这算是从应用层绕开超时问题的备用方案。
最后提醒一点排查思路:改完配置后务必用nginx -t检查语法再nginx -s reload重载,很多人口口声声说改了没用,其实是配置文件改错了位置没生效。验证方法很简单,故意生成一张高步数大图,同时在服务器上执行tail -f /var/log/nginx/error.log,如果看到upstream timed out字样消失,浏览器端进度条能一路走到出图完成,说明问题已经彻底解决。