Nginx Rewrite实战:URL重写、执行原理与避坑排错指南 做网站时间长了总会遇到一类需求旧的详情页地址要换结构、带参数的URL要统一收口、动态接口要用伪静态对外发布。每次看到这种需求第一反应就是打开Nginx配置文件写一条rewrite。但rewrite这玩意儿看起来就是一行正则加一个地址真跑起来之后重定向循环、404、参数丢失、跳到了错误的后端服务各种情况都容易冒出来。如果你用Nginx做过一次彻底的Rewrite改造应该能理解我说的这种纠结。它不是在URL后面加个斜杠那么简单而是涉及Nginx整个请求处理流程里一个独立阶段的运算规则。这篇文章不谈官方文档复读只讲我实际配置和排障过程中沉淀下来的判断方法、语法细节、真实场景的写法以及那些配置完才发现“原来会这样”的坑。适合正在用Nginx做站点迁移、URL结构优化、接口网关路由调整的同学参考。1. 动手写rewrite之前先分清你要解决的是哪类URL问题rewrite在Nginx里被翻译成“重写”但在实际业务里我们遇到的URL问题分为好几种很多人不管三七二十一全用rewrite硬写。这种做法的后果就是配置混乱规则互相覆盖最后只能靠叠规则碰运气。1.1 URL跳转、URL重写和路由分发是三种需求先说最常见的三种场景。第一种是URL跳转也就是说用户在浏览器访问老地址需要让浏览器主动发起新地址的请求最典型的是旧域名迁新域名、HTTP升级成HTTPS、文章旧链接变成新链接。这种情况下客户端地址栏会改变所以响应码应该是301或302。Nginx里完成这件事有两种方式一种是rewrite加redirect或permanent标志另一种是直接使用return 301。两者效果看起来一样但实现阶段不同后面会单独讲。第二种是URL重写形式上是把对外比较长的动态参数地址变成简洁的静态化地址或者反过来前端访问简洁地址内部转发到真实的PHP或Java处理脚本。用户地址栏通常不会变或者说我们不希望它变这种情况用的是rewrite加last或break标志让Nginx在内部用新的URI继续找location。第三种是路由分发其本质不是改URL而是根据URL里的某个特征决定把这个请求交给哪个location或哪台后端服务器。这种情况更推荐直接在location层匹配不一定需要rewrite介入。为什么要先分清楚这三类因为它们的配置位置和判断标准完全不同。把跳转写成内部重写用户会看到地址栏没变但页面已经换成了新内容把内部重写写成跳转每一次请求都会多一次网络往返而且可能把后台接口地址暴露给用户。本质上rewrite只是发起变换动作的指令你在哪个阶段使用它、带什么标志直接决定了这个变换是让客户端感知还是只在服务端内部生效。1.2 能不用rewrite就别用先用return和try_files试一下可能有人会觉得既然rewrite功能这么强那所有URL问题都应该用它。实际经验恰恰相反相当一部分URL问题用return或try_files能更干净地解决。如果只是需要返回一个固定的301或302响应应该直接写return。return会在较早的请求处理阶段直接终止连接生成响应头中的Location。它的效率比rewrite高规则也更简洁而且不会出现很多人担心的正则回溯问题。比如HTTP强制跳HTTPS最稳妥的写法是server { listen 80; server_name example.com; return 301 https://$host$request_uri; }这段配置不需要任何正则$host保留原始域名$request_uri保留原始完整地址连查询参数都原封不动带上。如果用rewrite写常见的写法是rewrite ^(.*)$ https://$host$1 permanent;这就有个风险如果URL里本身包含中文或特殊字符$1拿到的是已经解码后的URI在跳转时可能丢失编码信息导致参数错乱。再说伪静态场景。很多人会这样写一套“所有不存在的文件都转给index.php处理”的规则location / { if (!-e $request_filename) { rewrite ^/(.*)$ /index.php?/$1 last; } }这种写法能用它的正则看起来也不复杂。但它把“判断文件是否存在”和“URL重写”耦合在一起每来一个静态资源请求都要先做一次文件系统检查。相比之下try_files的语义更清晰location / { try_files $uri $uri/ /index.php?$query_string; }try_files按顺序检查文件是否存在都不存在才把请求交给最后的/index.php中间不需要写正则也不会有if带来的隐患。因此如果在写配置之前问自己一个问题这个需求是非得正则替换不可还是可以通过return跳转、try_files兜底、location匹配就能完成大多数场景其实都可以用更简单的方式解决。2. rewrite语法与正则的细节决定了规则会不会按预期执行当你已经确认某个场景确实需要rewrite再来看它的语法就踏实得多。一条完整的rewrite指令长这样rewrite regex replacement [flag];regex用于匹配当前请求的URI注意不是匹配包含域名在内的完整URLreplacement是替换后的目标flag是可选参数控制匹配完成后是继续处理还是终止。2.1 rewrite的匹配对象和正则需要知道的事Nginx的rewrite指令默认匹配的是$uri也就是经过解码、去掉了参数部分的路径。这意味着正则表达式里不需要考虑查询字符串但也意味着我们不能在replacement里直接依赖参数结构。所以一个很重要的常识是如果你想根据URL里的参数来决定要不要重写不能把这个判断写进正则本身比如用rewrite ^.*\?id123 /new last这种方式是无效的因为匹配的URI本身不含问号。你需要两层思路配合第一层用if ($arg_xxx)或$query_string判断参数条件第二层再写rewrite根据不同参数值进行替换。正则部分还有个容易踩的细节Nginx的正则底层用PCRE库处理默认是区分大小写的。如果你希望不区分大小写正则前面要加(?i)修饰符比如rewrite ^/category/(?i)apple/?$ /goods/apple last;这段配置能同时匹配/category/apple和/category/Apple。但要注意(?i)只对它后面的部分生效如果放在开头通常会影响整个正则实际项目中建议把需要忽略大小写的子串处理清楚不要靠感觉。捕获组是实战里最常用的能力正则中用括号括起来的每一段会被依次保存为$1、$2、$3。例如要做一个文章详情页的ID提取location / { rewrite ^/article/(\d{1,10})\.html$ /index.php?id$1 last; }访问/article/123.html时内部的URI会被替换为/index.php?id123。括号要尽量精确写得太宽泛容易把不该进来的URL也替换掉。我在实际排查中就遇到过一个规则写成了rewrite ^/(.*)\.html$ /index.php?id$1 last结果所有以.html结尾的地址包括静态帮助页全被吞进了动态脚本。2.2 四个标志位的真正含义比看起来更重要rewrite后的flag一共有四种last、break、redirect、permanent。它们的差别决定了这次URL变更是否继续影响后边的处理流程。last停止执行当前server块或location块中的后续rewrite指令然后把重写后的URI交给Nginx重新做一次location匹配。break停止执行后续rewrite指令但不再重新匹配location直接在当前location继续处理。redirect返回302临时跳转常用于未确定规则是否长久有效时。permanent返回301永久跳转常用于确定性的链接结构迁移。其中容易混淆的就是last和break。我用一种容易记住的方式来理解如果你改写的目标地址希望它重新进入另一个location匹配流程在这条规则里用last。典型例子是伪静态转发到PHP文件因为PHP文件一般有自己的location ~ \.php$匹配块需要last重新匹配。如果你的rewrite就写在一个很具体的location块内而且你只是想让后续指令在改写后的URI上继续执行不指望它跳去别的location用break。尤其是配合proxy_pass把规则改写成后端内部路由时break能避免改写后的URI再次落入别的正则location造成误匹配。当然也有一些团队习惯在server块内统一写rewrite规则然后不带任何flag让Nginx默认继续执行到末尾的location匹配阶段。这种写法在规则少的时候能工作但规则一多执行顺序不清晰而且改写后的URI可能被后面的rewrite再次改动。稳妥的统一规范是如果改写必须匹配某个location才继续处理就用last否则用break尽量不依赖缺少flag时的默认行为。2.3 标记redirect和permanent时记得确认目标是否以http开头有一种常见的误解是rewrite加permanent标志就一定会跳转。实际上replacement如果不以http://或https://开头并且没有显式指定redirect、permanent默认只是内部改写不会把Location头发给浏览器。比如这样写rewrite ^/old$ /new permanent;当客户端访问/old时Nginx确实会返回301Location头会被补成站内URL。但如果site配置里同时用了server_name_in_redirect等变量域名生成有的微妙差异。稳妥起见跳转到站外地址时replacement必须写完整协议例如rewrite ^/link/(\w)$ https://partner.example.com/go?id$1 permanent;另外像301这种永久跳转浏览器和应用都会缓存。正式上线前如果还没有完全确定新地址先用302调等地址确认稳定了再切301。我做过一次旧栏目迁移最初把跳转写成permanent结果后来发现新版页面路径少加了一层目录但老用户和搜索引擎已经缓存了旧301地址只能再等缓存过期。3. 五类高频场景的rewrite写法与思路拆解聊完语法和规则就到了“抄作业”环节。我整理了实际项目中反复出现的几类需求每类都给了配置和说明你可以根据自己的情况改域名、改路径。3.1 老地址迁移旧URL整体换新路径比如旧站点中所有/product/...路径要换到/goods/...并且要告诉搜索引擎这是永久迁移规则可以这样写server { listen 80; server_name example.com; rewrite ^/product/(.*)$ https://example.com/goods/$1 permanent; }此时$1会把/product/后面剩余的所有路径原样带到新地址。由于URL中旧路径只有一层也可以用.*消费掉剩余内容。如果旧路径有参数前面已经提到参数不会被匹配进这段正则中但有一个隐藏现象需要注意当使用rewrite生成301时Nginx会把当前请求的$request_uri追加到新地址后面吗不会。$1只是URI的路径部分。如果你想完整保留参数应该在replacement里显式加上?$argsrewrite ^/product/(.*)$ https://example.com/goods/$1?$args permanent;但这会产生一个问题当原URL没有参数时新URL会以一个空问号结尾比如https://example.com/goods/123?。为了避免这个问题可以把判断和跳转拆开写放在server块配合ifserver { listen 80; server_name example.com; if ($args) { rewrite ^/product/(.*)$ https://example.com/goods/$1?$args permanent; } rewrite ^/product/(.*)$ https://example.com/goods/$1 permanent; }这样带参数的请求会走带参数跳转的规则不带参数的请求走后一条。虽然比一个rewrite完成所有事显得繁琐但实际线上请求类型五花八门用这种“参数有无分支”处理能避免目标URL出现空问号之类的瑕疵。3.2 只转发带参数的某个URL其他请求一律拒绝有一类网关场景很典型内网有一个服务只允许接收带特定参数的请求。比如客户端的支付回调或扫码登录跳转URL必须是/callback?tokenxxx格式不带参数的裸/callback请求直接返回404。服务端配置可以这么写server { listen 80; server_name gateway.internal; location /callback { if ($args ) { return 404; } proxy_pass http://backend_server/callback/verify; } }这里用location /callback做精确匹配保证只有不带其它路径的/callback才会进入这个块。里面的if ($args )判断请求是否带参数为空则直接终止返回404不再往后端转发。如果带了参数则通过proxy_pass转发给后端服务。这种“先条件判断再转发”的做法比单纯在rewrite正则里嵌套参数判断清晰得多。因为proxy_pass和rewrite在同一条链路下只要请求能到达这个location就说明路径匹配了接下来区分的就是参数。需要注意if块里能安全使用的指令非常有限日常配置中我默认只建议在if里使用return、rewrite和set这三种它们的行为经过长期验证是可靠的。不要随便在if里挂proxy_pass这块存在很多历史兼容问题。如果不想使用if还有一种更符合Nginx哲学的写法利用map把参数通过变量转成开关map $args $has_args { 0; default 1; } server { listen 80; location /callback { if ($has_args 0) { return 404; } proxy_pass http://backend_server/callback/verify; } }两种方法本质相同主要看团队是否对if的使用规范有严格要求。3.3 伪静态动态地址和静态地址互相转换伪静态从用户视角是把/index.php?id123page2变成/article/123/page/2.html但从Nginx视角它是把静态化的URL还原成动态脚本能识别的参数。下面是一个典型的规则server { listen 80; server_name example.com; location / { try_files $uri $uri/ rewrite_to_php; } location rewrite_to_php { rewrite ^/article/(\d)/page/(\d)\.html$ /index.php?id$1page$2 last; rewrite ^/article/(\d)\.html$ /index.php?id$1 last; } }这种写法有几个好处第一先把静态文件和目录交给try_files处理只有真正不存在的静态地址才会进入命名location第二在命名location里写两条rewrite规则前一条匹配带分页的地址后一条匹配不带分页的地址。rewrite是按顺序匹配的一旦命中就会停不会继续往下执行。为什么把rewrite放进命名location而不是直接在根location里写因为要尽量避免在location /这种大范围块内做正则匹配尽量减少对静态文件请求的干扰。把规则收拢到一个专门的命名location中让业务规则内聚排障时打开对应文件就能看到。3.4 替换URL中的一段特定目录且路径深度不同有时URL不是整体换结构只是中间某个路径段变了比如后台管理地址由/admin/manage/user改成/console/manage/user。一个简单粗暴的规则是rewrite ^/admin(.*)$ /console$1 last;这类规则要注意两个问题。一是.*能匹配到空字符串所以/admin会改写成/console如果/console正好也有独自的location这是预期行为二是防止把/administrator之类以admin开头的地址也误改。所以更严格的规则要使用边界描述例如要求admin后面紧跟斜杠或结尾rewrite ^/admin(?:/(.*))?$ /console/$1 last;如果你熟悉Nginx的location匹配也可以直接在location层做location ^~ /admin/ { rewrite ^/admin/(.*)$ /console/$1 last; }这样路径前缀的匹配交给locationrewrite只需要处理斜杠后面的部分。实际上左侧前缀越明确改写逻辑越简单越不容易把不相干的地址卷进来。3.5 多域名跳转和HTTP跳HTTPS的组合对于有多域名归属的老站经常会在一个server块里完成“非规范域名跳转”和“升级HTTPS”两件事。可以直接复用跳转能力server { listen 80; server_name old-site.com www.old-site.com; if ($host old-site.com) { rewrite ^(.*)$ https://www.old-site.com$1 permanent; } rewrite ^(.*)$ https://www.old-site.com$1 permanent; }这个配置的逻辑是访问老域名时先跳到带www的域名然后再跳到HTTPS版本。其实在这个场景下两次跳转可以合并成一行server { listen 80; server_name old-site.com www.old-site.com; return 301 https://www.old-site.com$request_uri; }无论请求来自old-site.com还是www.old-site.com最终都会301到https://www.old-site.com。这个例子是想强调很多步骤看起来要用rewrite逐步过渡但return 301可以一步到位。真正需要rewrite的通常是路径层面做了结构替换的场景域名层面的规整尽量用return完成。4. rewrite执行阶段与location匹配决定内部转发的最终归宿为什么同一个rewrite放在server块里和放在location块里表现完全不同这就要理解Nginx处理一个HTTP请求的大致流程。Nginx不是拿到请求后直接执行rewrite而是先解析配置确定这个请求要进哪个server然后在server内部先做一系列重写阶段操作再进入location匹配。4.1 server级rewrite先于location匹配执行server块内的rewrite规则会在Nginx寻找具体location之前执行。如果你在server块里写server { listen 80; rewrite ^/upload/(.*)$ /storage/uploads/$1 break; location /storage/ { alias /data/files/; } }请求/upload/avatar.png时Nginx会先执行server里的rewrite把它改写成/storage/uploads/avatar.png然后再匹配到location /storage/最终通过alias返回/data/files/uploads/avatar.png。这里有个很容易被忽视的执行顺序server块内的rewrite会在所有location匹配之前把URI改掉。所以如果你在rewrite规则里用了last它就会以改写后的URI重新做一次location查找等效于用户直接请求了新路径里的内容。4.2 location内的rewrite配合last让请求跳到对应的带正则location在实际项目中最常见的rewrite场景是我前面提到的伪静态转发。比如PHP环境通常会有这样的location配置location ~ \.php$ { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; }用户在外部访问/article/123.html时这个URL不会匹配到.php$这个location因为它的后缀是.html。所以必须把静态化的路径先改写成/index.php这样的URI并且让它重新进入location匹配阶段找到\.php$的规则。这一步就需要last。server { location / { rewrite ^/article/(\d).html$ /index.php?id$1 last; } }用了lastNginx会把/index.php?id123按新URI再次匹配location这次能进入location ~ \.php$交给PHP-FPM执行。如果用break替代结果就是当前location /里继续执行后续指令但因为没有fastcgi相关配置请求最终会以普通文件形式匹配多半以404告终。所以在“需要从普通路径跳到另一个专项location”的场景必须用last。4.3 防止内部路径被用户直接访问题如何利用rewrite的break有内部需求时问题可能反过来你反而不希望用户直接访问内部location。比如后台真实的脚本路径是/app/backend/render.php?idxxx但不想让别人猜测到这套结构只开放一个公开的/render?xxx入口Nginx内部再改成真实路径。这种情况下可以建一个只供内部跳转使用的命名location配合last从公共入口跳过去server { location /render { rewrite ^/render/?$ /app/backend/render.php?$args last; } location deny_direct { return 404; } location ~ ^/app/backend/ { try_files $uri deny_direct; } }用户访问/render?id1URI会被改写为/app/backend/render.php?id1然后重新匹配到location ~ ^/app/backend/接着正常被PHP处理。如果用户手动访问/app/backend/render.php?id1同样会进入这个location但由于没走公共入口的合法性校验用try_files把请求推到deny_direct返回404。这种隔离方式避免了内部PHP文件被外部直接扫描出来的风险。4.4 与proxy_pass联动的rewrite注意代理阶段后的URIrewrite的服务端内部转发除了给PHP这类fastcgi业务用另一个大方向就是反向代理。Nginx作为代理层时经常要把外部URL改写成后端服务能识别的内部路径。假设后端服务只识别/internal/order/detail这个地址但对外网关收到的地址是/gateway/order?orderId123。配置可以这样处理server { listen 80; location /gateway/ { rewrite ^/gateway/order/?$ /internal/order/detail break; proxy_pass http://backend_cluster; } }这里用break而不是last是因为当请求进入location /gateway/后改写完URL并不需要再让Nginx重新匹配其他location而是要直接在当前location内继续做代理操作。如果用last改写后的/internal/order/detail可能又会被别的location截获从而造成路由混乱。但要注意一个问题此时的proxy_pass如果写成proxy_pass http://backend_cluster;不带URI那么真正转发给后端时会使用改写后的URI也就是/internal/order/detail。如果写成proxy_pass http://backend_cluster/;带了一个URI路径即使location里已经用rewrite改写了proxy_pass里带URI的部分也可能替换掉改写后的路径结果可能与预期不符。Nginx中proxy_pass尾部是否带斜杠和alias的语义有得一拼非常容易踩坑。建议的方案是当你在location内用了rewrite改写URI并且希望改写后的URI传给后端proxy_pass就不要带URI保持proxy_pass http://upstream;这种裸上游写法。如果proxy_pass本身带了路径例如http://upstream/apis/那它跟location前缀之间有自己的一套拼接规则此时rewrite是否生效会取决于正则location的匹配方式。很多网上教程建议只要涉及复杂改写加代理就直接把rewrite目标设为变量配合命名location可以绕开proxy_pass的URI拼接迷雾server { location /gateway/ { rewrite ^/gateway/order/?$ /internal/order/detail break; proxy_pass http://backend_cluster$uri$is_args$args; } }这里用$uri让proxy_pass拼接改写后的URI$is_args判断是否带参数$args携带参数。这种做法更透明、更可控但如果后端地址本身已经配置了路径前缀就要确保最终拼接结果符合后端预期不然要去掉或调整URI部分。5. 调试rewrite与规避经典大坑全凭这套排查链路rewrite虽小却牵扯正则、变量、阶段、缓存排障过程必须一环一环拆解。结合我自己的经验把最常见的几类故障和排查办法列出来。5.1 配置检查和日志定位的基本功写完rewrite先别着急reload执行nginx -t这一步能检查语法错误和server块里重复监听的冲突。语法没问题再执行nginx -s reload。但语法正确不表示逻辑正确这时候要看error日志。默认的error_log级别通常不打印rewrite匹配细节需要临时提升error_log logs/error.log notice;如果要更细的匹配过程可以开到debug级别并通过--with-debug编译时开启日志支持。生产环境不建议长时间开debug它会产生海量日志影响磁盘IO一般排障完再调回error级别。除了日志curl是最快的验证手段curl -I http://example.com/old/123查看响应码和Location头判断是301还是302Location地址中的路径和参数是否符合预期。这条命令可以在不打开浏览器的情况下反复测试不同URL变体非常适合我们批量验证规则。5.2 重定向循环URL在两个规则之间反复套娃重定向循环是最经典的rewrite问题。比如你写了一条规则希望把不带www的地址跳转到带www的地址server { listen 80; server_name example.com; rewrite ^(.*)$ http://www.example.com$1 permanent; }但如果还有一个server块监听www.example.com并且里面也有类似的rewrite规则比如把路径去掉某前缀而新路径又符合第一条rewrite的条件请求就会在两个server之间反复跳转直到浏览器提示“重定向次数过多”。还有一个常见的循环案例是把HTTP跳HTTPS的规则写在HTTPS的server里。比如server { listen 443 ssl; rewrite ^(.*)$ https://example.com$1 permanent; }当请求已经通过HTTPS到达这个server后rewrite又把同一URL重写到HTTPS地址但这条server本身就是处理该地址的于是请求又一次进入同一个server再次执行rewrite无限循环。HTTP跳HTTPS的规则只应该放在监听80端口的server块中443的server块里正常返回业务内容。定位这类循环除了看error日志的rewrite or internal redirection cycle提示还需要打开浏览器的网络面板看请求链路中Location头的变化规律一旦发现域名或路径在两步之间反复出现基本就能判断是哪几个规则在轮流命中。5.3 rewrite后404先确认内部地址能被location和实际文件接住另一个高频问题rewrite配置后页面还是404。比如这样location /download { rewrite ^/download/(.)$ /files/$1 last; }访问/download/setup.exe理论上应该转发到/files/setup.exe。但如果location /files没有配置正确的alias或root规则或者/files目录在文件系统中不存在最终依然会得到404。这种情况下的排查思路是确认rewrite确实执行了最直接的办法是在error日志查匹配到的规则行。确认改写后的URI能匹配到哪个location可以临时在对应的location块中加一段return 200来验证。确认location下的root/alias路径正确文件真实存在于磁盘中。有一点要避免在win环境或某些IDE里路径大小写和结尾斜杠经常出问题。文件系统严格区分大小写时/Files/和/files/是两个目录rewrite生成的目标必须与实际路径大小写一致。5.4 别忘了永久跳转的缓存问题这个坑严格来说不是rewrite写错了而是使用301后的上线流程问题。rewrite配置中加permanent会返回301这会导致浏览器、CDN和搜索引擎同时缓存这条跳转。有一次我在测试环境调试旧链接迁移先用permanent跳转后来发现目标URL的路径层级需要调整改完后反复刷新浏览器看到的还是旧地址。原因就是浏览器已经把请求/old/123缓存为/new/abc后端的修改根本不会被触发。解决方法是把这类临时调整的规则先写成默认的302或者redirect标志等最终确认后再上线301。如果已经发出过错误的301而且用户手里已经缓存只能建议用户无痕模式或清理缓存。对于线上大范围业务这几乎不可控。所以凡是涉及永久跳转的配置务必在正式发布前做足测试。5.5 关于Nginx里臭名昭著的if我建议守住两个原则Nginx社区长期流传一句话叫“if is evil”核心原因在于if块中的指令行为在某些历史场景下不符合直觉容易产生地雷。但在实践中rewrite相关的if使用频率非常高也并非完全不能碰。结合了大量运维经验后我给自己定的两个原则是第一能用map或location表达的条件判断不要用if表达。参数切换、域名切换、UA切换这些map变量和location匹配都能以更结构化的方式完成。第二如果不得不用if只允许在其中使用return、rewrite、set这三种指令不把proxy_pass、fastcgi_pass等其它指令放进if块。比如下面的配置location /admin/ { if ($remote_addr !~ ^192\.168\.) { return 403; } rewrite ^/admin/(.*)$ /management/$1 last; }这个配置中if只做了条件拦截返回403rewrite在if块外执行逻辑简单清晰。遵守这两个原则后遇到的多数组件兼容问题都能被提前绕开。6. rewrite之外的扩展什么时候用map和location代替重写最后补充一种容易被忽略的思路并非所有URL映射问题都要靠rewrite逐个写规则。当规则数量多、URL和跳转目标关系明确时map是更优雅的方案。map指令通常定义在http块中它做的事情本质上是建立一个字典映射。例如map $uri $new_uri { default /404; /page/about /about-us; /page/contact /contact-us; /page/terms /legal/terms; }然后在server里server { listen 80; server_name example.com; if ($new_uri) { rewrite ^ $new_uri permanent; } }这段逻辑的意思是当请求的URI能在map表中找到对应新地址时执行跳转找不到时因为没有$new_uri变量或者变量内容为空就不执行rewrite继续走正常处理流程。相比一条条写rewrite正则map方案的可维护性明显更强。尤其是URL映射清单经常变化时只需要改map这块不用动server里的逻辑。它的另一个优点是map是启动时一次性加载的哈希结构性能远好于反复跑正则匹配。类似的场景如果跳转规则只是针对某个目录或参数条件能用location的多层嵌套表示的也不要硬写成规则location /old/ { location /old/static/ { try_files $uri 404; } rewrite ^/old/(.*)$ /archive/$1 permanent; }这种写法精准地把静态文件请求和动态跳转请求分流开。静态文件仍然走正常文件读取其它路径才进入跳转逻辑规则之间不会再互相干扰。总之rewrite是Nginx抵御URL变化的一把利器但使用它的关键是理解它在请求处理流程中所处的阶段明确它究竟是写给客户端看的跳转还是写给内部路由看的重写。希望上面这些从场景出发的配置思路和踩坑经验能让你在下次处理Rewrite需求时少花一点时间在“为什么没按我的想法跳”上。

相关新闻

最新新闻

三相并联型APF仿真全解析:PI控制、谐波补偿与SVPWM调制

三相并联型APF仿真全解析:PI控制、谐波补偿与SVPWM调制

先说明一下我做这个项目的场景:实验室有一台模拟非线性负载的整流设备,电网侧5、7次谐波超标比较严重,THD到了27%左右。我们最初的想法当然是上APF,但直接买一台成品柜子去做补偿测试成本高、风险也不小。所以我先在Simulink里把三…

2026/9/8 16:15:18
Python自动化Perforce实战课

Python自动化Perforce实战课

正在进行实战授课的这门课程, 它专注于凭借脚本去简化版本控制流程, 而担任主讲的是一位在育碧供职超过6年的全职技术美术工作者, 是这样的情况。他曾投身于多个3A级游戏项目的开发工作, 像《看门狗3》, 还有《孤岛惊魂6》, 以及尚在开发进程中的《超越善恶2》。该课程的核心目…

2026/9/8 16:15:18
三位独立开发者对RLHF奖励模型推理引擎做了一次彻底测速

三位独立开发者对RLHF奖励模型推理引擎做了一次彻底测速

此研究是由三位各自独立的研究者开展完成的, 其于2026年7月22日以预印本的形式予以发布, 论文所具有的编号是arXiv:2607.19712, 那些存有兴趣想要深入去了解一番的读者能够借助该编号去查询完整的论文。**故事从一个被忽视的角落开始**在人工智能范畴当中, 存在着一种技术名为R…

2026/9/8 16:15:18
FPGA上板调通测试实战指南:从仿真到板级稳定运行的关键方法

FPGA上板调通测试实战指南:从仿真到板级稳定运行的关键方法

搞数字逻辑实验的同学,大概率都有过这种体验:仿真波形怎么测怎么对,逻辑功能挑不出毛病,结果代码一下到板子上,LED死活不亮,数码管乱跳,或者输出波形跟预期完全对不上。这时候最容易怀疑人生&am…

2026/9/8 16:15:18
Mojo编程语言:Python的“性能升级版”,AI开发者的新利器!

Mojo编程语言:Python的“性能升级版”,AI开发者的新利器!

如今, 在AI领域圈子当中, 最为热门流行的编程语言, 那绝对无疑就是Mojo了!它是由一家公司推行问世的, 这家公司的创始人里面有包括Chris在内的人员, 这位Chris同时还是LLVM以及Swift的创造者呢。它被誉为是专门“为人工智能这个领域量身打造所应运而生”的语言。它存…

2026/9/8 16:15:18
Muse Code三档订阅上线,编程助手性价比时代来临

Muse Code三档订阅上线,编程助手性价比时代来临

最近AI编程助手这个赛道算是彻底卷起来了,从年初各家还在拼模型参数、拼代码补全的"准不准",到眼下已经变成拼落地场景、拼定价策略、拼谁能真正融进开发者的日常。Muse Code结束测试、推出三档订阅方案这件事,放在这个大背景下看就…

2026/9/8 16:10:18