2019-09-07

[CodeIgniter] 有關網址出現特定中文字而讓 apache 出現 404 找不到網頁的可能解決方式

開始在用 CodeIgniter (之後都稱 CI 或 ci) 也有三個月了(對…這份新工作已做滿試用期三個月了)

因為以前真的沒怎麼碰過 php mvc 的 framework ,本來真的會擔心入手困難,但這三個月是沒有遇上大問題。

本來在操作連結時…都一直使用純英數字的連結方式,像 http://localhost/news/1 這種單純的連結…

後來有想到把最新消息的 title 文字給放上網址…像 http:/localhost/news/1/20190906-今天放豪雨假 之類的。

在大部分的情況下都算正常,比較常會有的小問題是可能要去設定 route 的問題…但這其實也還好,試個幾次就能試出個所以然來,而且 CI3 之後 route 可以用 regex 表示法來操作,也方便不少…

但後來不知為什麼很認真的在試每個消息的連結時會發現有幾個特別的連結會出現 apache 404 的回應;為什麼我要特別提到是 apache 404,是的…因為如果在使用特定的 mvc framework 之後,應該會有人發現其實這一類的 framework 的優先權會變的很高,尤其是像 error log、404、500 這種操作都不太會給 apache (因為我主要在用 apache ,反正我所指的是 web service ) 去生成

所以當我發現這一些連結出現的 404 並不是我在 ci 中自定的或預設的,而是 apache 本身的 404 時就覺得這問題有些奇怪…

我發現大概在 url 出現「全」、「慧」、「公」等中文字時,就會造成 apache 404 …

我本來一直糾結在是 ci 上的問題,後來就改成去找 apache 的,但實際上很怪的是,這問題並不是在 apache 的所有目錄或 vhost 都會有,而是 ci 的目錄或 vhost 才會出現,所以就一直苦惱這問題許久…久,大概一個工作天的下午吧,後來回到住處時又再一次把當初 ci 從建置時有操作過的設定拿出來想了一輪,這才發現問題所在…

一般在 url 上的文字基本上會被做 urlencode 來轉譯,而在英數字並不會直接被轉,而是為了讓人方便辨視,還是會保留原來的文字,但像符號或全形文字(中、日、韓這種)就會被轉成 %xx%yy%zz (有時會是三組代表一個全形文字,有時會是四組)…而這種被轉過的若是出現 %85 時就會跑出 apache 404

雖然找到這個慣性,但一開始還是覺得為什麼會是在 apache 這邊拋出 404 …

後來在 ci 底下的 .htaccess 找到狀況…

在 ci 相關的文章,都有說到為了不要讓網址出現 http://localhost/index.php/xxxx 的這種型式,都要說可以去寫個
RewriteRule ^(.*)$ ./index.php/$1 [L]
這種 rewrite 來讓網址 http://localhost/xxxx => http://localhost/index.php/xxx

但似乎就是這邊出了問題…

我這邊測了 http://localhost/99/abc 跟 http://localhost/99/公 的兩個連結,然後 rewriterule 是 index.php?a=$1,然後看 rewrite log ,結果如下
http://localhost/99/abc
[perdir /php7/99/] strip per-dir prefix: /php7/99/abc -> abc
[perdir /php7/99/] applying pattern '^(.*)$' to uri 'abc'
[perdir /php7/99/] RewriteCond: input='/php7/99/abc' pattern='!-f' => matched
[perdir /php7/99/] RewriteCond: input='/php7/99/abc' pattern='!-d' => matched
[perdir /php7/99/] rewrite 'abc' -> './index.php?a=abc'
split uri=./index.php?a=abc -> uri=./index.php, args=a=abc
[perdir /php7/99/] add per-dir prefix: ./index.php -> /php7/99/./index.php
[perdir /php7/99/] strip document_root prefix: /php7/99/./index.php -> /99/./index.php
[perdir /php7/99/] internal redirect with /99/./index.php [INTERNAL REDIRECT]
[perdir /php7/99/] strip per-dir prefix: /php7/99/index.php -> index.php
[perdir /php7/99/] applying pattern '^(.*)$' to uri 'index.php'
[perdir /php7/99/] RewriteCond: input='/php7/99/index.php' pattern='!-f' => not-matched
[perdir /php7/99/] pass through /php7/99/index.php


http://localhost/99/公
[perdir /php7/99/] strip per-dir prefix: /php7/99/\xe5\x85\xac -> \xe5\x85\xac
[perdir /php7/99/] applying pattern '^(.*)$' to uri '\xe5\x85\xac'
[perdir /php7/99/] pass through /php7/99/\xe5\x85\xac
可以看到,在正常的 99/abc 時,rewrite 會很正常的在確定 pattern='!-f', '!-d' 是符合之後就 rewrite to ./index.php?a=abc 了,但 99/公 會把 公 轉成 \xe5\x85\xac 之後,就沒進入 pattern match 判斷,就直接 pass through 過去成為是一個「實際連結」,而因為實際連結中,在 99 底下並沒有「公」或「\xe5\x85\xac」的檔案或資料夾,所以就丟出 404 了…

至於為什麼這一些特定的中文字會這樣,我在網路上有看到說 \x85 有可能會判是 unicode 16進位的 85 ,說這是斷行符號…ok,斷行符號,但為什麼會造成 rewrite 沒去做 pattern match…這就沒有特別說明了

至於解決這個狀況的方式,就是把
RewriteRule ^(.*)$ ./index.php/$1 [L]

改成
RewriteRule ^([\w\W]*)$ ./index.php/$1 [L]


RewriteRule ^([\s\S]*)$ ./index.php/$1 [L]

沒有留言: