본문 바로가기
Apache

mod_rewrite 로 URL 처리 예제

by 씨엔아이소프트 2017. 4. 18.
반응형

QUERY_STRING 을 Location 으로 전환

책의 정보를 보는 example.com/book.php?novel 라는 URL 이 있다. 이제 이 URL 을 example.com/book/novel 로 들어와도 정상 동작하게 설정해 보자.

RewriteRule ^/book/(.*) /book.php?$1 [L,PT]

 

여러 개의 QUERY_STRING 을 Location 으로 전환 

위 예제는 잘 동작하지만 다음과 같은 경우 제대로 동작하지 않는다. 

example.com/book.php?category=novel&bookid=1234

이 문제는 ? 뒤의 모든 파라미터를 하나의 파라미터로 전달하므로 발생한다. 다음과 같이 하면 URL 을 파싱해서 두 개의 파라미터로 변환해서 book.php 에 전달한다.

RewriteRule ^/book/([^/]*)/([^/]*) /book.php?category=$1&bookid=$2 [PT]

([^/]*)  정규식 패턴은 / 를 제외한 모든 문자를 의미하므로 URL 을 표현하게 된다.

 

기존 html 을 JSP 로 전환

사이트를 개편해서 기존 html 대신 jsp 를 제공하고 있다. URL 은 같고 파일의 확장자만 다를 경우 예전 html 을 호출하는 클라이언트는 다음 규칙을 통해 jsp 로 전환 시킬 수 있다.

RewriteRule ^/?([a-z/]+).html$ $1.jsp[L,R=301]


요청 리소스가 없을 경우 404 페이지로 전환

클라이언트가 요청하는 리소스가 없을 경우 별도의 에러 페이지로 전환 시킬 수 있다. 쉘 스크립트에서 사용되는 test 구문중 파일이 존재하는지를 판단하는 -f 와 디렉터리인지 판단하는 -d 옵션을 RewriteCond 에 사용할 수 있다.

다음 예제는 클라이언트가 요청한 파일 및 디렉터리가 존재하지 않을 경우 별도의 404 페이지를 실행하고 rewrite 처리를 종료하는 예제이다.

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

RewriteRule .? /404.php [L]


리소스의 외부 링크 차단

HTTP 프로토콜은 요청을 보낼 때 이 URI 를 어디에서 알았는지에 대한 정보를 전송한다. 다시 말하면 링크를 타고 왔을 때 이 요청을 링크한 페이지의 URL 에 대한 정보이다. 
이 HTTP 헤더를 리퍼러(Referer)라고 하며 이 정보를 이용하면 내부 페이지에서 링크되지 않고 외부에서 링크하여 들어오는 경우를 차단할 수 있다.

이 기능은 보통 이미지 파일등의 외부 링크를 차단할 때 많이 사용된다. 다음은 리퍼러가 example.com, 또는 www.example.com 이 아닐 경우 차단하는 예제이다. 링크된 페이지가 URL 에 대해 http 를 썼을 수도 있고 안 썼을 수도 있으므로 http:// 부분은 ? 문자로 처리해야 한다.

RewriteCond %{HTTP_REFERER} !^(https?:\/\/)?(www.)?example\.com/ [NC]
RewriteCond %{HTTP_REFERER} !^$
RewriteRule .(gif|jpe?g|png)$ - [F]

 

RewriteCond %{HTTP_REFERER} !^$ 가 있으면 리퍼가 정보가 없는 경우는 403 을 발생시키지 않는다. 
이는 리퍼러 정보를 제대로 보내지 않는 브라우저나 또는 사용자가 페이지 북마크를 한 경우 처리하기 위함이나 이런 경우에도 다 차단하려면 이 항목을 주석 처리하면 된다.

 

ServerAlias 로 연결시 대표 이름으로 전환

가상 호스트 설정에 ServerName 은 example.com 이고 ServerAlias 는 www.example.com, web.example.com, home.example.com 이 있다고 하자. ServerAlias 로 연결할 경우 example.com 으로 전환하고 싶다.

다음 설정은 클라이언트가 Host: 헤더에 보낸 호스트 이름이 example.com 이 아닐 경우 example.com 으로 전환하는 예제이다. 클라이언트가 호스트 이름을 대문자로 쓸 수도 있으므로 NC 플래그를 붙이는게 좋다.

RewriteCond %{HTTP_HOST} !^example\.com$ [NC]
RewriteRule (.*) http://example.com$1 [R,L]

 

HTTP 로 연결이 들어올 경우 HTTPS 로 전환

secure_page 디렉터리 밑에는 보안이 필요한 페이지들이 있다. 이 디렉터리 아래에 있는 모든 리소스들은 사용자가 실수로 http 로 들어와도 자동으로 https 로 전환하고 싶다. 

다음 규칙으로 요구 사항을 충족할 수 있다.

RewriteCond %{REQUEST_URI} ^/secure_page/
RewriteCond %{HTTPS} !on
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R,L]

 

모든 페이지를 HTTPS 로 전환

아주 중요한 서비스를 개발했고 모든 페이지에 HTTPS 를 적용하겠다고 가정해 보자. 고객은 보통 https 라는걸 잘 모르므로 이런 경우에도 http 포트를 열고 모든 http 는 https 로 전환하는게 좋다.

다음 규칙을 사용하면 모든 http 요청을 https 로 전환할 수 있다.

RewriteCond %{HTTPS}  !on
RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R,L]

 


htaccess 와 RewriteRule, RewriteCond 정규표현식 규칙


<중요>
1.  Rewrite 구문

  RewriteCond     TestString     CondPattern

  RewriteRule     Pattern        Substitution


* 수행순서
1. RewriteRule의 Pattern에 어긋나지 않는다면 RewriteCond의 TestString으로 가서 조건검사를 시작한다.
2. RewriteCond의 CondPattern을 지나 RewriteRule의  Substitution으로 처리되어 조건에 만족하게 된다.

Cond = Condition
Rule = Rule

즉, 규칙(Rule)의 Pattern 에 맞으면 조건(Cond)을 찾아 TestString 조건검사를 수행하며, 조건검사가 이상없을경우 규칙대로(Rule) Substitution으로 리다이렉트를 수행한다.


*주의*
RewriteCond 구문에서는 . ( ) [ ]    <- 이 5가지 문자에 대해서만 역슬래시로 구분을 짓는다. 
ex) \. ,  \( ,  \) ,  \[ ,  \] 

* 요약1
RewriteRule 정규표현식

  .   :  ?과 같습니다. 무엇이든 하나의 문자를 뜻합니다. A가 될수도 있고 Z가 될수 있다. 반드시 한글자 이상
  [A]   =>  역시 하나의 문자가 올수 있다는 뜻.
         ex) a[eo]t => aot 혹은 aet
  [
^A]  =>  A문자는 올수 없다는 뜻.
  ?     =>  0개 또는 1개의 텍스트.
  *     =>  0개 또는 1개 이상의 텍스트.
  +     =>  1개 이상의 텍스트. 0은 될수 없다.

*주의
- 대가로 [ ] 안에 있는 ^ 표시는 not을 의미한다.

*좋은예제1
  RewriteCond   %{HTTP_HOST}             ^[^.]+\.test\.co\.kr$
  RewriteRule   ^(.+)                     %{HTTP_HOST}$1 [C]
  RewriteRule   ^([^.]+)\.test\.co\.kr(.*)   /home/$1/htdocs$2

*해석 1
  RewriteRule 에 ( ) 안에 있는 구문은 $1이 되고, 두번째 나온 ( ) 는 $2가 되어 변수처럼 사용된다.
 즉, ???.test.co.kr.??? 도메인 중 첫번째 ???에 해당하는 내용이 $1이 되고, 두번째 ???에 해당하는 내용이 $2가 된다.
  다시한번 해석하면 컴마를 제외한 제일 하단의 도메인(제일 앞) ??? 값은 /home/??? 자리에 들어가게 될 것이다.
  두번째는 대가로 [ ] 가 아니기때문에 컴마를 컴마로 인식하지 않고 정규표현식으로 인식하므로 하나의 문자를 뜻한다.
  즉, 두번째 ???는 하나이상의 텍스트를 뜻하는데 개인적인 생각으로 말하자면 그냥 (+)만 쓰는것은 어떨까..
 
*참고
마지막 라인에서 /home/$1/htdocs$2 로 던질때 $2 앞에 "/"가 없는 이유는 kr 도메인 다음에 "/"가 오게 될텐데, (.*) 로서 "/"까지 변수로 가져오기 때문이다.



RewriteRule ^sub/(.*)/$ ./page/sub.php?page=$1
RewriteRule ^sub/(.*)/(.*)/$ ./page/sub.php?page=$1&nn=$2
RewriteRule ^sub/(.*)/(.*)/(.*)/$ ./page/sub.php?page=$1&nn=$2&sn=$3
RewriteRule ^sub/(.*)/(.*)/(.*)/(.*)/$ ./page/sub.php?page=$1&nn=$2&sn=$3&no=$4

이렇게 작성하면 http://localhost/sub/login/1/ 여기에 안들어가지고, 안됬던것들이

RewriteRule ^sub/([^/\.]+)/$ ./page/sub.php?page=$1
RewriteRule ^sub/([^/\.]+)/([^/\.]+)/$ ./page/sub.php?page=$1&nn=$2
RewriteRule ^sub/([^/\.]+)/([^/\.]+)/([0-9a-zA-Z-]+)/$ ./page/sub.php?page=$1&nn=$2&sn=$3

* 요약2
(.*) :한 글자 이상의 문자열(*이므로 한 글자도 없을 수도 있음, +라면 무조건 한 글자는 있어야 함)
([^/\.]+) :
/와 .이 포함되지 않은 한 글자 이상의 문자열
([0-9a-zA-Z-]+) : 0부터 9, a부터 z, A부터 Z, -로 이루어져 있는 한 글자 이상의 문자열
([0-9a-zA-Z-]*) : 0부터 9, a부터 z, A부터 Z, -로 이루어져 있는 한 글자 이상의 문자열이지만 한 글자도 없을 수도 있음.
([0-9a-zA-Z-]{1}) : 0부터 9, a부터 z, A부터 Z, -로 이루어져 있는 한 글자
([0-9a-zA-Z-]{2,}) : 0부터 9, a부터 z, A부터 Z, -로 이루어져 있는 두 글자 이상의 문자열
([0-9a-zA-Z-]{2,4}) : 0부터 9, a부터 z, A부터 Z, -로 이루어져 있는 두 글자에서 4글자 이하의 문자열 


[해결답변]
(.*)로 했을 때 되지 않는 이유: (.*)은 /도 모두 포함하므로 URL의 끝까지 $1로 인식함. 다시 말하면 뭘 입력하든 첫 번째 'RewriteRule ^sub/(.*)/$ ./page/sub.php?page=$1'에서 모두 True가 되어버림.

([^/\.]+)로 하면 되는 이유: 입력값은 /를 포함하지 않아야 하므로 입력값에 따라 첫 번째 RewriteRule이 False가 되고 다른 RewriteRule을 체크할 수 있음.





* .htaccess에서의 특수문자
 - 예를들면 프로그래밍을 하면서 그 프로그램 내부 코드명령어로 쓰이고 있어서 쓰지 못하는 문자가 있다.
 - 그래서 역슬래시(\)를 사용하여 모든 문자를 사용하도록 했다.
 - 주로 점 "." 이나 대괄호"[]", 괄호"( )" 등에 쓰입니다. 

* 정규표현식 기초
 .: 다수의 한문자
?: 0개 이상의 한문자
*: 0개 이상의 문자 또는 문자열
+: 1개 이상의 문자 또는 문자열
(chars): (, ) 안의 문자또는 문자열을 그룹으로 묶습니다. 이 문자그룹은 Substitution(return URL)에서 $N 의 변수로 활용할수 있습니니다.
^: 문자열의 첫문(열)을 지정합니다.
$: 문자열의 끝 문자(열)을 지정합니다.

\(역슬래쉬): 정규표현식에서 특별한 의미로 사용되는 문자의 특수기능을 제거합니다.(예:(, ), [, ], . 등)
{n}: 정확히 n번 반복
{n,}: n번 이상 반복
{n,m}: n 이상 m 이하 반복
[chars]: 문자들의 범위 또는 표현할 수 있는 문자
ex) [a-z]: a 부터 z 까지의 소문자, [tT] : 소문자 t 또는 대문자 T

*정규표현식 단축표현

[:alpha:] : 알파벳. [a-zA-Z] 와 같은 표현
[:alnum:] : 알파벳과 숫자. [a-zA-Z0-9] 와 같은 표현
[:digit:] : 숫자 [0-9] 와 같은 표현
[:upper:] : 대문자. [A-Z] 와 같은 표현


* RewriteCond 명령어
 -d: 디렉토리를 의미합니다. TestString이 이렉토리를 가리키거나 포함하고 있을 때 처리됩니다.
 -f: 파일을 의미합니다. TestString이 파일을 가리키더나 포함하고 있을 때 처리됩니다.
 -l: 심볼릭링크를 의미합니다. TestString이 심볼릭링크를 가리키거나 포함하고 있을 때 처리됩니다.
 마지막으로 느낌표(!)는 부정을 뜻합니다.

*좋은예제2
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ http://test.domain.co.kr/$1

위 예제는 Request 내용이 디렉토리나 파일을 가리키고 있지 않을 경우 저쪽 사이트로 접속하게 만들라는 의미입니다. $1로 하위주소는 유지하고 있다.

* Flag
라인마다 Flag라 불리는 기능을 사용할 수 있다.

F: 403 Forbidden 에러 페이지를 출력합니다. 접근 권한이 없다는 의미.
L: Last(마지막)를 의미. Rewrite 끝.
N: 새로운 Rule이 시작된다는 의미.
QSA: Cond의 내용을 지난 결과 이어서 덧붙임.
NE: Out 될 값에 특수문자가 HexCode로 되어 포함되어 있는 경우
R: Redirection. 무조건 넘김. 뒤 주소로 넘긴다.
NC: 대소문자를 구별하지 않는다.
OR: 프로그래밍의 or.
위의 Flag들을 사용하여 아래와 같은 예제가 탄생했습니다!

RewriteCond %{REMOTE_HOST} ^domain.* [OR]
RewriteCond %{REMOTE_HOST} ^DOMAIN.* [OR]
RewriteCond %{REMOTE_HOST} ^DOMAIN2.* [NC]
RewriteRule ^(.*)$ http://www.domain.co.kr/$1 [R,L]

domain, domain2, DOMAIN, DOMAIN2로부터 접속한 경우 Redirect합니다. http://www.domain.co.kr로 접속하게 된다.
더 쉽게 말하자면, ^ 는 시작을 알리고, domian.??? DOMAIN.??? 의 유알엘은 OR로 되어있다. 추가적으로 DOMAIN2.??? 가 OR로 묶여있다.
여기서 마지막 Cond의 플래그가 [NC] 이기 때문에 대소문자를 가리지 않고 모두 조건충족.
즉, domain, domain2, DOMAIN, DOMAIN2로부터 접속한 경우 Redirect를 하게 되는 것이다.


참고 : http://community.plusonnet.co.kr/?p=56





반응형

댓글