.htaccess配置重写规则后POST请求数据丢失问题排查
.htaccess重写规则导致POST数据丢失修复
问题背景
站点通过.htaccess配置重定向、重写规则实现去除文件扩展名访问,POST方法提交数据时会在规则处理阶段丢失,单独给重定向规则加非POST判断后问题仍然存在。
当前使用的.htaccess配置:
Options +FollowSymLinks RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^resume-.*/([0-9]+)$ /book.php?id=$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^channel-books/([0-9]+)/([0-9]+)/([0-9]+)$ /getChannelBooks.php?idChaine=$1&page=$2&orderby=$3 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^channel-books/([0-9]+)/([a-zA-Z]+)$ /getChannelBooks.php?idChaine=$1&$2 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^chaine-.*/([0-9]+)$ /model_contenu_de_chaine.php?idChaine=$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^search-([0-9]+)$ /search_page.php?search=$1 [QSA] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^audio-([0-9]+)$ /audiobook.php?id=$1 [QSA] RewriteRule ^$ /index.php [NC] RewriteCond %{REQUEST_METHOD} !POST RewriteCond %{THE_REQUEST} \s/([^.]+)\.php [NC] RewriteRule ^ /%1 [NE,L,R=307] RewriteRule ^([^\.]+)$ $1.php [NC]
POST请求示例:
处理用户登出逻辑的destroy_session.php代码如下:
if(isset($_POST['action']) && $_POST['action'] == 'logout'){ session_unset("username"); session_destroy(); }
故障原因
- 前6条业务路由规则中,搜索、音频详情、首页的三条规则漏加
L(last)标记,规则匹配完成后不会终止重写流程,会继续向下匹配最后一条追加.php后缀的规则,触发二次重写。 - 最后一条无后缀路径追加.php的内部重写规则,没有加文件/目录/软链接存在性校验,也没有加
L标记,会导致重写循环,Apache检测到循环后会自动触发外部重定向,POST数据在外部跳转过程中被丢弃。 - 仅给
.php转无后缀的307跳转规则加POST判断不足以覆盖所有重写场景,内部重写的循环问题没有被拦截。
修复后的配置
直接替换原有.htaccess内容即可:
Options +FollowSymLinks RewriteEngine On RewriteBase / # 业务路由规则 全部补全L标记,匹配后立即终止重写 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^resume-.*/([0-9]+)$ /book.php?id=$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^channel-books/([0-9]+)/([0-9]+)/([0-9]+)$ /getChannelBooks.php?idChaine=$1&page=$2&orderby=$3 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^channel-books/([0-9]+)/([a-zA-Z]+)$ /getChannelBooks.php?idChaine=$1&$2 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^chaine-.*/([0-9]+)$ /model_contenu_de_chaine.php?idChaine=$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^search-([0-9]+)$ /search_page.php?search=$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^audio-([0-9]+)$ /audiobook.php?id=$1 [QSA,L] RewriteRule ^$ /index.php [NC,L] # 仅非POST请求触发.php到无后缀的307跳转,保留POST请求数据 RewriteCond %{REQUEST_METHOD} !POST RewriteCond %{THE_REQUEST} \s/([^.]+)\.php [NC] RewriteRule ^ /%1 [NE,L,R=307] # 无后缀路径追加.php的内部重写,补全资源存在校验+L标记,避免重写循环 RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-l RewriteRule ^([^\.]+)$ $1.php [NC,L]
测试注意事项
- 修改配置后先清空浏览器缓存,旧的307跳转记录会被浏览器强缓存,导致测试结果不准
- POST表单的action地址写带.php后缀或者无后缀都可以正常传参,不需要修改现有业务代码
- 如果后续新增静态资源,不需要调整规则,新增的三条资源存在判断会自动跳过静态资源的重写
内容的提问来源于stack exchange,提问作者Ayoub Mafkoud
相关产品推荐
相关产品推荐

