Nginx là 1 web server đang càng ngày được sử dụng phổ biến. Tuy vậy việc cấu hình nó có một chút khó khăn đối với newbie. Bài viết này sẽ giới thiệu cơ bản về các khái niệm và một số directive cơ bản trong cấu hình của nginx.

1. Các khái niệm liên quan tới nginx

a. Nginx?

Nginx là một web server có thể được dùng như một reverse proxy, load balancer, mail proxy và HTTP cache.

Tính năng của nginx:

  • Khả năng xử lý hơn 10k request trong khi sử dụng ít tài nguyên máy.
  • Phục vụ static file, index file và auto indexing.
  • Cache response để tăng tốc.
  • Load balancing với health checking.
  • Cho phép sửa đổi HTTP Header, URL rewriting và redirection.
  • Hỗ trợ HTTPS/SSL, IPv6, Websoctket.

b. Reverse proxy?

Là một loại proxy server trung gian giữa server và các client. Nó được đặt bên phía server. Nó kiểm soát yêu cầu của các client, nếu hợp lệ, sẽ chuyển đến các server thích ứng.

Có 1 loại proxy server khác là forward proxy, nó là một trung gian cho các client liên hệ với nó liên lạc với bất kỳ server nào, còn reverse proxy là một trung gian cho các server liên hệ với nó được liên lạc bởi bất kỳ client nào.
So sánh Reverse proxy vs Forward Proxy
Ảnh từ https://docs.microsoft.com

c. Load balancer

Load balancer là một phương pháp để tối ưu khả năng sử dụng tài nguyên. Tài nguyên ở đây có thể là network, disk, web server,...
Load balancer có thể là thiết bị vật lý hoặc application.
Mục đích của load balancer là tối ưu tài nguyên sử dụng, tối đa hóa thông lượng, tối thiểu thời gian phản hồi, tránh quá tải ở một đơn vị tài nguyên từ đó đảm bảm hệ thống ổn định và luôn sẵn có (available).

2. File cấu hình nginx

Việc cài đặt nginx còn tùy thuộc vào mỗi os nên trong này không nhắc đến. Tham khảo tại đây hoặc sử dụng docker image.
Tùy vào os mà file cấu hình có thể tìm thấy ở /usr/local/nginx/conf, /etc/nginx, hoặc /usr/local/etc/nginx.
Một file cấu hình bao gồm các directive và các context:

worker_processes 2; # directive in main context

http {              # http context
    gzip on;        # directive in http context

  server {          # server context (virtual server)
    listen 80;      # directive in server context
  }
}

3. Directive

Directive là một cấu hình nào đó nó bao gồm tên cấu hình và tham số tương ứng. Nó nên kết thúc với ;. Đây là ví dụ:

user             nobody;
error_log        logs/error.log notice;
worker_processes 1;

Có 1 directive đặc biệt là include cho phép tách file config ra thành nhiều file nhỏ

include conf.d/http;
include conf.d/stream;
include conf.d/exchange-enhanced;

Directive có thể sử dụng nhiều lần ở cùng một context cũng như ở các context con với khả năng kế thừa. Có 3 loại directive:

Normal

Chỉ có một giá trị trong cùng một context.
Directive ở context con sẽ override directive đó ở context cha.

gzip on;
gzip off; # Không hợp lệ

server {
  location /downloads {
    gzip off; # Override gzip on ở parent context
  }
}

Array

Cho phép nhiều giá trị trong cùng một context .
Directive ở context con sẽ override tất cả directive đó ở context cha.

error_log /var/log/nginx/error.log;
error_log /var/log/nginx/error_notive.log notice;
error_log /var/log/nginx/error_debug.log debug;

server {
  location /downloads {
    # Chỉ còn directive này có tác dụng
    error_log /var/log/nginx/error_downloads.log;
  }
}

Action directive

Cho phép thay đổi gì đó ở request.
Hành vi kế thừa tùy thuộc vào loại directive.
Đây là ví dụ về directive rewrite - thay đổi url của request

server {
  rewrite ^ /foobar;

  location /foobar {
    rewrite ^ /foo;
    rewrite ^ /bar;
  }
}

Nếu người dùng fetch /anything thì

  • /anything => /foobar
  • context location /foobar sẽ được thực thi
  • /foobar => /foo
  • /foo => /bar

4. Context

Context là một vùng để chứa các directive. Nó tương tự như một scope trong ngôn ngữ lập trình

http {
# cấu hình http
}

Toàn bộ file được gọi là main context.
Có một số ít context ở top-level:

  • events: Cấu hình chung
  • http: Cấu hình cho http traffic
  • mail: Cấu hình cho mail traffic
  • stream: Cấu hình cho TCP/UDP traffic

Trong mỗi traffic context bao gồm một hoặc nhiều server context. Nó được gọi là virtual server. Tùy thuộc vào loại traffic mà trong server context bao gồm context con như location, stream, ...

user nobody; # directive ở 'main' context

events {
    # cấu hình chung
}

http {
    # cấu hình cho HTTP tác động tới tất cả virtual server trong nó 

    server {
        # cấu hình cho HTTP virtual server 1
        location /one {
            # cấu hình xử lý cho URI bắt đầu bằng '/one'
        }
        location ~ \.(jpe?g|png|gif|ico)$ {
            # cấu hình xử lý cho URI là image sử dụng regex
        }
    } 
    
    server {
        # cấu hình cho HTTP virtual server 2
    }
}

stream {
    # cấu hình cho TCP/UDP traffic tác động tới tất cả virtual server trong nó
    server {
        # cấu hình cho TCP/UDP virtual server 1 
    }
}

5. Xử lý request

Đây là ví dụ về cách mà nginx xử lý request:

http {
    server {
      listen      *:80 default_server;
      server_name netguru.co;

      return 200 "Hello from netguru.co";
    }

    server {
      listen      *:80;
      server_name foo.co;

      return 200 "Hello from foo.co";
    }

    server {
      listen      *:81;
      server_name bar.co;

      return 200 "Hello from bar.co";
    }
}

Nginx sẽ tìm virtual server để xử lý dựa vào listen directive và server_name directive:

  • listen directive để so sánh IP:PORT mà virtual server đang lắng nghe.
  • server_name để so sánh với domain name của server (trong Host header).

Nginx chọn virtual server theo thứ tự:

  1. Khớp với IP:port và server_name.
  2. Khớp với IP:port và với default_server flag.
  3. Khớp với cái IP:port đầu tiên.
  4. Từ chối request.

Như ví dụ ở trên thì:

Request to foo.co:80     => "Hello from foo.co"
Request to www.foo.co:80 => "Hello from netguru.co"
Request to bar.co:80     => "Hello from netguru.co"
Request to bar.co:81     => "Hello from bar.co"
Request to foo.co:81     => "Hello from bar.co"

6. Các directive cơ bản

a. server_name

server_name netguru.co www.netguru.co; # exact match
server_name *.netguru.co;              # wildcard matching
server_name netguru.*;                 # wildcard matching
server_name  ~^[0-9]*\.netguru\.co$;   # regexp matching

Nginx sẽ ưu tiên theo thứ tự sau:

  1. server_name chính xác.
  2. Wildcard dài nhất bắt đầu bằng *, ví dụ: *.example.org
  3. Wildcard dài nhất kết thúc bằng *, ví dụ: "mail.*"
  4. Regex đầu tiên

b. listen

listen 127.0.0.1:80;
listen 127.0.0.1;    # Port mặc đinh là 80

listen *:81;
listen 81;           # Tất cả các ip

listen [::]:80;      # Địa chỉ IPv6
listen [::1];        

listen unix:/var/run/nginx.sock; # UNIX-domain sockets

listen localhost:80; # Hostname
listen netguru.co:80;

Nếu không cấu hình listen directive thì *:80, sẽ được sử dụng.

c. root

Cho phép nginx ánh xạ yêu cầu tương ứng với file trong hệ thống.

# netguru.co/index.html returns /var/www/netguru.com/index.html
server {
  listen 80;
  server_name netguru.co;
  root /var/www/netguru.co;
}

d. location

Bao gồm các cấu hình tương ứng với URI.
location [modifier] path { # Các cấu hình }
Các modifier và thứ tự ưu tiên:

=           - Match chính xác
^~          - Match ưu tiên
~ && ~*     - Match regex (* để không phân biệt chữ hoa)
Để trống    - Match tiền tố

Ví dụ:

location /match {
  return 200 'Match tiền tố: match với uri bắt đầu bằng /match';
}

location ~* /match[0-9] {
  return 200 'Match regex không phân biệt chữ hoa';
}

location ~ /MATCH[0-9] {
  return 200 'Match regex phân biệt chữ hoa';
}

location ^~ /match0 {
  return 200 'Match ưu tiên';
}

location = /match {
  return 200 'Match chính xác';
}

Kết qủa:

/match     # => 'Match chính xác'
/match0    # => 'Match ưu tiên'
/match1    # => 'Match regex không phân biệt chữ hoa'
/MATCH1    # => 'Match regex phân biệt chữ hoa'
/match-abc # => 'Match tiền tố: match với uri bắt đầu bằng /match'

e. proxy_pass

Chuyển request tới server khác:

proxy_pass http://localhost:8000

f. index

Xử lý request với url kết thúc bằng /.

server {
    server_name netguru.co;
    root /var/www/netguru.co;
    
    location / {
        index index.html;
    }
}
# netguru.co/ returns /var/www/netguru.com/index

7. Tham khảo

https://www.netguru.com/codestories/nginx-tutorial-basics-concepts
https://docs.nginx.com/nginx/admin-guide/basic-functionality/managing-configuration-files/