apache RewriteRule 的語法以匹配 % 編碼的 URL?(修復字元編碼問題;windows-1252 <=> utf-8)
我託管了一個 URL 中包含“”的網頁,與託管靜態文件
project²
的磁碟目錄相匹配。project²
基於 java 的客戶端使用此頁面從 URL(生物資訊學軟體IGV)載入數據。我的頁面以
http://localhost:60151/load?file=http://example.org/project²/some/data/file.bam
. 在瀏覽器中點擊這些連結將導致 IGV 客戶端(在 localhost 上執行)向GET http://example.org/project²/some/data/file.bam
我的伺服器請求。✅ Linux/Mac 上的 IGV 通過將此 URL 請求為 UTF-8 編碼
²
=來響應%C2%B2
,一切正常。❌我新獲得的Win-10使用者的客戶端請求
²
=%B2
(windows-1252編碼),導致404-not-found。在嘗試了幾十件事之後,我對如何幫助這個使用者束手無策。
我的印像是我應該能夠在伺服器端動態重寫錯誤編碼的 URL,以便它們最終仍然提供所需的數據,但我不知道使規則模式匹配的神奇字元組合轉義字元。
我已經嘗試過的事情
仔細檢查 404 不是網路問題;我看到
GET %B2
我ssl_access_log
的 with404
作為返回的狀態碼,所以它確實是伺服器在做它。“正確”方式:在將 URL 提供給客戶端之前對 URL 進行 UrlEncoding。Perl
URI::Encode
encode_uri
將²
變成%C3%82%C2%B2
(顯然ò
?)不知何故更錯誤?三重檢查提供載入 URL 的網頁是否為 utf-8
- 它提供標題
Content-Type: text/html; charset=UTF-8
- 設置
AddDefaultCharset UTF-8
在httpd.conf
- 似乎編碼資訊沒有從 webbrowser API-link-click 傳輸到 Java 程序中
通過符號連結將目錄“加倍”~~
projectª -> project²
~~並且project%B2 -> project²
(編輯:ª 沒有任何關係;不知道我從哪裡得到的ª
是 UTF8 匹配%B2
)試圖以
mod_rewrite
幾種不同的方式將“壞”的 URL 變成好的 URL,但似乎都沒有:RewriteEngine on # RewriteRule Pattern Substitution [flags] RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE] # encoded 'bad' request, unencoded redirect RewriteRule (.*)²(.*) $1%C2%B2$2 [B,NE] # config file is utf-8 encoded, so this is senseless. RewriteRule (.*)%B2(.*) $12$2 [B,NE] # doesn't match? RewriteRule (.*)TZZT(.*) $1test$2 # works, so RewriteEngine is working
RewriteRule和RewriteRuleFlags文件也不能幫助我理解我應該如何編碼
Pattern
-part 以便它可以工作:-(類似的問題在這裡
- Apache .htaccess 能否將編碼 URI 中的百分比編碼從 Win-1252 轉換為 UTF-8?-> 外部編碼程序
rewritemap
似乎有點矯枉過正,因為它實際上只有一個文件夾project²
,所以我的範圍更小。- 在 NGinX 中將ASCII 百分比編碼的位置重寫為其 UTF-8 編碼的等效相同問題,指向上述 Apache 問題。
解決方案
RewriteRule
s 必須使用\x
而不是%
為了匹配 % 編碼的 URL!(字節序列的 PCRE 語法)
mod_rewrite
-config 使用 PCRE 正則表達式語法,並對解碼的 URL 進行操作,因此%
在模式中鍵入 -encodingRewriteRule
會導致它查找文字%
-character,而不是編碼值。RewriteRules 中正確的轉義字元是,因此可以使用(或不區分大小寫)匹配
\x
URLencoded 值。%B2``\xb2``\xB2
請注意,這
RewriteRule
是一種針對字元編碼問題的 hacky 解決方案,它僅在恰好有一個特定的錯誤編碼字元位於特定的、可預測的位置時才有效。有關任意位置多個錯誤編碼字元的通用解決方案,請參閱Apache .htaccess 能否將編碼 URI 中的百分比編碼從 Win-1252 轉換為 UTF-8?,它提出了一種通用解決方案,該解決方案使用
RewriteMap
功能齊全的程式語言耦合到外部程序。正確的解決方案仍然是從源頭上防止這種情況,在整個鏈中使用顯式的 %-encoding。這可以避免依賴於作業系統的編碼意外發生在您無法控制的“中間某處”。(假設路徑上沒有客戶端進行雙重編碼,這應該是一種應受懲罰的罪行..)
我是怎麼到這裡的
絕望了,我按照mod_rewrite docs
LogLevel Warn rewrite:trace3
中的建議提高了伺服器範圍的日誌記錄。這被警告(嚴重)影響伺服器性能,但由於這是一個低流量的伺服器,並且沒有預先存在的重寫,所以可以管理。額外的日誌記錄被發送到 (
ssl_
)error_log
中。這讓我深入了解瞭如何嘗試匹配,以及規則和 URI 的內部表示在mod_rewrite
.摘自
ssl_error_log
(為簡潔起見省略了許多列),帶有規則RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE,L]
[rewrite:trace3] applying pattern '(.*)project%B2/(.*)' to uri 'project\xb2/' [rewrite:trace1] pass through /var/www/html/example.org/project\xb2
請注意,來自客戶端的 request-uri 是寫的
\xb2
,但我的模式使用%B2
.使用規則將規則語法與 uri 語法匹配
RewriteRule (.*)project\xB2/(.*) $1project²/$2 [NE,L]
[rewrite:trace3] applying pattern '(.*)project\\xb2/(.*)' to uri 'project\xb2/' [rewrite:trace2] rewrite 'project\xb2/' -> 'project%c2%b2/' [rewrite:trace1] internal redirect with /auth-test/project\xc2\xb2/ [INTERNAL REDIRECT]
🎉成功!🎉 如我們所見,我們現在正在匹配!
為什麼沒有
[R]
/[R=302]
標誌?由於這是一個字元編碼問題,我不認為進行額外的 HTTP 往返會增加價值;饋入客戶端的每個連結都會再次遇到相同的問題,除非我在將其饋入客戶端 java 程序之前修復了編碼問題。
不要忘記
RewriteBase
請注意,這個縮短的版本省略了正確的設置
RewriteBase
,這可能會破壞重寫的路徑,具體取決於您conf
寫入的位置(例如<Directory>
vs<Location>
)。沒有RewriteBase
我不小心重定向到❌https://example.org/var/www/html/rewrite-testing/project²
而不是✅https://example.org/rewrite-testing/project²
)
您不能僅使用 mod_rewrite “轉換編碼”,但是,您可以在請求的 URL 中搜尋特定的字元序列並“更正它”。
http://localhost:60151/load?file=http://example.org/project²/some/data/file.bam
RewriteRule (.*)project%B2/(.*) $1project²/$2 [NE]
請注意,它
project²
作為查詢字元串的一部分出現在您發布的範例 URL 中,但是,RewriteRule
模式(您在上面使用的)僅與 %-decoded URL 路徑匹配(不包括查詢字元串)。要匹配查詢字元串,您需要使用附加RewriteCond
指令並匹配QUERY_STRING
(或THE_REQUEST
)伺服器變數。請注意,
QUERY_STRING
(andTHE_REQUEST
) 伺服器變數是 % 編碼的(或者更確切地說,是從客戶端發送的) - 它們沒有經過 % 解碼。請嘗試以下操作:
RewriteCond %{QUERY_STRING} (.+)/project%B2/(.*) RewriteRule ^(load)$ $1?%1/project%C2%B2/%2 [NE,L]
反向引用
%1
和替換%2
字元串中的引用前面的CondPattern - 麻煩部分之前和之後的部分。/project%B2/
$1
只是對 URL 路徑的反向引用(以保存重複),我假設它總是load
.該
NE
標誌防止%
自身(當用作 URL 編碼字元的一部分時)被 URL 編碼。**更新:**恐怕我最初的問題不清楚誰獲取哪個 URL,因此您的答案的“查詢字元串”部分不適用……
如果您需要匹配**% 編碼的 URL 路徑**,那麼您應該匹配
THE-REQUEST
伺服器變數。THE_REQUEST
包含 HTTP 請求標頭的第一行並且未進行 % 解碼。它包含從客戶端發送的完整 URL 路徑(和查詢字元串)(以及請求方法和協議版本)。例如,對於格式錯誤的請求,格式如下的字元串:GET /project%B2/some/data/file.bam HTTP/1.1
您可以匹配和更正如下:
RewriteCond %{THE_REQUEST} ^[A-Z]{3,7}\s(/project)%B2([^\s]+) RewriteRule ^/?project %1%B2%C2%2 [NE,L]
%1
並且%2
是對前面CondPattern中擷取的子模式的反向引用。另一方面,
RewriteRule
pattern僅與預處理的 %-decoded URL-path 匹配(如上所述)。那麼,%B2
無論解碼為什麼;假設是 UTF-8 編碼。不幸的是,這是一個不可列印的字元,因此需要用正則表達式中的十六進製字元序列表示,即。\xb2
(這是表示單個字節序列的 PCRE 語法)。