[apache] mod_rewrite의 숨겨진 기능

mod_rewrite최근에 특정 측면이 작동하는 방식에 대해 약간의 혼란과 함께 적절한 수의 스레드가 떠 다니는 것 같습니다 . 결과적으로 나는 일반적인 기능에 대한 몇 가지 메모와 아마도 몇 가지 성가신 뉘앙스를 편집했습니다.

사용하면서 겪은 다른 기능 / 일반적인 문제는 무엇입니까 mod_rewrite?



답변

mod_rewrite 규칙을 배치 할 위치

mod_rewrite규칙은 httpd.conf파일 또는 파일 내에 배치 될 수 있습니다 .htaccess. 에 액세스 할 수있는 경우 httpd.conf여기에 규칙을 배치하면 성능 이점이 제공됩니다 ( .htaccess파일이 호출 될 때마다 규칙이 한 번 처리되기 때문에 ).

mod_rewrite 요청 로깅

httpd.conf파일 내에서 로깅을 활성화 할 수 있습니다 (포함 <Virtual Host>).

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

일반적인 사용 사례

  1. 모든 요청을 단일 지점으로 전달하려면 :

    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
    

    Apache 2.2.16부터 FallbackResource.

  2. 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]
    

    참고 : 외부 리디렉션은 암시 적으로 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. SSL 강제

    RewriteEngine on
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://example.com/$1 [R,L]
    
  4. 일반적인 플래그 :

    • [R]또는 [redirect]-강제 리디렉션 (기본값은 302 임시 리디렉션)
    • [R=301]또는 [redirect=301]-301 영구 리디렉션 강제
    • [L]또는 [last]-재 작성 프로세스 중지 (일반적인 함정에 대해서는 아래 참고 참조)
    • [NC]또는 [nocase]-일치가 대소 문자를 구분하지 않도록 지정

    긴 형식의 플래그를 사용하는 것이 더 읽기 쉽고 나중에 코드를 읽는 다른 사람들에게 도움이됩니다.

    쉼표로 여러 플래그를 구분할 수 있습니다.

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

일반적인 함정

  1. mod_alias스타일 리디렉션과 혼합mod_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
    

    참고 : mod_alias와 함께 사용할 수 mod_rewrite있지만 위와 같이 기본 리디렉션을 처리하는 것보다 더 많은 작업이 필요합니다.

  2. 컨텍스트가 구문에 영향을 미침

    .htaccess파일 내 에서 선행 슬래시는 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]은 마지막이 아닙니다! (때때로)

    [L]플래그는 상기 다시 쓰기 규칙 처리를 중지 규칙 세트를 통해 해당 패스 . 그러나 해당 패스에서 URL이 수정되고 .htaccess컨텍스트 또는 <Directory>섹션에있는 경우 수정 된 요청이 URL 구문 분석 엔진을 통해 다시 전달됩니다. 그리고 다음 패스에서 이번에는 다른 규칙과 일치 할 수 있습니다. 당신이 이것을 이해하지 못한다면, 당신의 [L]깃발이 효과가 없었던 것처럼 보입니다 .

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

    재 작성 로그는 규칙이 두 번 실행되고 URL이 두 번 업데이트되었음을 ​​보여줍니다.

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

    이 문제를 해결하는 가장 좋은 방법 은 규칙의 모든 추가 처리 (및 후속 패스)를 진정으로 중지하려는 경우 플래그 대신 [END]플래그 ( Apache 문서 참조 )를 사용하는 것입니다 [L]. 그러나 [END]플래그는 Apache v2.3.9 + 에서만 사용할 수 있으므로 v2.2 이하를 사용하는 경우 [L]플래그 만 붙어 있습니다.

    이전 버전 RewriteCond의 경우 URL 구문 분석 엔진의 후속 패스에서 규칙이 일치하지 않도록 명령문에 의존해야합니다 .

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

    또는 RewriteRule이 httpd.conf요청을 다시 구문 분석하지 않는 컨텍스트 (예 :)에 있는지 확인해야합니다 .


답변

.htaccess에서 발생하는 내부 리디렉션 / 재 작성을 ‘차단’해야하는 경우

RewriteCond %{ENV:REDIRECT_STATUS} ^$

여기에 설명 된 대로 조건 .


답변

RewriteBase와의 거래 :

거의 항상 RewriteBase를 설정해야합니다. 그렇지 않으면 아파치는 기본이 디렉토리에 대한 물리적 디스크 경로라고 추측합니다. 따라서 다음과 같이 시작하십시오.

RewriteBase /


답변

기타 함정 :

1- 때때로 MultiViews를 비활성화하는 것이 좋습니다.

Options -MultiViews

나는 모든 MultiViews 기능에 대해 좋은 구절은 아니지만, 속성 중 하나가 내가 찾고 있다고 생각하는 파일에 대한 확장자를 시도하고 ‘추측’하는 것이기 때문에 활성화되면 mod_rewrite 규칙을 엉망으로 만듭니다. .

설명하겠습니다 : 웹 디렉토리에 2 개의 php 파일, file1.php 및 file2.php가 있고 다음 조건과 규칙을 .htaccess에 추가한다고 가정합니다.

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

파일 또는 디렉토리와 일치하지 않는 모든 URL이 file1.php에 의해 확보된다고 가정합니다. 놀라다! 이 규칙은 URL http : // myhost / file2 / somepath에 적용되지 않습니다 . 대신 file2.php 내부로 이동합니다.

무슨 일이 일어나고 있는지 MultiViews는 실제로 원하는 URL이 http : //myhost/file2.php/somepath 라고 자동으로 추측 하여 기꺼이 당신을 데려갔습니다.

이제 방금 무슨 일이 일어 났는지 전혀 알지 못하며, 그 시점에서 mod_rewrite에 대해 알고 있다고 생각했던 모든 것에 의문을 제기하고 있습니다. 그런 다음이 새로운 상황의 논리를 이해하기 위해 규칙을 가지고 놀기 시작하지만 더 많이 테스트할수록 의미가 떨어집니다.

간단히 말해, mod_rewrite가 로직에 가까운 방식으로 작동하도록하려면 MultiView를 끄는 것이 올바른 방향으로 나아가는 단계입니다.

2- FollowSymlinks 활성화

Options +FollowSymLinks

저것은 세부 사항을 잘 모르지만 여러 번 언급 한 적이 있으니 그냥하세요.


답변

방정식은 다음 예를 통해 수행 할 수 있습니다.

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]

동적 부하 분산 :

mod_proxy를 사용하여 시스템의 균형을 맞추는 경우 작업자 서버의 동적 범위를 추가 할 수 있습니다.

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


답변

[L] 플래그를 더 잘 이해하는 것이 좋습니다. [L] 플래그 마지막입니다. 요청이 URL 구문 분석 엔진을 통해 다시 라우팅되는 원인을 이해하면됩니다. 문서에서 ( http://httpd.apache.org/docs/2.2/rewrite/flags.html#flag_l ) (강조 내) :

[L] 플래그는 mod_rewrite가 규칙 세트 처리를 중지하도록합니다. 대부분의 컨텍스트에서 이는 규칙이 일치하면 더 이상 규칙이 처리되지 않음을 의미합니다. 이것은 Perl의 마지막 명령 또는 C의 break 명령에 해당합니다.이 플래그를 사용하여 추가 규칙을 고려하지 않고 현재 규칙을 즉시 적용해야 함을 나타냅니다.

.htaccess 파일 또는 <Directory>섹션 에서 RewriteRule을 사용 하는 경우 규칙이 처리되는 방법을 이해하는 것이 중요합니다. 이것의 단순화 된 형태는 일단 규칙이 처리되면 재 작성된 요청이 URL 구문 분석 엔진으로 다시 전달 되어 그것으로 할 수있는 일을 할 수 있다는 것입니다. 재 작성된 요청이 처리<Directory>
될 때.htaccess 파일 또는섹션이 다시 발생할 수 있으므로 규칙 세트가 처음부터 다시 실행될 수 있습니다. 대부분의 경우 규칙 중 하나가 내부 또는 외부 리디렉션으로 인해 요청 프로세스가 다시 시작되는 경우에 발생합니다.

따라서 [L] 플래그 규칙 세트 를 통과 하는 해당 패스에 대한 추가 다시 쓰기 규칙 처리를 중지 합니다. 그러나 [L]로 표시된 규칙이 요청을 수정하고 .htaccess 컨텍스트 또는 <Directory>섹션에있는 경우 수정 된 요청이 URL 구문 분석 엔진을 통해 다시 전달됩니다. 그리고 다음 패스에서 이번에는 다른 규칙과 일치 할 수 있습니다. 무슨 일이 일어 났는지 이해가 안된다면 [L] 플래그를 사용한 첫 번째 다시 쓰기 규칙이 효과가없는 것 같습니다.

이 문제를 해결하는 가장 좋은 방법은 정말로 중지하려는 경우 [L] 플래그 대신 [END] 플래그 ( http://httpd.apache.org/docs/current/rewrite/flags.html#flag_end )를 사용하는 것입니다. 모든 추가 규칙 처리 (및 후속 재분석). 그러나 [END] 플래그는 Apache v2.3.9 +에서만 사용할 수 있으므로 v2.2 이하를 사용하는 경우 [L] 플래그 만 사용됩니다. 이 경우 RewriteCond 문을 사용하여 URL 구문 분석 엔진의 후속 패스에서 규칙이 일치하지 않도록해야합니다. 또는 RewriteRule이 요청을 다시 구문 분석하지 않는 컨텍스트 (예 : httpd.conf)에 있는지 확인해야합니다.


답변

또 다른 훌륭한 기능은 rewrite-map-expansions입니다. 처리 할 호스트 / 재 작성이 엄청나게 많은 경우 특히 유용합니다.

키-값 대체와 같습니다.

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

그런 다음 다음과 같은 규칙에 매핑을 사용할 수 있습니다.

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

이 주제에 대한 자세한 정보는 여기에서 찾을 수 있습니다.

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