[开发]SoSite 重写规则备忘

👑Lv.11 元老 🌞 骨干会员
2026-10-24 00:11:39

本文由 FlintHub 开发文档归档发布。原始文档日期见标题前缀,来源:8月13日备份/重写规则备忘.md。

SoSite 重写规则备忘

用途:SoSite 是前端控制器路由(/thread/149、/blog/xxx 等路径由 index.php 解析),

需要在 Web 服务器配置"伪静态"规则,把非真实文件/目录的请求转发到 index.php。

配置前请确认站点能正常访问 /index.php(PHP 解析正常),再配置重写规则。

★ 核心原则(血泪教训):

1. 静态资源目录(assets、plugins)绝不能进安全隐藏规则——否则主题/插件资源全部 404,功能"看似失效"。

2. nginx 不要用多端口 upstream 轮询做"免配置切版本"——死端口会拖慢每个请求(实测 2.4s)。

3. 服务器级重写规则都是"治标":换环境(nginx/apache/IIS)就得重新配;真正"治本"是程序生成服务器无关的 URL(见 §六)。


一、原理:SoSite 路由与重写规则的关系

SoSite 所有动态页面(帖子、博客、搜索、用户中心、后台)都是前端控制器模式:

所有请求进入 index.php,由 Router 根据 $_SERVER['REQUEST_URI'] 解析路由。

服务器重写规则的作用只有一个:把"看起来像文件路径但不是真实文件"的请求(如 /thread/149)转交给 index.php。

  • 真实存在的文件(/assets/css/admin.css、/index.php 等)→ 服务器直接返回,不经过重写
  • 不存在的路径(/thread/149、/blog/xxx)→ 重写规则转发给 index.php

所以规则是否正确的判断标准:

  • 动态页(/thread/149)→ 200(被重写)✅
  • 静态文件(/assets/...)→ 200(直接返回,未被误伤)✅
  • 核心目录(/app/...、/protected/...)→ 403/404(被安全规则拦下)✅

二、Apache(通用,解压即用)

原理:Apache 支持目录级 .htaccess,把规则文件放到站点根目录即可自动生效,

无需改 httpd.conf(需 AllowOverride All,绝大多数虚拟主机默认开启)。

做法:在站点根目录创建/覆盖 .htaccess:

# ============================================================
# SoSite 伪静态规则(Apache)
# ============================================================

    RewriteEngine On

    # 核心:非真实文件/目录的请求统一转发到 index.php
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.php [L,QSA]

注意事项:

  • 子目录部署(如 /sosite/):.htaccess 放子目录即可,RewriteRule 目标 index.php 为相对路径自动解析,无需额外处理。
  • HTTPS 强制跳转(可选,加在 RewriteEngine On 之后):

```apache

RewriteCond %{HTTPS} off

RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

```

  • 安全说明:Apache 下不需要额外隐藏目录规则——app/、protected/ 里的 .php 被直接访问时会执行但无入口输出;如需加固可加:

```apache

# 禁止直接访问核心目录(可选加固)

RewriteRule ^(app|protected|update)(/|$) - [F,L]

```


三、IIS(通用,需 URL Rewrite 模块)

原理:IIS 使用目录级 web.config 配置 URL Rewrite(需已安装 IIS URL Rewrite 模块,绝大多数 Windows 主机自带)。

做法:在站点根目录创建/覆盖 web.config(以下为完整版,含安全规则):



    
        
            
                
                
                    
                    
                    
                    
                
                
                
                    
                    
                    
                
            
        

        
            
                
                
                    
                    
                

                
                
                    
                    
                

                
                
                    
                    
                        
                        
                        
                        
                    
                    
                

                
            
        
    

注意事项:

  • 前提:IIS "处理程序映射" 中必须已配置 PHP FastCGI(.php → php-cgi.exe),否则 .php 会被当作静态文件直接下载,config.php 等敏感文件会泄露,规则拦不住这一步。
  • 子目录部署(如 /sosite/):web.config 放子目录即可,规则自动适用于该目录。
  • hiddenSegments 与 rewrite 403 的区别:hiddenSegments 返回 404(隐藏存在性,更隐蔽);rewrite 规则返回 403(明示拒绝)。两者对核心目录做了双保险,是合理的。

四、Nginx(规则通用,接入方式因环境而异)

规则本身是通用的(和 Apache/IIS 等价),但 nginx 没有目录级配置文件(不支持 .htaccess / web.config 机制),

必须把规则放进站点 server 配置里,且不同环境接入位置不同——所以 nginx 需要使用者按自己环境配置。

4.1 规则片段(通用,二选一)

写法 A(if + rewrite,与 Apache 语义最接近):

location / {
    index index.php index.html;
    # SoSite 伪静态:非真实文件/目录请求转发到 index.php
    if (!-e $request_filename) {
        rewrite ^/(.*)$ /index.php/$1 last;
    }
    ...
}

写法 B(try_files,官方推荐、避免 if 指令):

location / {
    index index.php index.html;
    try_files $uri $uri/ /index.php?$query_string;
    ...
}

4.2 各环境接入位置

环境配置位置
宝塔面板站点 → 设置 → 伪静态 → 选择/粘贴规则(保存后自动生效)
LNMP 一键包/usr/local/nginx/conf/vhost/域名.conf 的 location / 内
phpstudyExtensions/Nginx1.15.11/conf/vhosts/站点.conf 的 location / 内;或参考程序根目录 nginx-server.conf
自编译/Docker nginx站点 server 块的 location / 内

4.3 PHP 处理(必须配套)

nginx 下 PHP 由 fastcgi_pass 处理,需同时配置 PHP location(端口/方式因环境不同):

location ~ \.php(.*)$ {
    fastcgi_pass   127.0.0.1:9000;   # ← 端口按环境实际 PHP 版本/套接字改
    fastcgi_index  index.php;
    fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
    fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
    fastcgi_param  PATH_INFO  $fastcgi_path_info;
    include        fastcgi_params;
}

  • 宝塔/LNMP:fastcgi_pass 通常为 unix:/tmp/php-cgi-xx.sock 或 127.0.0.1:9000,以环境默认值为准。
  • phpstudy:端口映射固定 9000→php7.3 / 9001→php8.0.2 / 9002→php8.2.9(见 COM/xp.ini),

切换 PHP 版本后若 502,把端口改成对应版本即可。

  • 子目录部署:root 指向站点目录,规则不变;若部署在二级目录需调整 try_files/rewrite 前缀。

4.4 ★ 避坑:不要用多端口 upstream 轮询做"免配置切版本"

错误示范(曾真实采用并踩坑,见 §五.2):

# ✗ 错误:三端口轮询想实现"切 PHP 版本免配置",实测每个请求被死端口拖慢
upstream sosite_php_backend {
    server 127.0.0.1:9000 max_fails=1 fail_timeout=5s;   # 死端口(未在跑)
    server 127.0.0.1:9001 max_fails=1 fail_timeout=5s;   # 唯一活着的端口
    server 127.0.0.1:9002 max_fails=1 fail_timeout=5s;   # 死端口
}
...
location ~ \.php(.*)$ {
    fastcgi_pass sosite_php_backend;   # ← 轮询到死端口 → 连接失败重试 → 延迟
}

正确做法:单端口直连当前 PHP 版本:

location ~ \.php(.*)$ {
    fastcgi_pass 127.0.0.1:9001;   # ✓ 当前 PHP 8.0.2 端口,无轮询、无延迟
    ...
}

切换 PHP 版本后 502,改一行端口即可(注释写明 9000/9001/9002 对应关系)。

如需真正"免配置切版本",正确方向见 §六(程序级方案)。


五、实战踩坑记录(重要)

5.1 IIS:hiddenSegments 误藏 assets/plugins → 主题切换"失效"

现象:服务器上用 web.config 后,后台切换主题"看似保存了但没变化"。

根因:web.config 的 hiddenSegments 里多加了 assets(还有 plugins):



    
    
       

  • 主题变体 CSS 在 /assets/css/themes/modern-xxx.css,全站静态资源都在 /assets/ 下
  • hiddenSegments 是 IIS 请求过滤,命中即 404,且在 rewrite 之前拦截
  • 结果:主题切换后变体样式 404 加载不出来 → 看起来"切换失效"
  • 而主路由规则里还写了排除 assets(想放行),与 hiddenSegments 自相矛盾

教训:静态资源目录(assets、plugins)绝不能进任何安全隐藏规则。

hiddenSegments 只隐藏核心代码目录(app / protected / update)。

plugins 同理——插件资源 /plugins/{id}/assets/... 是直连的,隐藏 plugins 连插件样式一起 404。

5.2 Nginx:upstream 多端口轮询 → 死端口拖慢每个请求

现象:本地改用 nginx-server.conf(含三端口 upstream)后,页面加载有明显延迟(实测 /thread/149 2.4s),服务器上正常。

根因:

  • phpstudy 固定端口映射 9000/9001/9002,但同时只有当前 PHP 版本一个端口在监听(如只有 9001)
  • upstream 轮询把请求轮流发给三个端口,命中 9000/9002 死端口时连接失败 → nginx 重试下一个 → 每次请求平均多撞 1-2 次死端口 → 延迟数倍
  • 服务器上是单端口 fastcgi_pass 直连,不走轮询,所以没延迟

修复:去掉 upstream,改回单端口直连(见 §4.4),延迟从 2.4s 降到 0.34s。

教训:upstream 轮询只适用于"多个后端都在提供服务"的场景(如负载均衡);对"多版本 PHP 但同一时刻只有一版在跑"的场景,轮询= 撞死端口 = 延迟。要"免配置切版本"应该走程序级方案(§六),而不是服务器级投机。

5.3 共性结论

教训本质正确做法
hiddenSegments 藏 assets安全规则误伤静态资源只藏核心目录,永不藏 assets/plugins
upstream 轮询死端口负载均衡用错场景单端口直连;切版本改端口
服务器级规则换环境要重配治标不治本程序生成服务器无关 URL(§六)


六、治本方向:程序生成服务器无关的 URL(免任何服务器配置)

为什么说是治本:服务器级重写规则(.htaccess / web.config / nginx 配置)每换一个环境(nginx、apache、IIS、宝塔、LNMP、cPanel…)都要重新配置——这是"治标"。

参考实现(xiunox 同款思路):

xiunox 的 url() 函数默认 url_rewrite_on=0,所有链接生成 index.php?xxx.htm 查询参数格式——查询参数是服务器无关的,nginx/apache/IIS/Windows 通吃,装完即用,不需要任何重写规则。伪静态(url_rewrite_on=1+)只是可选增强,开启才需要服务器规则。

SoSite 已有基础:Router.php 已内置 ?r= fallback(index.php?r=/forum → 路由解析为 /forum)。

若实施程序级方案,改动集中在:

  1. TemplateCompiler::url() 加开关:off 时生成 index.php?r=/forum 格式
  2. Controller::redirect() 同步生成
  3. Router ?r= fallback 放开 admin 限制(有 isAdmin + 二次验证保护,风险可控)
  4. 前端硬编码路径(/api/vote、/api/upload 等)动态化

何时做:当"换环境零配置"成为硬需求(分发安装包给他人、多服务器迁移频繁)时实施;

本地/单一环境用服务器级规则(§二~§四)即可,简单直接。


七、验证清单

配置完成后,Ctrl+F5 强刷,逐项检查:

  1. 动态页:访问 /thread/149(或任一帖子)→ 正常显示(非 404)→ 重写规则生效
  2. 静态文件:访问 /assets/css/admin.css → 正常返回 → 重写规则未误伤静态资源
  3. PHP 入口:访问 /index.php → 正常 → PHP 解析正常
  4. 核心目录(IIS):访问 /app/、/protected/ → 403/404 → 安全规则生效
  5. 敏感文件(IIS):访问 /config.php → 403 → 防泄露规则生效
  6. 主题切换:后台切主题 → 前台样式跟随变化 → assets 未被误伤(若失效查 hiddenSegments)
  7. 性能:curl -w "%{time_total}" 测 /thread/149 → 应 < 1s(若数秒延迟,查是否 upstream 轮询死端口)

故障速查:

现象原因处理
所有页面 404重写规则未生效/未加载检查规则是否在正确位置、是否 reload
静态文件 404规则误伤静态资源检查 !-e/!-f 条件、hiddenSegments 是否藏了 assets
动态页 502fastcgi/PHP 配置错误检查 fastcgi_pass 端口、PHP 是否运行
页面慢(数秒)upstream 轮询死端口改单端口直连(§4.4)
主题切换没反应IIS hiddenSegments 藏了 assets从 hiddenSegments 移除 assets(§5.1)
换服务器后伪静态失效服务器级规则需重配用程序级方案(§六)


本备忘由 SoSite 项目维护,规则与 SoSite V5.0.0 前端控制器路由配套。

更新记录:2026-08-11 重写——新增 §五 实战踩坑(IIS hiddenSegments 误藏 assets、Nginx upstream 轮询死端口延迟)、§六 治本方向、§七 验证与故障速查。

轻量级、高性能、零 MySQL 依赖的PHP社区系统。
| 浏览 1 次 | 回复 0 次

全部回复 (0)

暂无回复

请 登录