NGINX 요청 처리 원리: server와 location 선택

NGINX 요청 처리 원리: server와 location 선택

요청 하나가 NGINX에 들어오면 "어떤 server 블록이 처리할까?"라는 질문부터 시작해요. 그리고 그다음에는 "어떤 location 블록이 처리할까?"라는 질문이 이어지죠. 이 문서는 이 두 단계의 선택 규칙을 실제 설정 예시와 함께 풀어내는데, 이름 기반 가상 서버(virtual server)와 default_server, 그리고 프리픽스·정규표현식 location 매칭 순서가 핵심이에요.

출처: NGINX 공식 문서 - How nginx processes a request

이름 기반 가상 서버

NGINX는 먼저 요청을 처리할 server 블록을 결정해요. 아래처럼 세 개의 가상 서버가 모두 80 포트에서 듣는 단순한 설정을 보겠습니다. NGINX는 이 경우 요청의 Host 헤더 필드만 보고 요청을 라우팅할 서버를 정해요.

server { listen 80; server_name example.org www.example.org; ... }
server { listen 80; server_name example.net www.example.net; ... }
server { listen 80; server_name example.com www.example.com; ... }

Host 값이 어떤 서버 이름과도 맞지 않거나, 요청에 Host 헤더가 아예 없다면 NGINX는 **해당 포트의 기본 서버(default server)**로 라우팅해요. 위 설정에서 기본 서버는 첫 번째 서버인데, 이게 NGINX의 표준 기본 동작이에요. 어떤 서버를 기본으로 삼을지 명시적으로 지정할 수도 있는데, listen 지시어에 default_server 파라미터를 붙이면 되요.

server { listen 80 default_server; server_name example.net www.example.net; ... }

기본 서버가 listen 포트의 속성이라는 점을 기억해 둘 필요가 있어요. 서버 이름의 속성이 아니라 포트에 딸린 속성이기 때문에, 포트마다 다른 기본 서버를 둘 수 있어요.

서버 이름이 정의되지 않은 요청 막기

Host 헤더 필드가 없는 요청을 받고 싶지 않다면, 그런 요청을 그냥 끊어버리는 서버를 정의하면 돼요.

server { listen 80; server_name ""; return 444; }

여기서는 서버 이름을 빈 문자열("")로 두어 Host 헤더가 없는 요청과 매칭시키고, NGINX 비표준 응답 코드인 444를 돌려 연결을 닫아버려요. 444는 응답을 보내지 않고 연결만 끊는 특수 코드예요. 버전 0.8.48 이후로는 빈 문자열 서버 이름이 기본값이라 server_name ""; 는 생략할 수 있어요.

이름·IP 기반 가상 서버 혼합

서버들이 서로 다른 주소에서 듣는 더 복잡한 구성을 볼게요.

server { listen 192.168.1.1:80; server_name example.org www.example.org; ... }
server { listen 192.168.1.1:80; server_name example.net www.example.net; ... }
server { listen 192.168.1.2:80; server_name example.com www.example.com; ... }

이 구성에서 NGINX는 먼저 요청의 IP 주소와 포트를 각 server 블록의 listen 지시어와 비교해요. 그다음, IP·포트가 매칭된 server 블록들의 server_name 항목과 요청의 Host 헤더를 비교합니다. 서버 이름을 찾지 못하면 그 포트의 기본 서버가 요청을 처리해요.

이미 언급했듯 기본 서버는 listen 포트의 속성이므로, 포트마다 서로 다른 기본 서버를 지정할 수 있어요.

server { listen 192.168.1.1:80; server_name example.org www.example.org; ... }
server { listen 192.168.1.1:80 default_server; server_name example.net www.example.net; ... }
server { listen 192.168.1.2:80 default_server; server_name example.com www.example.com; ... }

간단한 PHP 사이트 설정 예시

이제 전형적인 PHP 사이트에서 NGINX가 어떤 location 으로 요청을 보낼지 정하는 과정을 살펴볼게요.

server {
    listen 80;
    server_name example.org www.example.org;
    root /data/www;

    location / {
        index index.html index.php;
    }

    location ~* \.(gif|jpg|png)$ {
        expires 30d;
    }

    location ~ \.php$ {
        fastcgi_pass localhost:9000;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

NGINX는 먼저 리터럴 문자열로 주어진 프리픽스 location 중 **가장 구체적인 것(가장 긴 프리픽스)**을 찾아요. 등장 순서는 상관없어요. 위 구성의 유일한 프리픽스 location 은 "/" 인데, 모든 요청과 매칭되므로 마지막 수단으로 사용돼요.

그다음 NGINX는 설정 파일에 적힌 순서대로 정규표현식 location 을 검사해요. 첫 번째로 매칭되는 식에서 검색이 멈추고 그 location 을 사용해요. 정규표현식 location 이 어느 것도 매칭되지 않으면, 앞서 찾아둔 가장 구체적인 프리픽스 location 을 사용합니다. ~ 는 대소문자 구분 정규표현식, ~* 는 대소문자 무시 정규표현식이에요.

한 가지 짚고 넘어갈 점은 모든 유형의 location 이 요청 라인의 URI 부분만 테스트한다는 거예요. 쿼리 문자열에 담긴 인자는 포함되지 않아요. 그 이유는 인자가 여러 방식으로 주어질 수 있기 때문이죠.

/index.php?user=john&page=1
/index.php?page=1&user=john

그리고 누구든 쿼리 문자열에 어떤 것이든 넣어 요청할 수 있어요.

/index.php?page=1&something+else&user=john

이 구성에서 실제 요청이 어떻게 처리되는지 몇 가지 따라가 볼게요.

  • /logo.gif — 프리픽스 location "/" 에 먼저 걸리고, 이후 정규표현식 \.(gif|jpg|png)$ 에 걸려 후자 location 이 처리해요. root /data/www 에 따라 /data/www/logo.gif 파일을 찾아 클라이언트에 보내줘요.
  • /index.php — 프리픽스 "/" 에 걸린 뒤 정규표현식 \.(php)$ 에 걸려 FastCGI 위치로 전달돼요. fastcgi_paramSCRIPT_FILENAME/data/www/index.php 로 설정하고, 9000 포트의 FastCGI 서버가 그 파일을 실행해요. $document_rootroot 지시어의 값, $fastcgi_script_name 은 요청 URI(/index.php)와 같아요.
  • /about.html — 프리픽스 "/" 에만 걸려 이 location 에서 처리돼요. /data/www/about.html 파일을 찾아 보내주죠.
  • / — 조금 더 복잡해요. 프리픽스 "/" 에만 걸려 이 location 이 처리하는데, index 지시어가 지시어 파라미터와 root /data/www 를 보고 인덱스 파일의 존재를 검사해요. /data/www/index.html 이 없고 /data/www/index.php 가 있다면 내부 리다이렉트로 /index.php 를 다시 요청하고, NGINX는 마치 클라이언트가 보낸 것처럼 location 을 다시 탐색해요. 결국 그 리다이렉트된 요청은 위에서 봤듯 FastCGI 서버가 처리하게 됩니다.

더 알아보기 (Learn more)