用nginx map预判移动设备并设$is_mobile变量,location ^~ /m/内用rewrite ^/m/(.*)$ /$1 break剥离前缀,再以fastcgi_param request_uri $uri;确保php获取重写后路径,避免404。

用 Nginx 的 rewrite 解决移动端和 PC 端路由冲突,关键不是“多写几条规则”,而是让同一套后端逻辑(比如 Laravel 或纯 PHP)只配置一次路由,由 Nginx 在请求到达前统一剥离或补全路径前缀(如 /m/),同时确保 PHP 正确拿到重写后的 URL。否则就会出现:Nginx 重写了,但 PHP 还在读 /m/register,结果找不到 /register 对应的路由,直接 404。
先识别设备类型,再决定是否剥离 /m/
不推荐在每个 location 里用 if + rewrite 判断 User-Agent——性能差、易出错、难维护。正确做法是用 map 提前定义变量:
- 在
http块里加一段预判逻辑:
default 0;
~*android 1;
~*iphone 1;
~*ipad 0;
~*mobile 1;
}
这样就把移动设备判断抽象成一个开关变量 $is_mobile,后续所有规则可复用,且不触发 if 的隐式性能惩罚。
对移动端请求,内部重写掉 /m/ 前缀
目标是:用户访问 https://example.com/m/login,Nginx 内部把它当作 /login 去匹配 PHP 路由,浏览器地址栏不变。
- 在
server块中添加:
rewrite ^/m/(.*)$ /$1 break;
}
注意这里用 break 而不是 last:因为 break 会停止当前 location 内所有 rewrite,并继续用新 URI(即 /login)执行本 location 下的其他指令(如 try_files 或 fastcgi_pass),不会重新匹配 location —— 这正是我们想要的。
如果用了 last,Nginx 会拿着 /login 重新走一遍 location 匹配流程,可能又掉进另一个 location 块,甚至循环或错配。
修正 PHP 获取的 REQUEST_URI
这是 404 的真正元凶。默认 fastcgi_param REQUEST_URI $request_uri; 传的是原始请求路径(含 /m/),PHP 框架靠它解析路由,当然失败。
- 必须改成传重写后的 URI:
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
$uri 是 Nginx 内部处理后的标准化路径(已去掉 /m/),$fastcgi_script_name 确保脚本名正确。这两项配合,Laravel、ThinkPHP 等框架才能正常识别 /login 而非 /m/login。
顺带检查:try_files $uri $uri/ /index.php?$query_string; 中的 $uri 也是重写后的值,所以静态资源也能正确定位。
补充:PC 端强制跳转到无前缀路径(可选)
如果希望桌面用户访问 /m/login 时自动跳到 /login(SEO 友好或统一入口),可用 return 做外部重定向:
- 加在
server块顶层(不在 location 内):
return 301 $scheme://$host$request_uri;
}
⚠️ 注意:这个 if 仅用于跳转,不参与路径改写;且必须放在所有 location 外,避免被覆盖。更稳妥的做法是把跳转逻辑也收进 map + return 组合,但对多数项目,上面写法足够清晰。











