Tham khảo: mod_rewrite, viết lại URL và các liên kết đẹp


142

"Liên kết đẹp" là một chủ đề thường được yêu cầu, nhưng nó hiếm khi được giải thích đầy đủ. mod_rewrite là một cách để tạo ra "các liên kết đẹp", nhưng nó phức tạp và cú pháp của nó rất ngắn gọn, khó hiểu và tài liệu giả định mức độ thành thạo nhất định về HTTP. Ai đó có thể giải thích một cách đơn giản về cách "liên kết đẹp" hoạt động và cách mod_rewrite có thể được sử dụng để tạo ra chúng không?

Các tên phổ biến khác, bí danh, thuật ngữ cho các URL sạch: URL RESTful , URL thân thiện với người dùng, URL thân thiện với SEO , sên và URL MVC (có thể là một cách viết sai)


2
Slug hoặc Slugging là một bí danh / thuật ngữ phổ biến khác cho các url đẹp.
Mike B

2
@Mike Sắp xếp, nhưng sên thường là một phần của các URL đẹp. Một con sên khá đặc biệt khi, ví dụ, tiêu đề của một bài viết được chuyển thành một hình thức thân thiện với URL, sau đó đóng vai trò là định danh của bài viết đó. Vì vậy, reference-mod-rewrite-url-rewriting-explainedlà sên, /questions/20563772/reference-mod-rewrite-url-rewriting-explainedlà URL khá.
lừa dối

2
Tôi nghĩ rằng các thẻ .htaccessmod-rewritenên được cập nhật để bao gồm một liên kết đến câu hỏi này, vì nó bao gồm phần lớn những gì được hỏi một cách thường xuyên. Suy nghĩ?
Mike Rockétt

Câu trả lời:


110

Để hiểu mod_rewrite trước tiên bạn cần hiểu cách thức hoạt động của một máy chủ web. Một máy chủ web đáp ứng các yêu cầu HTTP . Một yêu cầu HTTP ở mức cơ bản nhất của nó trông như thế này:

GET /foo/bar.html HTTP/1.1

Đây là yêu cầu đơn giản của trình duyệt đến máy chủ web yêu cầu URL /foo/bar.html từ nó. Điều quan trọng là phải nhấn mạnh rằng nó không yêu cầu một tệp , nó chỉ yêu cầu một số URL tùy ý. Yêu cầu cũng có thể như thế này:

GET /foo/bar?baz=42 HTTP/1.1

Đây chỉ là một yêu cầu hợp lệ cho một URL và rõ ràng nó không liên quan gì đến các tệp.

Máy chủ web là một ứng dụng lắng nghe trên một cổng, chấp nhận các yêu cầu HTTP đến trên cổng đó và trả về phản hồi. Một máy chủ web hoàn toàn miễn phí để đáp ứng bất kỳ yêu cầu nào theo bất kỳ cách nào nó thấy phù hợp / theo bất kỳ cách nào bạn đã cấu hình nó để đáp ứng. Phản hồi này không phải là một tệp, đó là phản hồi HTTP có thể có hoặc không liên quan gì đến các tệp vật lý trên bất kỳ đĩa nào. Một máy chủ web không phải là Apache, có nhiều máy chủ web khác, tất cả chỉ là các chương trình chạy liên tục và được gắn vào một cổng đáp ứng các yêu cầu HTTP. Bạn có thể tự viết một cái. Đoạn này có ý định ly dị bạn với bất kỳ khái niệm nào rằng các URL trực tiếp bằng các tệp, điều này thực sự quan trọng để hiểu. :)

Cấu hình mặc định của hầu hết các máy chủ web là tìm kiếm một tệp khớp với URL trên đĩa cứng. Nếu gốc tài liệu của máy chủ được đặt thành, giả sử /var/www, nó có thể xem liệu tệp /var/www/foo/bar.htmlcó tồn tại hay không và phục vụ nó nếu có. Nếu tệp kết thúc bằng ".php", nó sẽ gọi trình thông dịch PHP và sau đó trả về kết quả. Tất cả các hiệp hội này là hoàn toàn cấu hình; một tệp không phải kết thúc bằng ".php" để máy chủ web chạy nó thông qua trình thông dịch PHP và URL không phải khớp với bất kỳ tệp cụ thể nào trên đĩa để xảy ra sự cố.

mod_rewrite là một cách để viết lại xử lý yêu cầu nội bộ. Khi máy chủ web nhận được yêu cầu về URL /foo/bar, bạn có thể viết lại URL đó thành một thứ khác trước khi máy chủ web sẽ tìm một tệp trên đĩa để khớp với nó. Ví dụ đơn giản:

RewriteEngine On
RewriteRule   /foo/bar /foo/baz

Quy tắc này cho biết bất cứ khi nào một yêu cầu khớp với "/ foo / bar", hãy viết lại thành "/ foo / baz". Yêu cầu sau đó sẽ được xử lý như thể /foo/bazđã được yêu cầu thay thế. Điều này có thể được sử dụng cho các hiệu ứng khác nhau, ví dụ:

RewriteRule (.*) $1.html

Quy tắc này khớp với bất cứ thứ gì ( .*) và bắt nó ( (..)), sau đó viết lại để thêm ".html". Nói cách khác, nếu /foo/barlà URL được yêu cầu, nó sẽ được xử lý như thể /foo/bar.htmlđã được yêu cầu. Xem http://THER-expressions.info để biết thêm thông tin về kết hợp biểu thức chính quy, chụp và thay thế.

Một quy tắc khác thường gặp là:

RewriteRule (.*) index.php?url=$1

Điều này, một lần nữa, phù hợp với bất cứ điều gì và viết lại nó vào tệp index.php với URL được yêu cầu ban đầu được nối vào urltham số truy vấn. Tức là, đối với bất kỳ và tất cả các yêu cầu đến, tệp index.php được thực thi và tệp này sẽ có quyền truy cập vào yêu cầu ban đầu $_GET['url'], vì vậy nó có thể làm bất cứ điều gì nó muốn với nó.

Chủ yếu bạn đặt các quy tắc viết lại này vào tập tin cấu hình máy chủ web của bạn . Apache cũng cho phép * bạn đặt chúng vào một tệp được gọi .htaccesstrong tài liệu gốc của bạn (tức là bên cạnh các tệp .php của bạn).

* Nếu được cho phép bởi tệp cấu hình chính của Apache; đó là tùy chọn, nhưng thường được kích hoạt.

Những gì mod_rewrite không làm

mod_rewrite không kỳ diệu làm cho tất cả các URL của bạn "đẹp". Đây là một sự hiểu lầm phổ biến. Nếu bạn có liên kết này trong trang web của bạn:

<a href="https://stackoverflow.com/my/ugly/link.php?is=not&amp;very=pretty">

không có gì mod_rewrite có thể làm để làm cho nó đẹp. Để làm cho điều này một liên kết đẹp, bạn phải:

  1. Thay đổi liên kết thành một liên kết đẹp:

    <a href="https://stackoverflow.com/my/pretty/link">
    
  2. Sử dụng mod_rewrite trên máy chủ để xử lý yêu cầu tới URL /my/pretty/linkbằng bất kỳ một trong các phương pháp được mô tả ở trên.

(Người ta có thể sử dụng mod_substitutekết hợp để chuyển đổi các trang HTML đi và các liên kết có trong đó. Mặc dù điều này là nỗ lực hơn bình thường so với việc chỉ cập nhật tài nguyên HTML của bạn.)

Có rất nhiều mod_rewrite có thể thực hiện và các quy tắc khớp rất phức tạp mà bạn có thể tạo, bao gồm xâu chuỗi nhiều lần viết lại, yêu cầu ủy quyền cho một dịch vụ hoặc máy hoàn toàn khác, trả lại mã trạng thái HTTP cụ thể dưới dạng phản hồi, chuyển hướng yêu cầu, v.v. Nó rất mạnh mẽ và có thể được sử dụng để rất tốt nếu bạn hiểu cơ chế phản hồi yêu cầu HTTP cơ bản. Nó không tự động làm cho các liên kết của bạn đẹp.

Xem tài liệu chính thức cho tất cả các cờ và tùy chọn có thể.


6
Có thể đề cập đến chỉ thị FallbackResource được giới thiệu trong phiên bản 2.2.16 như là cách viết lại được giới thiệu trước cho một người điều phối.
Darsstar

78

Để mở rộng câu trả lời của lừa đảo , tôi muốn cung cấp một vài ví dụ và giải thích về một số chức năng mod_rewrite khác.

Tất cả các ví dụ dưới đây cho rằng bạn đã bao gồm RewriteEngine Ontrong .htaccesstệp của mình .

Viết lại ví dụ

Hãy lấy ví dụ này:

RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]

Quy tắc được chia thành 4 phần:

  1. RewriteRule - bắt đầu quy tắc viết lại
  2. ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ - Đây được gọi là mẫu, tuy nhiên tôi sẽ chỉ coi nó là mặt trái của quy tắc - những gì bạn muốn viết lại từ
  3. blog/index.php?id=$1&title=$2 - được gọi là sự thay thế hoặc bên phải của quy tắc viết lại - những gì bạn muốn viết lại thành
  4. [NC,L,QSA] là các cờ cho quy tắc viết lại, được phân tách bằng dấu phẩy, tôi sẽ giải thích thêm về sau

Viết lại ở trên sẽ cho phép bạn liên kết đến một cái gì đó như /blog/1/foo/và nó thực sự sẽ tải /blog/index.php?id=1&title=foo.

Bên trái của quy tắc

  • ^cho biết bắt đầu của tên trang - vì vậy nó sẽ viết lại example.com/blog/...nhưng khôngexample.com/foo/blog/...
  • Mỗi bộ (…)dấu ngoặc đơn biểu thị một biểu thức chính quy mà chúng ta có thể nắm bắt như một biến ở phía bên phải của quy tắc. Trong ví dụ này:
    • Bộ dấu ngoặc đầu tiên - ([0-9]+)- khớp với một chuỗi có độ dài tối thiểu 1 ký tự và chỉ có các giá trị số (nghĩa là 0-9). Điều này có thể được tham chiếu với $1ở phía bên phải của quy tắc
    • Tập thứ hai của dấu ngoặc đơn phù hợp với một chuỗi với tối thiểu là 1 nhân vật trong chiều dài, chứa ký tự chữ và số chỉ (AZ, az, hoặc 0-9) hoặc -hoặc +(lưu ý +là dấu gạch chéo ngược như không thoát nó này sẽ thực hiện như một regex nhân vật lặp lại ). Điều này có thể được tham chiếu với $2ở phía bên phải của quy tắc
  • ?có nghĩa là ký tự trước là tùy chọn, vì vậy trong trường hợp này cả hai /blog/1/foo//blog/1/foosẽ viết lại vào cùng một vị trí
  • $ chỉ ra đây là phần cuối của chuỗi chúng ta muốn khớp

Cờ

Đây là các tùy chọn được thêm vào trong ngoặc vuông ở cuối quy tắc viết lại của bạn để chỉ định các điều kiện nhất định. Một lần nữa, có rất nhiều cờ khác nhau mà bạn có thể đọc trong tài liệu , nhưng tôi sẽ xem qua một số cờ phổ biến hơn:

NC

Cờ không có nghĩa là quy tắc viết lại không phân biệt chữ hoa chữ thường, vì vậy đối với quy tắc ví dụ ở trên, điều này có nghĩa là cả hai /blog/1/foo//BLOG/1/foo/(hoặc bất kỳ biến thể nào của điều này) sẽ được khớp.

L

Cờ cuối cùng chỉ ra rằng đây là quy tắc cuối cùng cần được xử lý. Điều này có nghĩa là nếu và chỉ khi quy tắc này phù hợp, sẽ không có quy tắc nào nữa được đánh giá trong quá trình xử lý viết lại hiện tại. Nếu quy tắc không khớp, tất cả các quy tắc khác sẽ được thử theo thứ tự như bình thường. Nếu bạn không đặt Lcờ, tất cả các quy tắc sau sẽ được áp dụng cho URL được viết lại sau đó.

END

Kể từ Apache 2.4, bạn cũng có thể sử dụng [END]cờ. Một quy tắc phù hợp với nó sẽ chấm dứt hoàn toàn xử lý bí danh / viết lại. (Trong khi đó, [L]cờ thường có thể kích hoạt vòng thứ hai, ví dụ như khi viết lại vào hoặc ra khỏi thư mục con.)

QSA

Cờ nối thêm chuỗi truy vấn cho phép chúng ta chuyển các biến bổ sung vào URL đã chỉ định sẽ được thêm vào tham số get gốc. Ví dụ của chúng tôi, điều này có nghĩa là một cái gì đó như /blog/1/foo/?comments=15sẽ tải/blog/index.php?id=1&title=foo&comments=15

R

Lá cờ này không phải là cái tôi đã sử dụng trong ví dụ trên, nhưng là cái tôi nghĩ là đáng nói. Điều này cho phép bạn chỉ định chuyển hướng http, với tùy chọn bao gồm mã trạng thái (ví dụ R=301). Ví dụ: nếu bạn muốn thực hiện chuyển hướng 301 trên / myblog / đến / blog / bạn chỉ cần viết một quy tắc như thế này:

RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]

Điều kiện viết lại

Điều kiện viết lại làm cho việc viết lại thậm chí còn mạnh mẽ hơn, cho phép bạn chỉ định viết lại cho các tình huống cụ thể hơn. Có rất nhiều điều kiện mà bạn có thể đọc trong tài liệu , nhưng tôi sẽ đề cập đến một vài ví dụ phổ biến và giải thích chúng:

# if the host doesn't start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

Đây là một thực tiễn rất phổ biến, sẽ bổ sung tên miền của bạn www.(nếu chưa có) và thực hiện chuyển hướng 301. Ví dụ, tải lên http://example.com/blog/nó sẽ chuyển hướng bạn đếnhttp://www.example.com/blog/

# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]

Điều này hơi ít phổ biến, nhưng là một ví dụ tốt về quy tắc không thực thi nếu tên tệp là thư mục hoặc tệp tồn tại trên máy chủ.

  • %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC] sẽ chỉ thực hiện ghi lại cho các tệp có phần mở rộng tệp là jpg, jpeg, gif hoặc png (không phân biệt chữ hoa chữ thường).
  • %{REQUEST_FILENAME} !-f sẽ kiểm tra xem tệp có tồn tại trên máy chủ hiện tại không và chỉ thực hiện ghi lại nếu không
  • %{REQUEST_FILENAME} !-d sẽ kiểm tra xem tệp có tồn tại trên máy chủ hiện tại không và chỉ thực hiện ghi lại nếu không
  • Việc viết lại sẽ cố tải cùng một tệp trên một tên miền khác

39

Người giới thiệu

Stack Overflow có nhiều tài nguyên tuyệt vời khác để bắt đầu:

Và tổng quan regex thân thiện với người mới thậm chí:

Giữ chỗ thường được sử dụng

  • .*phù hợp với bất cứ điều gì, ngay cả một chuỗi trống. Bạn không muốn sử dụng mẫu này ở mọi nơi, nhưng thường trong quy tắc dự phòng cuối cùng.
  • [^/]+thường được sử dụng cho các đoạn đường dẫn. Nó phù hợp với bất cứ điều gì ngoại trừ dấu gạch chéo về phía trước.
  • \d+ chỉ khớp với chuỗi số.
  • \w+phù hợp với các ký tự chữ và số. Về cơ bản nó là tốc ký [A-Za-z0-9_].
  • [\w\-]+cho các đoạn đường dẫn kiểu "sên", sử dụng chữ cái, số, dấu gạch ngang - _
  • [\w\-.,]+thêm dấu chấm và dấu phẩy. Thích một \-dấu gạch ngang thoát trong các […]lớp.
  • \.biểu thị một thời kỳ nghĩa đen. Mặt khác .bên ngoài […]là giữ chỗ cho bất kỳ biểu tượng.

Mỗi trình giữ chỗ này thường được gói trong (…)ngoặc đơn dưới dạng nhóm chụp. Và toàn bộ mô hình thường ở ^………$đầu + dấu kết thúc. Trích dẫn "mẫu" là tùy chọn.

RewriteRules

Các ví dụ sau đây là trung tâm PHP và gia tăng hơn một chút, dễ thích nghi hơn cho các trường hợp tương tự. Chúng chỉ là tóm tắt, thường liên kết đến nhiều biến thể hoặc Hỏi & Đáp chi tiết.

  • Ánh xạ tĩnh
    /contact,/about

    Việc rút ngắn một vài tên trang thành các lược đồ tệp nội bộ là đơn giản nhất:

     RewriteRule ^contact$  templ/contact.html
     RewriteRule ^about$    about.php
    
  • Số định danh
    /object/123

    Giới thiệu các phím tắt như http://example.com/article/531các tập lệnh PHP hiện có cũng dễ dàng. Trình giữ chỗ số chỉ có thể được ánh xạ lại thành một $_GETtham số:

     RewriteRule ^article/(\d+)$    article-show.php?id=$1
     #                      └───────────────────────────┘
    
  • Giữ chỗ kiểu sên
    /article/with-some-title-slug

    Bạn có thể dễ dàng mở rộng quy tắc đó để cho phép /article/title-stringgiữ chỗ:

     RewriteRule ^article/([\w-]+)$    article-show.php?title=$1
     #                       └────────────────────────────────┘
    

    Lưu ý rằng tập lệnh của bạn phải có khả năng (hoặc được điều chỉnh) để ánh xạ các tiêu đề đó trở lại id cơ sở dữ liệu. RewriteRules một mình không thể tạo hoặc đoán thông tin ngoài không khí.

  • Sên có tiền tố số
    /readable/123-plus-title

    Do đó, bạn sẽ thường thấy các /article/529-title-slugđường dẫn hỗn hợp được sử dụng trong thực tế:

     RewriteRule ^article/(\d+)-([\w-]+)$    article.php?id=$1&title=$2
     #                      └───────────────────────────────┘
    

    Bây giờ bạn chỉ có thể bỏ qua việc chuyển qua title=$2, bởi vì tập lệnh của bạn thường sẽ dựa vào id cơ sở dữ liệu. Việc -title-slugnày đã trở thành trang trí URL tùy ý.

  • Thống nhất với danh sách thay thế
    /foo/… /bar/… /baz/…

    Nếu bạn có các quy tắc tương tự cho nhiều đường dẫn trang ảo, thì bạn có thể khớp và nén chúng với |các danh sách thay thế. Và một lần nữa chỉ cần gán lại chúng cho các tham số GET nội bộ:

     #                               ┌─────────────────────────┐
     RewriteRule ^(blog|post|user)/(\w+)$  disp.php?type=$1&id=$2
     #               └───────────────────────────────────┘
    

    Bạn có thể chia chúng ra thành từng phần RewriteRulenếu việc này trở nên quá phức tạp.

  • Gửi các URL liên quan đến các phụ trợ khác nhau
    /date/SWITCH/backend

    Việc sử dụng thực tế hơn các danh sách thay thế là ánh xạ đường dẫn yêu cầu tới các tập lệnh riêng biệt. Ví dụ: để cung cấp URL thống nhất cho ứng dụng web cũ hơn và mới hơn dựa trên ngày:

     #                   ┌─────────────────────────────┐
     #                   │                 ┌───────────┼───────────────┐
     RewriteRule ^blog/(2009|2010|2011)/([\d-]+)/?$ old/blog.php?date=$2
     RewriteRule ^blog/(\d+)/([\d-]+)/?$  modern/blog/index.php?start=$2
     #                          └──────────────────────────────────────┘
    

    Điều này chỉ đơn giản là ánh xạ lại các bài đăng 2009-2011 vào một tập lệnh và tất cả các năm khác hoàn toàn cho một trình xử lý khác. Lưu ý các quy tắc cụ thể hơn đến đầu tiên . Mỗi tập lệnh có thể sử dụng các thông số GET khác nhau.

  • Các dấu phân cách khác không chỉ là /dấu gạch chéo
    /user-123-name

    Bạn thường thấy RewriteRules nhất để mô phỏng cấu trúc thư mục ảo. Nhưng bạn không bị buộc phải không sáng tạo. Bạn cũng có thể sử dụng -dấu gạch nối để phân đoạn hoặc cấu trúc.

     RewriteRule ^user-(\d+)$    show.php?what=user&id=$1
     #                   └──────────────────────────────┘
     # This could use `(\w+)` alternatively for user names instead of ids.
    

    Đối với /wiki:section:Page_Namechương trình cũng phổ biến :

     RewriteRule ^wiki:(\w+):(\w+)$  wiki.php?sect=$1&page=$2 
     #                   └─────┼────────────────────┘       │
     #                         └────────────────────────────┘
    

    Đôi khi, nó phù hợp để xen kẽ giữa các phần /tử và :hoặc .trong cùng một quy tắc. Hoặc có hai RewriteRules một lần nữa để ánh xạ các biến thể vào các tập lệnh khác nhau.

  • /Dấu gạch chéo tùy chọn
    /dir=/dir/

    Khi chọn các đường dẫn kiểu thư mục, bạn có thể làm cho nó có thể truy cập được mà không cần đến cuối cùng /

     RewriteRule ^blog/([\w-]+)/?$  blog/show.php?id=$1
     #                         ┗┛
    

    Bây giờ điều này xử lý cả http://example.com/blog/123/blog/123/. Và /?$cách tiếp cận rất dễ dàng để thêm vào bất kỳ RewriteRule nào khác.

  • Phân đoạn linh hoạt cho đường dẫn ảo
    .*/.*/.*/.*

    Hầu hết các quy tắc bạn sẽ gặp ánh xạ một tập hợp các /…/phân đoạn đường dẫn tài nguyên bị ràng buộc thành các tham số GET riêng lẻ. Tuy nhiên, một số tập lệnh xử lý một số lượng tùy chọn . Công cụ regrec Apache không cho phép tùy chọn số lượng tùy ý của chúng. Nhưng bạn có thể dễ dàng mở rộng nó thành một khối quy tắc:

     Rewriterule ^(\w+)/?$                in.php?a=$1
     Rewriterule ^(\w+)/(\w+)/?$          in.php?a=$1&b=$2
     Rewriterule ^(\w+)/(\w+)/(\w+)/?$    in.php?a=$1&b=$2&c=$3
     #              └─────┴─────┴───────────────────┴────┴────┘
    

    Nếu bạn cần tối đa năm đoạn đường dẫn, thì hãy sao chép lược đồ này cùng với năm quy tắc. Tất nhiên bạn có thể sử dụng một cách cụ thể hơn[^/]+ trình giữ chỗ mỗi. Ở đây, thứ tự không quan trọng, vì không trùng lặp. Vì vậy, có các đường dẫn được sử dụng thường xuyên nhất đầu tiên là được.

    Ngoài ra, bạn có thể sử dụng các tham số mảng PHP thông qua ?p[]=$1&p[]=$2&p[]=3 chuỗi truy vấn tại đây - nếu tập lệnh của bạn chỉ thích chúng được phân tách trước. (Mặc dù thông thường hơn là chỉ sử dụng quy tắc bắt tất cả và để tập lệnh tự mở rộng các phân đoạn ra khỏi REQUEST_URI.)

    Xem thêm: Làm cách nào để chuyển đổi các phân đoạn đường dẫn URL của tôi thành các cặp khóa-giá trị chuỗi truy vấn?

  • Phân khúc tùy chọn
    prefix/opt?/.*

    Một biến thể phổ biến là có các tiền tố tùy chọn trong một quy tắc. Điều này thường có ý nghĩa nếu bạn có chuỗi tĩnh hoặc giữ chỗ bị ràng buộc hơn xung quanh:

      RewriteRule ^(\w+)(?:/([^/]+))?/(\w+)$  ?main=$1&opt=$2&suffix=$3
    

    Bây giờ mô hình phức tạp hơn (?:/([^/])+)?ở đó chỉ đơn giản là bao bọc một nhóm không chụp (?:…) và làm cho nó tùy chọn )?. Trình giữ chỗ ([^/]+)được chứa sẽ là mẫu thay thế $2, nhưng sẽ trống nếu không có /…/đường giữa .

  • Nắm bắt phần còn lại
    /prefix/123-capture/…/*/…whatever…

    Như đã nói trước đây, bạn thường không muốn các mẫu viết lại quá chung chung. Tuy nhiên, .*đôi khi có ý nghĩa để kết hợp so sánh tĩnh và cụ thể với đôi khi.

     RewriteRule ^(specific)/prefix/(\d+)(/.*)?$  speci.php?id=$2&otherparams=$2
    

    Điều này tùy chọn bất kỳ /…/…/…phân đoạn đường dẫn. Điều đó sau đó tất nhiên đòi hỏi kịch bản xử lý để phân tách chúng ra và chính phần trích xuất các tham số (đó là những gì mà các khung công tác Web- "MVC" làm).

  • Trailing "phần mở rộng"
    /old/path.HTML

    URL không thực sự có phần mở rộng tập tin. Đó là những gì toàn bộ tài liệu tham khảo này nói về (= URL là các trình định vị ảo, không nhất thiết phải là hình ảnh hệ thống tập tin trực tiếp). Tuy nhiên, nếu bạn đã có ánh xạ tệp 1: 1 trước đó, bạn có thể tạo các quy tắc đơn giản hơn:

     RewriteRule  ^styles/([\w\.\-]+)\.css$  sass-cache.php?old_fn_base=$1
     RewriteRule  ^images/([\w\.\-]+)\.gif$  png-converter.php?load_from=$2
    

    Các cách sử dụng phổ biến khác là ánh xạ lại .htmlcác đường dẫn lỗi thời đến các .phptrình xử lý mới hơn hoặc chỉ các tên thư mục bí danh chỉ cho các tệp riêng lẻ (thực tế / thực).

  • Ping-Pong (chuyển hướng và viết lại cùng một lúc)
    /ugly.html← →/pretty

    Vì vậy, tại một số điểm, bạn đang viết lại các trang HTML của mình để chỉ mang các liên kết đẹp, như được phác thảo bằng cách đánh lừa . Trong khi đó, bạn vẫn sẽ nhận được yêu cầu cho các đường dẫn , đôi khi thậm chí từ dấu trang. Khi giải quyết , bạn có thể trình duyệt bóng bàn để hiển thị / thiết lập các URL mới.

    Thủ thuật phổ biến này liên quan đến việc gửi chuyển hướng 30x / Vị trí bất cứ khi nào URL đến tuân theo sơ đồ đặt tên lỗi thời / xấu xí. Các trình duyệt sau đó sẽ kiểm tra lại URL mới / đẹp, sau đó được viết lại (chỉ bên trong) thành vị trí ban đầu hoặc mới.

     # redirect browser for old/ugly incoming paths
     RewriteRule ^old/teams\.html$ /teams [R=301,QSA,END]
    
     # internally remap already-pretty incoming request
     RewriteRule ^teams$ teams.php        [QSA,END]
    

    Lưu ý cách ví dụ này chỉ sử dụng [END]thay vì [L]để thay thế một cách an toàn. Đối với các phiên bản Apache 2.2 cũ hơn, bạn có thể sử dụng các cách giải quyết khác, ngoài ra còn ánh xạ lại các tham số chuỗi truy vấn chẳng hạn: Chuyển hướng xấu đến URL đẹp, ánh xạ lại vào đường dẫn xấu, không có vòng lặp vô hạn

  • Không gian trong các mẫu
    /this+that+

    Nó không đẹp trong các thanh địa chỉ trình duyệt, nhưng bạn có thể sử dụng khoảng trắng trong URL. Đối với các mẫu viết lại, sử dụng dấu cách dấu gạch chéo ngược \␣. Khác chỉ là - "trích dẫn toàn bộ mô hình hoặc thay thế:

     RewriteRule  "^this [\w ]+/(.*)$"  "index.php?id=$1"  [L]
    

    Khách hàng tuần tự hóa URL có +hoặc %20cho khoảng trắng. Tuy nhiên, trong RewriteRules, chúng được giải thích bằng các ký tự bằng chữ cho tất cả các phân đoạn đường dẫn tương đối.

Các bản sao thường xuyên:

Cạm .htaccessbẫy phổ biến

Bây giờ lấy cái này với một hạt muối. Không phải mọi lời khuyên có thể được khái quát cho tất cả các bối cảnh. Đây chỉ là một bản tóm tắt đơn giản của những người nổi tiếng và một vài vấp ngã khó hiểu:

  • Kích hoạt mod_rewrite.htaccess

    Để thực sự sử dụng RewriteRules trong các tệp cấu hình trên mỗi thư mục, bạn phải:

    • Kiểm tra xem máy chủ của bạn đã AllowOverride Allđược bật chưa . Nếu không, các .htaccesschỉ thị trên mỗi thư mục của bạn sẽ bị bỏ qua và RewriteRules sẽ không hoạt động.

    • Rõ ràng đã mod_rewritekích hoạt trong httpd.confphần mô-đun của bạn .

    • Chuẩn bị từng danh sách các quy tắc với RewriteEngine Ontĩnh. Trong khi mod_rewrite hoạt động ngầm trong <VirtualHost><Directory>các phần, các .htaccesstệp trên mỗi thư mục cần được triệu tập riêng lẻ.

  • Dấu gạch chéo hàng đầu ^/sẽ không khớp

    Bạn không nên bắt đầu .htaccesscác mẫu RewriteRule của mình với ^/bình thường:

     RewriteRule ^/article/\d+$  …
                  ↑
    

    Điều này thường thấy trong các hướng dẫn cũ. Và nó được sử dụng để sửa cho các phiên bản Apache 1.x cổ đại. Ngày nay, các đường dẫn yêu cầu hoàn toàn thuận tiện liên quan đến thư mục trong .htaccessRewriteRules. Chỉ cần rời khỏi hàng đầu /.

    · Lưu ý rằng dấu gạch chéo hàng đầu vẫn đúng trong <VirtualHost>các phần. Đó là lý do tại sao bạn thường thấy nó được ^/?tùy chọn cho tương đương quy tắc.
    · Hoặc khi sử dụng, RewriteCond %{REQUEST_URI}bạn vẫn phù hợp để dẫn đầu /.
    · Xem thêm Webmaster.SE: Khi nào thì dấu gạch chéo (/) cần thiết trong các mẫu mod_rewrite?

  • <IfModule *> giấy gói

    Bạn có thể đã thấy điều này trong nhiều ví dụ:

    <IfModule mod_rewrite.c>
       Rewrite… 
    </IfModule>
    
    • thực sự có ý nghĩa trong <VirtualHost>các phần - nếu nó được kết hợp với một tùy chọn dự phòng khác, chẳng hạn như ScriptAliasMatch. (Nhưng không ai từng làm điều đó).
    • Và nó thường được phân phối cho các quy tắc mặc định .htaccessvới nhiều dự án nguồn mở. Ở đó, nó chỉ có nghĩa là dự phòng và giữ cho các URL "xấu" hoạt động như mặc định.

    Tuy nhiên, bạn không muốn điều đó thường trong .htaccesscác tập tin của riêng bạn .

    • Thứ nhất, mod_rewrite không thảnh thơi ngẫu nhiên. (Nếu có, bạn sẽ gặp vấn đề lớn hơn).
    • Nếu nó thực sự bị vô hiệu hóa, RewriteRules của bạn vẫn không hoạt động.
    • Nó có nghĩa là để ngăn chặn 500lỗi HTTP . 404Thay vào đó, những gì nó thường thực hiện là thay đổi người dùng của bạn bằng các lỗi HTTP . (Không quá nhiều người dùng hơn thân thiện nếu bạn nghĩ về nó.)
    • Thực tế nó chỉ ngăn chặn các mục nhật ký hữu ích hơn, hoặc thư thông báo máy chủ. Bạn sẽ không khôn ngoan hơn về lý do tại sao RewriteRules của bạn không bao giờ hoạt động.

    Những gì có vẻ hấp dẫn như bảo vệ tổng quát, thường trở thành một trở ngại trong thực tế.

  • Không sử dụng RewriteBasetrừ khi cần thiết

    Nhiều ví dụ sao chép + dán chứa một lệnh RewriteBase /. Mà dù sao cũng là mặc định. Vì vậy, bạn không thực sự cần điều này. Đó là một giải pháp cho các chương trình viết lại Virtualhost ưa thích và các đường dẫn DOCUMENT_ROOT bị nhầm lẫn cho một số máy chủ lưu trữ được chia sẻ.

    Nó có ý nghĩa để sử dụng với các ứng dụng web riêng lẻ trong các thư mục con sâu hơn. Nó có thể rút ngắn các mẫu RewriteRule trong những trường hợp như vậy. Nói chung, tốt nhất là thích các chỉ định đường dẫn tương đối trong các bộ quy tắc cho mỗi thư mục.

    Xem thêm RewriteBase hoạt động như thế nào trong .htaccess

  • Vô hiệu hóa MultiViewskhi đường dẫn ảo chồng lấp

    Viết lại URL chủ yếu được sử dụng để hỗ trợ các đường dẫn đến ảo . Thường bạn chỉ có một kịch bản điều phối ( index.php) hoặc một vài xử lý cá nhân ( articles.php, blog.php, wiki.php, ...). Cái sau có thể xung đột với các đường dẫn RewriteRule ảo tương tự.

    /article/123Ví dụ, một yêu cầu có thể ánh xạ tới article.phpvới một /123PATH_INFO. Sau đó, bạn phải bảo vệ các quy tắc của mình bằng cách phổ biến RewriteCond !-f+ !-dvà / hoặc vô hiệu hóa hỗ trợ PATH_INFO hoặc có thể chỉ cần vô hiệu hóa Options -MultiViews.

    Đó là không phải để nói rằng bạn luôn luôn phải . Đàm phán nội dung chỉ là một chủ nghĩa tự động đối với tài nguyên ảo.

  • Đặt hàng là quan trọng

    Xem mọi thứ bạn từng muốn biết về mod_rewrite nếu bạn chưa có. Kết hợp nhiều RewriteRules thường dẫn đến tương tác. Đây không phải là một cái gì đó để ngăn chặn thói quen trên mỗi [L]cờ, nhưng một kế hoạch bạn sẽ thực hiện một khi thành thạo. Bạn có thể viết lại các đường dẫn ảo từ quy tắc này sang quy tắc khác, cho đến khi nó đạt đến trình xử lý đích thực tế.

    Tuy nhiên, bạn thường muốn có các quy tắc cụ thể nhất ( /forum/…các mẫu chuỗi cố định hoặc giữ chỗ hạn chế hơn [^/.]+) trong các quy tắc ban đầu . Nói chung, tất cả các quy tắc ( .*) tốt hơn để lại cho các quy tắc sau . (Một ngoại lệ là một RewriteCond -f/-dngười bảo vệ là khối chính.)

  • Biểu định kiểu và hình ảnh ngừng hoạt động

    Khi bạn giới thiệu cấu trúc thư mục ảo, /blog/article/123điều này tác động đến các tham chiếu tài nguyên tương đối trong HTML (chẳng hạn như <img src=mouse.png>). Mà có thể được giải quyết bằng cách:

    • Chỉ sử dụng tài liệu tham khảo tuyệt đối cho máy chủ href="https://stackoverflow.com/old.html"hoặcsrc="/logo.png"
    • Thường chỉ đơn giản bằng cách thêm <base href="https://stackoverflow.com/index">vào <head>phần HTML của bạn . Điều này hoàn toàn phản bác các tham chiếu tương đối với những gì họ trước đây.

    Ngoài ra, bạn có thể tạo thêm RewriteRules để rebind .csshoặc .pngđường dẫn đến vị trí ban đầu của chúng. Nhưng điều đó không cần thiết, hoặc phát sinh thêm các chuyển hướng và cản trở bộ nhớ đệm.

    Xem thêm: CSS, JS và hình ảnh không hiển thị với url đẹp

  • RewriteConds chỉ che dấu một RewriteRule

    Một sự hiểu lầm phổ biến là RewriteCond chặn nhiều RewriteRules (vì chúng được sắp xếp trực quan với nhau):

     RewriteCond %{SERVER_NAME} localhost
     RewriteRule ^secret  admin/tools.php
     RewriteRule ^hidden  sqladmin.cgi
    

    Mà nó không theo mặc định. Bạn có thể xâu chuỗi chúng bằng [S=2]cờ. Khác bạn sẽ phải lặp lại chúng. Mặc dù đôi khi bạn có thể tạo quy tắc chính "đảo ngược" thành [END] để xử lý viết lại sớm.

  • QUERY_STRING được miễn từ RewriteRules

    Bạn không thể so khớp RewriteRule index.php\?x=y, vì mod_rewrite chỉ so sánh với các đường dẫn tương đối trên mỗi mặc định. Bạn có thể kết hợp chúng một cách riêng biệt tuy nhiên thông qua:

     RewriteCond %{QUERY_STRING} \b(?:param)=([^&]+)(?:&|$)
     RewriteRule ^add/(.+)$  add/%1/$1  # ←──﹪₁──┘
    

    Xem thêm Làm thế nào tôi có thể khớp các biến chuỗi truy vấn với mod_rewrite?

  • .htaccess so với <VirtualHost>

    Nếu bạn đang sử dụng RewriteRules trong tệp cấu hình trên mỗi thư mục, thì việc lo lắng về hiệu suất regex là vô nghĩa. Apache giữ lại các mẫu PCRE được biên dịch dài hơn một quy trình PHP với khung định tuyến chung. Tuy nhiên, đối với các trang web có lưu lượng truy cập cao, bạn nên xem xét việc chuyển các quy tắc vào cấu hình máy chủ vhost, khi chúng đã được thử nghiệm chiến đấu.

    Trong trường hợp này, thích ^/?tiền tố phân tách thư mục tùy chọn . Điều này cho phép di chuyển RewriteRules tự do giữa các tệp cấu hình máy chủ PerDir và máy chủ.

  • Bất cứ khi nào một cái gì đó không làm việc

    Không boăn khoăn.

    • So sánh access.logerror.log

      Thường thì bạn có thể tìm ra cách RewriteRule hoạt động sai chỉ khi nhìn vào error.logaccess.log. Tương quan thời gian truy cập để xem đường dẫn yêu cầu ban đầu được đưa vào và đường dẫn / tệp nào Apache không thể giải quyết (lỗi 404/500).

      Điều này không cho bạn biết RewriteRule nào là thủ phạm. Nhưng những con đường cuối cùng không thể tiếp cận như /docroot/21-.itle?index.phpcó thể cho đi nơi cần kiểm tra thêm. Nếu không thì vô hiệu hóa các quy tắc cho đến khi bạn nhận được một số đường dẫn dự đoán.

    • Kích hoạt RewriteLog

      Xem tài liệu Apache RewriteLog . Để gỡ lỗi, bạn có thể kích hoạt nó trong phần vhost:

      # Apache 2.2
      RewriteLogLevel 5
      RewriteLog /tmp/rewrite.log
      
      # Apache 2.4
      LogLevel alert rewrite:trace5
      #ErrorLog /tmp/rewrite.log
      

      Điều đó mang lại một bản tóm tắt chi tiết về cách các đường dẫn yêu cầu đến được sửa đổi theo từng quy tắc:

      [..] applying pattern '^test_.*$' to uri 'index.php'
      [..] strip per-dir prefix: /srv/www/vhosts/hc-profi/index.php -> index.php
      [..] applying pattern '^index\.php$' to uri 'index.php'
      

      Điều này giúp thu hẹp các quy tắc chung chung và rủi ro regex.

      Xem thêm:
      · .htaccess không hoạt động (mod_rewrite)
      · Mẹo để gỡ lỗi .htaccess viết lại quy tắc

    • Trước khi đặt câu hỏi của riêng bạn

      Như bạn có thể biết, Stack Overflow rất phù hợp để đặt câu hỏi trên mod_rewrite. Làm cho chúng theo chủ đề bằng cách bao gồm các nghiên cứu và cố gắng trước đó (tránh các câu trả lời dư thừa), chứng minh cơ bản Hiểu biết và:

      • Bao gồm các ví dụ đầy đủ về URL đầu vào, đường dẫn đích được viết lại sai, cấu trúc thư mục thực của bạn.
      • Bộ RewriteRule hoàn chỉnh, nhưng cũng chỉ tìm ra lỗi bị giả định.
      • Các phiên bản Apache và PHP, loại hệ điều hành, hệ thống tệp, $_SERVERmôi trường DOCUMENT_ROOT và PHP nếu đó là về một tham số không khớp.
      • Một đoạn trích từ của bạn access.logerror.logđể xác minh những quy tắc hiện có đã giải quyết. Tốt hơn nữa, một rewrite.logbản tóm tắt.

      Mạng này trả lời nhanh hơn và chính xác hơn, và làm cho chúng hữu ích hơn cho người khác.

  • Nhận xét của bạn .htaccess

    Nếu bạn sao chép các ví dụ từ một nơi nào đó, hãy cẩn thận để bao gồm a # comment and origin link. Mặc dù đó chỉ là cách cư xử tồi để bỏ qua sự quy kết, nhưng nó thường thực sự gây tổn hại cho việc bảo trì sau này. Tài liệu bất kỳ mã hoặc nguồn hướng dẫn. Đặc biệt, trong khi đảo ngược, bạn nên quan tâm nhiều hơn đến việc không coi chúng như hộp đen ma thuật.

  • Đó không phải là "SEO" -URL

    Disclaimer: Chỉ là một peeve vật nuôi. Bạn thường nghe thấy các lược đồ viết lại URL khá hay được gọi là liên kết "SEO" hoặc một cái gì đó. Mặc dù điều này rất hữu ích cho các ví dụ về Google, nhưng đó là một cách viết sai ngày.

    Không có công cụ tìm kiếm hiện đại nào thực sự bị làm phiền bởi .html.phptrong các đoạn đường dẫn hoặc ?id=123chuỗi truy vấn cho vấn đề đó. Các công cụ tìm kiếm cũ, chẳng hạn như AltaVista, đã tránh thu thập dữ liệu các trang web có đường dẫn truy cập tiềm tàng. Trình thu thập thông tin hiện đại thường thậm chí thèm các tài nguyên web sâu.

    Những URL "đẹp" nên được sử dụng một cách khái niệm là làm cho các trang web thân thiện với người dùng .

    1. Có sơ đồ tài nguyên dễ đọc và rõ ràng.
    2. Đảm bảo các URL tồn tại lâu dài ( permalinks AKA ).
    3. Cung cấp khả năng khám phá thông qua /common/tree/nesting.

    Tuy nhiên, đừng hy sinh các yêu cầu duy nhất cho sự tuân thủ.

Công cụ

Có nhiều công cụ trực tuyến khác nhau để tạo RewriteRules cho hầu hết các URL tham số GET:

Chủ yếu chỉ là các [^/]+trình giữ chỗ chung chung, nhưng có thể đủ cho các trang web tầm thường.


Vẫn cần một chút viết lại, nhiều liên kết hơn và nhiều tiêu đề phụ có phần đáng ghét. Có một số trùng lặp với các câu trả lời khác ở đây, vì vậy có thể được cắt giảm. Nó chủ yếu là về các ví dụ trực quan, và danh sách các vấn đề phổ biến.
mario

3
Đã không thấy một vẻ đẹp của một câu trả lời trong một thời gian dài! Mắt tôi đang phát sáng khi tôi đang đọc nó. Xin đừng ngừng đăng những câu trả lời như vậy :)
Rizier123

1
Tuyệt vời bài. Làm cho tôi hiểu các khái niệm cơ bản của mod_rewrite rất nhanh!
breez

6

Các lựa chọn thay thế cho mod_rewrite

Nhiều lược đồ URL ảo cơ bản có thể đạt được mà không cần sử dụng RewriteRules. Apache cho phép các tập lệnh PHP được gọi mà không cần .phpmở rộng và với một PATH_INFOđối số ảo .

  1. Sử dụng PATH_INFO , Luke

    Ngày nay AcceptPathInfo Onthường được bật theo mặc định. Về cơ bản cho phép .phpvà các URL tài nguyên khác mang một đối số ảo:

    http://example.com/script.php/virtual/path
    

    Bây giờ điều này /virtual/pathxuất hiện trong PHP như$_SERVER["PATH_INFO"] nơi bạn có thể xử lý bất kỳ đối số bổ sung nào theo cách bạn muốn.

    Đây không phải là thuận tiện như có Apache đoạn đường đầu vào riêng biệt vào $1, $2, $3và đi qua chúng như biệt $_GETbiến PHP. Nó chỉ đơn thuần là mô phỏng "các URL đẹp" với nỗ lực cấu hình ít hơn.

  2. Bật MultiViews để ẩn .phptiện ích mở rộng

    Tùy chọn đơn giản nhất để tránh .php"tiện ích mở rộng tệp" trong URL đang bật:

    Options +MultiViews
    

    Điều này có Apache chọn article.phpcho các yêu cầu HTTP /articledo tên cơ sở phù hợp. Và điều này hoạt động tốt cùng với tính năng PATH_INFO đã nói ở trên. Vì vậy, bạn chỉ có thể sử dụng các URL nhưhttp://example.com/article/virtual/title . Điều này có ý nghĩa nếu bạn có một ứng dụng web truyền thống với nhiều điểm / tập lệnh PHP.

    Lưu ý rằng MultiViews có mục đích khác / rộng hơn. Nó phải chịu một hình phạt hiệu năng rất nhỏ , bởi vì Apache luôn tìm kiếm các tệp khác có tên cơ sở phù hợp. Nó thực sự có nghĩa là cho Content-Negotiation , vì vậy các trình duyệt nhận được sự thay thế tốt nhất trong số các nguồn lực có sẵn (ví dụ như article.en.php, article.fr.php, article.jp.mp4).

  3. SetType hoặc SetHandler cho .phpcác tập lệnh mở rộng

    Một cách tiếp cận có định hướng hơn để tránh mang theo .phphậu tố trong các URL đang định cấu hình trình xử lý PHP cho các lược đồ tệp khác. Tùy chọn đơn giản nhất là ghi đè loại MIME / handler mặc định thông qua .htaccess:

    DefaultType application/x-httpd-php
    

    Bằng cách này, bạn có thể đổi tên article.phptập lệnh của mình thành chỉ article(không có phần mở rộng), nhưng vẫn xử lý nó thành tập lệnh PHP.

    Bây giờ điều này có thể có một số ý nghĩa về bảo mật và hiệu suất, bởi vì tất cả các tệp không có phần mở rộng sẽ được chuyển qua PHP ngay bây giờ. Do đó, bạn chỉ có thể đặt hành vi này cho các tệp riêng lẻ:

    <Files article>
      SetHandler application/x-httpd-php
      # or SetType 
    </Files>
    

    Điều này hơi phụ thuộc vào thiết lập máy chủ của bạn và PHPI đã sử dụng. Các lựa chọn thay thế phổ biến bao gồm ForceType application/x-httpd-phphoặc AddHandler php5-script.

    Một lần nữa lưu ý rằng các cài đặt như vậy lan truyền từ một .htaccessđến các thư mục con. Bạn luôn phải vô hiệu hóa thực thi tập lệnh ( SetHandler NoneOptions -Exechoặc php_flag engine offv.v.) cho tài nguyên tĩnh và tải lên / thư mục, v.v.

  4. Các lược đồ viết lại Apache khác

    Trong số nhiều tùy chọn của nó, Apache cung cấp mod_aliascác tính năng - đôi khi hoạt động giống như mod_rewriteRewriteRules. Lưu ý rằng hầu hết những thứ đó phải được thiết lập trong một <VirtualHost>phần, tuy nhiên, không phải trong .htaccesscác tệp cấu hình trên mỗi thư mục.

    • ScriptAliasMatchchủ yếu dành cho các tập lệnh CGI, nhưng cũng phải hoạt động cho PHP. Nó cho phép regexps giống như bất kỳ RewriteRule. Trong thực tế, nó có lẽ là tùy chọn mạnh mẽ nhất để cấu hình bộ điều khiển phía trước.

    • Và một đơn giản Aliasgiúp với một vài cách viết đơn giản là tốt.

    • Ngay cả một lệnh đơn giản ErrorDocumentcũng có thể được sử dụng để cho phép tập lệnh PHP xử lý các đường dẫn ảo. Lưu ý rằng đây là một cách giải quyết khác, tuy nhiên, nghiêm cấm mọi thứ trừ các yêu cầu GET và làm ngập lỗi.log theo định nghĩa.

    Xem http://httpd.apache.org/docs/2.2/urlmapping.html để biết thêm mẹo.

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.