客户端跳转产生另一次请求,内部重写则改变当前请求里的处理路径。

先决定让 nginx 跳转客户端,还是在内部重写

nginx 跳转与内部重写改变请求流程的不同部分。跳转返回状态和 Location,由客户端决定是否发起新请求;内部重写则在处理现有请求时改变 nginx 当前 URI。两者的选择决定客户端是否看到新地址、location 是否重新搜索,以及最终处理器收到怎样的路径和查询。不能因为都能让某个 URL 得到内容,就把它们当成同一种操作。 坏掉的样子 本来需要内部映射,却改成跳转,会增加客户端往返并暴露新 URL;本来需要公开永久迁移,却只做内部重写,客户端仍会继续使用旧地址。return 和 rewrite 也不能被当成总是默认相同跳转行为的通用缩写。明确状态码与重写标志,才能让预期转换可审阅,避免一行看似简单的配置隐藏行为差异。 状态选择影响方法语义和缓存。307、308 在自动跳转时保留请求方法,301、302 则允许历史上的 POST 改为 GET。永久不意味着每个客户端立刻更新所有已保存链接,临时也不意味着响应绝对不能缓存。缓存指令、请求上下文和客户端行为仍然重要,不能用状态名称替代明确的复用策略。 内部重写还可能选择错误处理器,或反复匹配自己。last 会为改变后的 URI 开始新 location 搜索;break 结束当前重写序列后,继续在现有 location 处理。查询参数可以按替换语法追加或丢弃。把这些区别视为可以互换,可能导致读取错误文件、重复参数,或进入返回 HTTP 500 的循环。 快速验证 使用宿主 Python、Docker 和固定 nginx 镜像运行脚本。实测为 Linux arm64 上的 nginx 1.28.0、Docker 29.3.1。它构建并检查可丢弃配置,只发布回环端口,不自动跟随跳转地验证真实响应,最后清理自身资源。一个小型静态文件让 break 所用处理器可以与目标 location 清楚区分: ...

一月 21, 2028 · 2 分钟 · XB Field Notes
命令在启动之前把输出与错误都指向文件。

重定向重新安排流向

重定向在命令执行前改变它的文件描述符。Shell 从左到右处理每一项,遇到打开文件或复制描述符的操作,就在那个位置完成相应变更。描述符零、一、二通常分别表示标准输入、标准输出和标准错误。把命令行读成对这些句柄的顺序操作,就能解释诊断为什么去了错误的位置,也能解释文件为什么还没有被程序读取就已经变空。 打开文件本身就可能改变数据 >output 打开输出文件,并且通常把已存在的普通文件截断为零长度;>>output 使用追加方式,<input 则打开文件供读取。这些动作由 Shell 完成,不是命令所指程序的内部行为。如果必须打开的目标失败,命令不会执行,但此前已经成功的重定向仍可能创建或截断其他文件。不能把“程序没有启动”理解成“整条命令完全没有副作用”。 因此,sed expression input >input 会在转换程序读取数据之前破坏原文件。即便先写 <input 打开输入,后续重定向截断同一个底层文件,也不能保住那些内容。应写入独立临时文件,检查转换状态与结果,再根据文件归属、权限和所在文件系统,明确执行替换步骤。输入输出恰好同名时,不能指望 Shell 自动替你实现安全的原地编辑。 复制记住的是当下的目标 >log 2>&1 先让描述符一指向日志,再让描述符二引用同一个已打开目标。2>&1 >log 则先复制原来的标准输出,之后只改变描述符一,因此标准错误仍留在原目标,例如终端或负责捕获输出的管道。这里复制的是描述符引用,不是文件内容,也不是给两个数字建立一个会随之后续变化的动态连接。 Bash 的 &>log 可以简写同时重定向两条流,而 >log 2>&1 也适用于可移植 Shell。不要把它写成 >log 2>log:后者分别打开同一个文件,两份打开状态拥有独立偏移量,输出可能相互覆盖。即使描述符共享一个目标,应用程序自身的缓冲仍会影响消息真正写出的时间,所以合并日志也不自动保证业务事件严格按发生顺序排列。 管道会先把标准输出连接到下一阶段,然后才应用命令自己的重定向。因此 producer >file | consumer 的生产者把输出交给了文件,消费者反而读不到那些数据。相反,producer 2>&1 | consumer 会把诊断也送进管道。如果下游要求机器可解析的记录,把面向人的错误文本混进去,就可能破坏原本合法的输入格式。 ...

九月 29, 2027 · 1 分钟 · XB Field Notes
观察、决策、行动三个方框,旁边强调按策略跟随跳转并保留方法。

重定向是对「去哪」的一份承诺

重定向是一份响应,要求客户端在另一个目标上发起新请求。Location 字段和状态码共同描述去哪以及方法怎样变化。浏览器通常会自动跟随,但客户端库也可以停下、检查或拒绝后续跳转。每一跳都应当作为协议数据记录:只看最终页面,无法知道 POST 是否变成了 GET、凭据是否越过了源边界,或是否有循环消耗了重试预算。 永久和临时表达不同承诺 301 与 308 表示永久迁移,302 与 307 表示临时绕行;303 明确要求用 GET(原请求为 HEAD 时用 HEAD)取得表示。永久状态可能被缓存、写入书签和搜索索引,所以维护窗口不能随意使用永久码。临时状态告诉客户端下次仍应访问旧地址。RFC 9111 的缓存规则和资源策略仍然有效,“永久”不是忘记授权、信任任意主机或跳过证书检查的指令。 方法是否保留要看状态码 出于历史兼容,许多客户端在 301、302 或 303 后把 POST 改为 GET 并丢掉正文;307 和 308 则要求保留原方法与正文,但前提是客户端能够重新读取该正文。重定向不会把非幂等动作变得安全,保留方法也不等于源站已经接受了重放。服务端应选择与操作语义相符的状态,客户端则应确认请求体可重放并匹配合适的重试策略。 相对 Location 要根据有效请求 URI 解析;客户端应记录解析后的每一跳并设置最大跳数。缺少 Location 时没有可跟随的新目标,自循环和过长链是服务器缺陷,不是无限提高上限的理由。跳转也可能改变 scheme、端口或 origin,转发 Cookie、Bearer token 和客户端证书前必须执行显式允许列表;指向另一源的链接不是发送凭据的授权。 ...

七月 28, 2026 · 4 分钟 · XB Field Notes