Các tính năng ẩn của mod_rewrite


119

Gần đây, có vẻ như có một số lượng lớn các mod_rewritechủ đề trôi nổi với một chút nhầm lẫn về cách hoạt động của một số khía cạnh nhất định. Do đó, tôi đã biên soạn một số ghi chú về chức năng phổ biến và có lẽ là một số sắc thái khó chịu.

Bạn đã gặp phải những tính năng / sự cố phổ biến nào khác khi sử dụng mod_rewrite?


Câu trả lời:


203

Nơi đặt quy tắc mod_rewrite

mod_rewritecác quy tắc có thể được đặt trong httpd.conftệp hoặc trong .htaccesstệp. nếu bạn có quyền truy cập httpd.conf, việc đặt các quy tắc ở đây sẽ mang lại lợi ích về hiệu suất (vì các quy tắc được xử lý một lần, trái ngược với mỗi lần .htaccesstệp được gọi).

Ghi nhật ký các yêu cầu mod_rewrite

Ghi nhật ký có thể được kích hoạt từ trong httpd.conftệp (bao gồm <Virtual Host>):

# logs can't be enabled from .htaccess
# loglevel > 2 is really spammy!
RewriteLog /path/to/rewrite.log
RewriteLogLevel 2

Các trường hợp sử dụng phổ biến

  1. Để dồn tất cả các yêu cầu đến một điểm duy nhất:

    RewriteEngine on
    # ignore existing files
    RewriteCond %{REQUEST_FILENAME} !-f   
    # ignore existing directories
    RewriteCond %{REQUEST_FILENAME} !-d   
    # map requests to index.php and append as a query string
    RewriteRule ^(.*)$ index.php?query=$1 
    

    Kể từ Apache 2.2.16, bạn cũng có thể sử dụng FallbackResource.

  2. Xử lý chuyển hướng 301/302:

    RewriteEngine on
    # 302 Temporary Redirect (302 is the default, but can be specified for clarity)
    RewriteRule ^oldpage\.html$ /newpage.html [R=302]  
    # 301 Permanent Redirect
    RewriteRule ^oldpage2\.html$ /newpage.html [R=301] 
    

    Lưu ý : chuyển hướng bên ngoài hoàn toàn là chuyển hướng 302:

    # this rule:
    RewriteRule ^somepage\.html$ http://google.com
    # is equivalent to:
    RewriteRule ^somepage\.html$ http://google.com [R]
    # and:
    RewriteRule ^somepage\.html$ http://google.com [R=302]
    
  3. Bắt buộc SSL

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://example.com/$1 [R,L]
    
  4. Cờ chung:

    • [R]hoặc [redirect]- buộc chuyển hướng (mặc định là chuyển hướng tạm thời 302)
    • [R=301]hoặc [redirect=301]- buộc chuyển hướng vĩnh viễn 301
    • [L]hoặc [last]- dừng quá trình viết lại (xem lưu ý bên dưới về các cạm bẫy phổ biến)
    • [NC]hoặc [nocase]- chỉ định rằng đối sánh phải không phân biệt chữ hoa chữ thường


    Sử dụng dạng cờ dài thường dễ đọc hơn và sẽ giúp những người khác đọc mã của bạn sau này.

    Bạn có thể phân tách nhiều cờ bằng dấu phẩy:

    RewriteRule ^olddir(.*)$ /newdir$1 [L,NC]
    

Những cạm bẫy phổ biến

  1. Kết hợp các mod_aliaschuyển hướng kiểu vớimod_rewrite

    # Bad
    Redirect 302 /somepage.html http://example.com/otherpage.html
    RewriteEngine on
    RewriteRule ^(.*)$ index.php?query=$1
    
    # Good (use mod_rewrite for both)
    RewriteEngine on
    # 302 redirect and stop processing
    RewriteRule ^somepage.html$ /otherpage.html [R=302,L] 
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    # handle other redirects
    RewriteRule ^(.*)$ index.php?query=$1                 
    

    Lưu ý : bạn có thể kết hợp mod_aliasvới mod_rewrite, nhưng nó bao gồm nhiều công việc hơn là chỉ xử lý các chuyển hướng cơ bản như trên.

  2. Ngữ cảnh ảnh hưởng đến cú pháp

    Trong .htaccesscác tệp, dấu gạch chéo ở đầu không được sử dụng trong mẫu RewriteRule:

    # given: GET /directory/file.html
    
    # .htaccess
    # result: /newdirectory/file.html
    RewriteRule ^directory(.*)$ /newdirectory$1
    
    # .htaccess
    # result: no match!
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # httpd.conf
    # result: /newdirectory/file.html
    RewriteRule ^/directory(.*)$ /newdirectory$1
    
    # Putting a "?" after the slash will allow it to work in both contexts:
    RewriteRule ^/?directory(.*)$ /newdirectory$1
    
  3. [L] không phải là cuối cùng! (đôi khi)

    Các [L]cờ dừng chế biến bất kỳ quy tắc viết lại tiếp tục cho qua rằng thông qua bộ quy tắc . Tuy nhiên, nếu URL đã được sửa đổi trong lần vượt qua đó và bạn đang ở trong .htaccessngữ cảnh hoặc <Directory>phần, thì yêu cầu đã sửa đổi của bạn sẽ được chuyển trở lại thông qua công cụ phân tích cú pháp URL. Và trong lần vượt qua tiếp theo, lần này nó có thể phù hợp với một quy tắc khác. Nếu bạn không hiểu điều này, thường có vẻ như [L]cờ của bạn không có tác dụng.

    # processing does not stop here
    RewriteRule ^dirA$ /dirB [L] 
    # /dirC will be the final result
    RewriteRule ^dirB$ /dirC     
    

    Nhật ký viết lại của chúng tôi cho thấy rằng các quy tắc được chạy hai lần và URL được cập nhật hai lần:

    rewrite 'dirA' -> '/dirB'
    internal redirect with /dirB [INTERNAL REDIRECT]
    rewrite 'dirB' -> '/dirC'
    

    Cách tốt nhất để giải quyết vấn đề này là sử dụng [END]cờ ( xem tài liệu Apache ) thay vì [L]cờ, nếu bạn thực sự muốn dừng tất cả các quá trình xử lý thêm các quy tắc (và các lần chuyển tiếp sau đó). Tuy nhiên, [END]cờ chỉ khả dụng cho Apache v2.3.9 + , vì vậy nếu bạn có v2.2 hoặc thấp hơn, bạn chỉ có [L]cờ.

    Đối với các phiên bản trước đó, bạn phải dựa vào các RewriteCondcâu lệnh để ngăn việc khớp các quy tắc trong các lần chuyển tiếp theo của công cụ phân tích cú pháp URL.

    # Only process the following RewriteRule if on the first pass
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ...
    

    Hoặc bạn phải đảm bảo rằng Quy tắc viết lại của bạn ở trong một ngữ cảnh (tức là httpd.conf) sẽ không khiến yêu cầu của bạn được phân tích lại.


10
Dude, hoàn toàn là bài báo hay nhất trên internet hiện nay về mod viết lại. Tôi ghét điều đó. Tôi là một kẻ dị giáo lighttpd tôi ghét mod_rewrite bao nhiêu.
Kent Fredric

3
Đây là hướng dẫn hữu ích nhất mà tôi tìm thấy trên mod_rewrite cho đến nay. Chỉ cần tìm hiểu về RewriteLog đã giúp khắc phục rất nhiều vấn đề mà những gì tôi mất nhiều ngày để theo dõi đã biến thành một vài phút. (Tôi có nghĩa là các quy tắc được viết nhưng tôi không thể tìm ra lý do tại sao họ không làm việc)
Joe Chin

1 bài đăng cũ, nhưng một trong những điều hữu ích hơn tôi đã tìm thấy trên SO - cho tôi.
Erik

3
Các [L]lá cờ có nghĩa là một quy tắc là cuối cùng trong chế biến hiện tại, điều này sẽ không dừng lại viết lại, bởi vì họ đang chuyển hướng nội, vì vậy bạn dirBáp dụng cho dirCchế biến htaccess tới. Một mình RewriteRule ^(.*)$ index.php?query=$1sẽ là một vòng lặp chuyển hướng nội bộ vô hạn (trong thực tế, nó sẽ kết thúc sau 10 lần lặp). -1 vì bạn cho rằng [L] không phải là cuối cùng . Nó không kết thúc quá trình viết lại, nhưng nó là cuối cùng .
kbec

3
Tôi tin rằng đây RewriteCond %{HTTPS} offlà cách ưa thích để kiểm tra kết nối HTTPS (trong ví dụ của bạn về việc buộc lưu lượng truy cập không phải ssl vào HTTPS)
Madbreaks

22

nếu bạn cần 'chặn' chuyển hướng / viết lại nội bộ xảy ra trong .htaccess, hãy xem

RewriteCond %{ENV:REDIRECT_STATUS} ^$

điều kiện, như đã thảo luận ở đây .


Cảm ơn, điều đó vừa được khắc phục sự cố của tôi!
Matthew

Cảm ơn vì tôi cũng vậy, người cứu rỗi cuộc sống!
Benjamin

Đây thực sự là một tiết kiệm cuộc sống! Mọi người nên ý thức hơn về điều đó. Trong thực tế, tôi sẽ đề nghị này cho mọi câu hỏi về .*với [L]cờ tôi đọc trước khi tôi đến đây.
Qwerty

Tôi đã thấy một vài sửa đổi này 200, !=200, ^., ^$. Rõ ràng là biến được đặt thành 200chuyển hướng, nhưng các trang khác (lỗi và nội dung) cũng đặt nó thành một số giá trị. Bây giờ điều đó có nghĩa bạn nên kiểm tra xem nó is empty, is not empty, is 200hoặc is not 200, tùy thuộc vào những gì bạn cần.
Qwerty

18

Thỏa thuận với RewriteBase:

Bạn hầu như luôn cần đặt RewriteBase. Nếu bạn không, apache đoán rằng cơ sở của bạn là đường dẫn đĩa vật lý đến thư mục của bạn. Vì vậy, hãy bắt đầu với điều này:

RewriteBase /

Ah. Điều đó hoàn toàn chỉ khắc phục được vấn đề tôi đang gặp phải. Cảm ơn vì điều đó!
Tom Savage

3
Bất kỳ cách nói nào RewriteBase .hoặc điều gì đó để chỉ ra rằng nó nên giữ nguyên URL, chỉ thay đổi những gì bạn đã chỉ định?
Jay K

Cảm ơn bạn, đây là một phần thông tin vô giá. :)
AturSams

2
Bạn chỉ cần đặt RewriteBasenếu bạn đang sử dụng thay thế đường dẫn tương đối trong RewriteRulechỉ thị. Tốt hơn là tránh sử dụng các đường dẫn tương đối.
MrWhite

2
Tôi không đồng ý với câu trả lời này. Trong nhóm RewriteBasenhà phát triển của chúng tôi, chúng tôi tránh hoàn toàn vì gần như tất cả các nhà phát triển đều hiểu sai chức năng của nó. Như @ w3d đã nói, bạn chỉ cần nó nếu bạn muốn lưu các ký tự và muốn áp dụng cùng một cơ sở cho tất cả các RewriteRules của bạn trong một tệp. Mã của bạn có thể sẽ rõ ràng hơn đối với người khác nếu bạn tránh nó.
Simon East

13

Cạm bẫy khác:

1- Đôi khi bạn nên tắt MultiView

Options -MultiViews

Tôi không hiểu rõ về tất cả các khả năng của MultiView, nhưng tôi biết rằng nó làm rối các quy tắc mod_rewrite của tôi khi hoạt động, bởi vì một trong những thuộc tính của nó là thử và 'đoán' một phần mở rộng cho một tệp mà nó cho rằng tôi đang tìm kiếm .

Tôi sẽ giải thích: Giả sử bạn có 2 tệp php trong web dir, file1.php và file2.php và bạn thêm các điều kiện và quy tắc này vào .htaccess của mình:

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ file1.php/$1 

Bạn giả sử rằng tất cả các url không khớp với một tệp hoặc một thư mục sẽ bị file1.php lấy. Sự ngạc nhiên! Quy tắc này không được áp dụng cho url http: // myhost / file2 / somepath . Thay vào đó, bạn được đưa vào bên trong file2.php.

Điều đang xảy ra là MultiViews tự động đoán rằng url mà bạn thực sự muốn là http: //myhost/file2.php/somepath và sẵn lòng đưa bạn đến đó.

Bây giờ, bạn không có manh mối gì vừa xảy ra và bạn đang đặt câu hỏi về mọi thứ mà bạn nghĩ rằng bạn đã biết về mod_rewrite. Sau đó, bạn bắt đầu chơi với các quy tắc để cố gắng hiểu logic đằng sau tình huống mới này, nhưng bạn càng thử nghiệm thì nó càng ít ý nghĩa hơn.

Ok, Tóm lại, nếu bạn muốn mod_rewrite hoạt động theo cách gần đúng với logic, thì việc tắt MultiViews là một bước đi đúng hướng.

2- bật FollowSymlinks

Options +FollowSymLinks 

Cái đó, tôi không thực sự biết chi tiết, nhưng tôi đã thấy nó được đề cập nhiều lần, vì vậy cứ làm đi.


Cảm ơn :) Tôi nhận thấy bất ngờ bất ngờ như / log / hoạt động chuyển thành /log.txt/activity .. Cảm ơn vì mẹo :) .. máy tính quá xấu không bao giờ tạo ra những điều thú vị bất ngờ xảy ra như vô tình dụ dỗ tất cả đồng nghiệp nữ của bạn trên facebook :)
AturSams

1
+FollowSymLinksđược đề cập trong tài liệu là bắt buộc mod_rewriteđể làm việc, vì lý do bảo mật mơ hồ.
Joey

Hai câu nói ở đây khiến tôi vô cùng lo lắng: 'Tôi không hiểu rõ về tất cả các khả năng của MultiView, nhưng tôi biết rằng nó làm rối tung các quy tắc mod_rewrite của tôi khi hoạt động' và câu này "Câu đó, tôi không thực sự biết chi tiết về , nhưng tôi đã thấy nó được đề cập nhiều lần, nên cứ làm đi. ' Tôi ước những người như bạn sẽ không viết câu trả lời trên SO về những điều bạn không chắc chắn.
TheCarver,

1
@PaparazzoKid: Tôi nghĩ bạn đang nhầm SO với một bách khoa toàn thư. Đó là một cộng đồng mọi người đến với nhau để tìm hiểu về công nghệ mà họ đang làm việc. Không giống như AW White và Joey trước bạn, nhận xét của bạn gần như không có giá trị. MV và FSL là 2 trong nhiều lựa chọn của Apache. Câu trả lời của tôi là về những cạm bẫy khi làm việc với mod_rw, một mô-đun riêng biệt, xung đột với một số tùy chọn và hoạt động với những người khác. Tôi đã giải thích cách MV ảnh hưởng đến mod_rw và đề cập rằng + FSL là một đề xuất phổ biến. Joey xác nhận rằng trên thực tế nó là bắt buộc. Bạn mang gì đến bàn?
Michael Ekoka

Cảm ơn. Tôi vừa dành phần tốt nhất của một giờ để trang web cũ hoạt động và cố gắng gỡ lỗi các quy tắc viết lại, chỉ để thấy rằng MultiViews đã ghi đè tất cả.
Andrew McCombe

5

Phương trình có thể được thực hiện với ví dụ sau:

RewriteCond %{REQUEST_URI} ^/(server0|server1).*$ [NC]
# %1 is the string that was found above
# %1<>%{HTTP_COOKIE} concatenates first macht with mod_rewrite variable -> "test0<>foo=bar;"
#RewriteCond search for a (.*) in the second part -> \1 is a reference to (.*)
# <> is used as an string separator/indicator, can be replaced by any other character
RewriteCond %1<>%{HTTP_COOKIE} !^(.*)<>.*stickysession=\1.*$ [NC]
RewriteRule ^(.*)$ https://notmatch.domain.com/ [R=301,L]

Cân bằng tải động:

Nếu bạn sử dụng mod_proxy để cân bằng hệ thống của mình, bạn có thể thêm một loạt máy chủ công nhân động.

RewriteCond %{HTTP_COOKIE} ^.*stickysession=route\.server([0-9]{1,2}).*$ [NC]
RewriteRule (.*) https://worker%1.internal.com/$1 [P,L]

4

Hiểu rõ hơn về cờ [L] theo thứ tự. Cờ [L] cuối cùng, bạn chỉ cần hiểu điều gì sẽ khiến yêu cầu của bạn được chuyển qua lại công cụ phân tích cú pháp URL. Từ tài liệu ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (tôi nhấn mạnh):

Cờ [L] khiến mod_rewrite ngừng xử lý bộ quy tắc. Trong hầu hết các ngữ cảnh, điều này có nghĩa là nếu quy tắc khớp, sẽ không có quy tắc nào khác được xử lý. Điều này tương ứng với lệnh cuối cùng trong Perl hoặc lệnh break trong C. Sử dụng cờ này để chỉ ra rằng quy tắc hiện tại nên được áp dụng ngay lập tức mà không cần xem xét các quy tắc khác.

Nếu bạn đang sử dụng RewriteRule trong các tệp .htaccess hoặc trong <Directory>các phần , điều quan trọng là bạn phải hiểu rõ về cách các quy tắc được xử lý. Hình thức đơn giản của điều này là một khi các quy tắc đã được xử lý, yêu cầu viết lại được gửi lại cho công cụ phân tích cú pháp URL để thực hiện những gì có thể với nó. Có thể khi yêu cầu viết lại được xử lý, tệp hoặc <Directory> phần .htaccess có thể gặp lại và do đó bộ quy tắc có thể được chạy lại từ đầu. Điều này thường xảy ra nhất nếu một trong các quy tắc gây ra chuyển hướng - nội bộ hoặc bên ngoài - khiến quá trình yêu cầu bắt đầu lại.

Vì vậy, cờ [L] không ngừng xử lý bất kỳ quy tắc viết lại nào cho lần vượt qua đó qua bộ quy tắc đó. Tuy nhiên, nếu quy tắc của bạn được đánh dấu bằng [L] đã sửa đổi yêu cầu và bạn đang ở trong ngữ cảnh .htaccess hoặc <Directory>phần, thì yêu cầu đã sửa đổi của bạn sẽ được chuyển trở lại thông qua công cụ phân tích cú pháp URL. Và trong lần vượt qua tiếp theo, lần này nó có thể phù hợp với một quy tắc khác. Nếu bạn không hiểu chuyện gì đã xảy ra, có vẻ như quy tắc viết lại đầu tiên của bạn với cờ [L] không có hiệu lực.

Cách tốt nhất để giải quyết vấn đề này là sử dụng cờ [END] ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end ) thay vì cờ [L], nếu bạn thực sự muốn dừng tất cả các quy trình xử lý tiếp theo của các quy tắc (và đánh giá lại sau đó). Tuy nhiên, cờ [END] chỉ khả dụng cho Apache v2.3.9 +, vì vậy nếu bạn có v2.2 trở xuống, bạn chỉ bị mắc kẹt với cờ [L]. Trong trường hợp này, bạn phải dựa vào các câu lệnh RewriteCond để ngăn việc khớp các quy tắc trong các lần chuyển tiếp theo của công cụ phân tích cú pháp URL. Hoặc bạn phải đảm bảo rằng RewriteRule của bạn nằm trong ngữ cảnh (tức là httpd.conf) sẽ không khiến yêu cầu của bạn được phân tích lại.


3

Một tính năng tuyệt vời khác là viết lại-bản đồ-mở rộng. Chúng đặc biệt hữu ích nếu bạn có một lượng lớn các máy chủ / bản ghi lại cần xử lý:

Chúng giống như một khóa-giá trị-thay thế:

RewriteMap examplemap txt:/path/to/file/map.txt

Sau đó, bạn có thể sử dụng ánh xạ trong các quy tắc của mình như:

RewriteRule ^/ex/(.*) ${examplemap:$1}

Thông tin thêm về chủ đề này có thể được tìm thấy ở đây:

http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html#mapfunc


Bỏ qua tính năng này nếu bạn đang sử dụng các đoạn .htaccessviết lại dựa trên cơ sở. Nó không hoạt động trong bối cảnh này.
TerryE

2
Chỉ thị RewriteMap phải được sử dụng trong ngữ cảnh máy chủ (httpd.conf), nhưng sau khi được xác định ở đó, bạn có thể sử dụng bản đồ thông qua RewriteRule trong tệp .htaccess.
JaredC

2

mod_rewrite có thể sửa đổi các khía cạnh của việc xử lý yêu cầu mà không làm thay đổi URL, ví dụ như đặt biến môi trường, đặt cookie, v.v. Điều này cực kỳ hữu ích.

Đặt một biến môi trường có điều kiện:

RewriteCond %{HTTP_COOKIE} myCookie=(a|b) [NC]
RewriteRule .* - [E=MY_ENV_VAR:%b]

Quay trở lại một phản ứng 503: RewriteRule's [R]cờ có thể mất một giá trị không 3xx và trả về một phản ứng không chuyển hướng, ví dụ để quản lý thời gian chết / bảo trì:

RewriteRule .* - [R=503,L]

sẽ trả về phản hồi 503 (không phải chuyển hướng mỗi lần).

Ngoài ra, mod_rewrite có thể hoạt động giống như một giao diện siêu hỗ trợ cho mod_proxy, vì vậy bạn có thể làm điều này thay vì viết ProxyPasschỉ thị:

RewriteRule ^/(.*)$ balancer://cluster%{REQUEST_URI} [P,QSA,L]

Ý kiến: Sử dụng RewriteRules vàRewriteCond s để định tuyến các yêu cầu đến các ứng dụng khác nhau hoặc bộ cân bằng tải dựa trên hầu như mọi khía cạnh có thể hình dung được của yêu cầu đều vô cùng mạnh mẽ. Việc kiểm soát các yêu cầu trên đường đến phần phụ trợ và có thể sửa đổi các phản hồi trên đường trở lại của chúng, khiến mod_rewrite trở thành nơi lý tưởng để tập trung tất cả các cấu hình liên quan đến định tuyến.

Hãy dành thời gian để tìm hiểu nó, nó rất xứng đáng! :)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.