Thứ hai, 08/12/2014 | 00:00 GMT+7

Hiểu và triển khai FastCGI Proxying trong Nginx

Nginx đã trở thành một trong những giải pháp web server linh hoạt và mạnh mẽ nhất hiện có. Tuy nhiên, về mặt thiết kế, trước hết nó là một server proxy. Trọng tâm này nghĩa là Nginx rất hiệu quả khi làm việc để xử lý các yêu cầu với các server khác.

Nginx có thể gửi các yêu cầu proxy bằng http, FastCGI, uwsgi, SCGI hoặc memcached. Trong hướng dẫn này, ta sẽ thảo luận về FastCGI proxy, là một trong những giao thức proxy phổ biến nhất.

Tại sao sử dụng FastCGI Proxying?

Proxying FastCGI trong Nginx thường được sử dụng để dịch các yêu cầu của client cho một server ứng dụng không hoặc không nên xử lý trực tiếp các yêu cầu của client . FastCGI là một giao thức dựa trên CGI trước đó, hoặc giao diện cổng thông thường, giao thức nhằm cải thiện hiệu suất bằng cách không chạy từng yêu cầu như một quy trình riêng biệt. Nó được sử dụng để giao tiếp hiệu quả với server xử lý các yêu cầu về nội dung động.

Một trong những trường hợp sử dụng chính của FastCGI proxy trong Nginx là để PHP processor . Không giống như Apache, có thể xử lý PHP processor trực tiếp bằng cách sử dụng module mod_php , Nginx phải dựa vào một PHP processor riêng để xử lý các yêu cầu PHP. Thông thường, quá trình xử lý này được xử lý bằng php-fpm , một PHP processor đã được thử nghiệm rộng rãi để hoạt động với Nginx.

Nginx với FastCGI được dùng với các ứng dụng sử dụng ngôn ngữ khác miễn là có một thành phần có thể truy cập được cấu hình để phản hồi các yêu cầu FastCGI.

Kiến thức cơ bản về FastCGI Proxying

Nói chung, các yêu cầu proxy liên quan đến server proxy, trong trường hợp này là Nginx, chuyển tiếp các yêu cầu từ client đến server backend . Lệnh mà Nginx sử dụng để xác định server thực tế để ủy quyền sử dụng giao thức FastCGI là fastcgi_pass .

Ví dụ: để chuyển tiếp bất kỳ yêu cầu phù hợp nào đối với PHP tới một phần backend dành để xử lý quá trình PHP processor bằng giao thức FastCGI, một khối vị trí cơ bản có thể trông giống như sau:

# server context  location ~ \.php$ {     fastcgi_pass 127.0.0.1:9000; }  . . .  

Đoạn mã trên sẽ không thực sự hoạt động hiệu quả vì nó cung cấp quá ít thông tin. Bất kỳ lúc nào kết nối proxy được thực hiện, yêu cầu ban đầu phải được dịch đảm bảo rằng yêu cầu được ủy quyền có ý nghĩa đối với server backend . Vì ta đang thay đổi giao thức với thẻ FastCGI, điều này liên quan đến một số công việc bổ sung.

Mặc dù proxy http-to-http chủ yếu liên quan đến việc tăng cường tiêu đề http đảm bảo rằng phần backend có thông tin cần thiết để phản hồi lại server proxy thay mặt cho khách hàng, FastCGI là một giao thức riêng biệt không thể đọc tiêu đề http. Do sự cân nhắc này, mọi thông tin thích hợp phải được chuyển đến phần backend thông qua các phương tiện khác.

Phương pháp chính để truyền thông tin bổ sung khi sử dụng giao thức FastCGI là với các tham số. Server nền phải được cấu hình để đọc và xử lý chúng, sửa đổi hành vi của nó tùy thuộc vào những gì nó tìm thấy. Nginx có thể đặt các tham số FastCGI bằng cách sử dụng chỉ thị fastcgi_param .

Cấu hình tối thiểu thực sự sẽ hoạt động trong kịch bản proxy FastCGI cho PHP là thông tin như sau:

# server context  location ~ \.php$ {     fastcgi_param REQUEST_METHOD $request_method;     fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;     fastcgi_pass 127.0.0.1:9000; }  . . .  

Trong cấu hình trên, ta đặt hai tham số FastCGI, được gọi là REQUEST_METHODSCRIPT_FILENAME . Cả hai đều được yêu cầu để server backend hiểu được bản chất của yêu cầu. Cái trước cho nó biết loại hoạt động mà nó sẽ thực hiện, trong khi cái sau nói cho ngược dòng biết file nào sẽ thực thi.

Trong ví dụ, ta đã sử dụng một số biến Nginx để đặt giá trị của các tham số này. Biến $request_method sẽ luôn chứa phương thức http do khách hàng yêu cầu.

Tham số SCRIPT_FILENAME được đặt thành sự kết hợp của biến $document_root và biến $fastcgi_script_name . $document_root sẽ chứa đường dẫn đến folder cơ sở, như được cài đặt bởi chỉ thị root . Biến $fastcgi_script_name sẽ được đặt thành URI yêu cầu. Nếu URI yêu cầu kết thúc bằng dấu gạch chéo (/), giá trị của chỉ thị fastcgi_index sẽ được thêm vào cuối. Loại định nghĩa vị trí tự tham chiếu này có thể thực hiện được vì ta đang chạy bộ xử lý FastCGI trên cùng một máy với version Nginx của ta .

Hãy xem một ví dụ khác:

# server context root /var/www/html;  location /scripts {     fastcgi_param REQUEST_METHOD $request_method;     fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;     fastcgi_index index.php;     fastcgi_pass unix:/var/run/php5-fpm.sock; }  . . .  

Nếu vị trí ở trên được chọn để xử lý một yêu cầu cho /scripts/test/ , giá trị của SCRIPT_FILENAME sẽ là sự kết hợp của các giá trị của chỉ thị root , URI yêu cầu và chỉ thị fastcgi_index . Trong ví dụ này, tham số sẽ được đặt thành /var/www/html/scripts/test/index.php .

Ta đã thực hiện một thay đổi đáng kể khác trong cấu hình ở trên trong đó ta đã chỉ định phần backend FastCGI bằng cách sử dụng socket Unix thay vì socket mạng. Nginx có thể sử dụng một trong hai loại giao diện để kết nối với FastCGI ngược dòng. Nếu bộ xử lý FastCGI sống trên cùng một server , thông thường nên sử dụng socket Unix để bảo mật.

Phá vỡ cấu hình FastCGI

Một luật quan trọng đối với mã có thể bảo trì là cố gắng tuân theo nguyên tắc KHÔ (“Không lặp lại chính bạn”). Điều này giúp giảm lỗi, tăng khả năng tái sử dụng và cho phép tổ chức tốt hơn. Xét rằng một trong những khuyến nghị cốt lõi để quản lý Nginx là luôn đặt ra các chỉ thị ở phạm vi áp dụng rộng nhất của chúng, các mục tiêu cơ bản này cũng áp dụng cho cấu hình Nginx.

Khi xử lý các cấu hình proxy FastCGI, hầu hết các trường hợp sử dụng sẽ chia sẻ phần lớn cấu hình. Vì điều này và do cách thức hoạt động của mô hình kế thừa Nginx, việc khai báo các tham số trong một phạm vi chung luôn luôn có lợi.

Khai báo chi tiết cấu hình FastCGI trong Khung cảnh root

Một cách để giảm sự lặp lại là khai báo chi tiết cấu hình trong ngữ cảnh mẹ cao hơn. Tất cả các tham số bên ngoài fastcgi_pass thực tế có thể được chỉ định ở các cấp cao hơn. Chúng sẽ đổ xuống vị trí xảy ra đèo. Điều này nghĩa là nhiều vị trí có thể sử dụng cùng một cấu hình.

Ví dụ: ta có thể sửa đổi đoạn mã cấu hình cuối cùng từ phần trên để làm cho đoạn mã này hữu ích ở nhiều vị trí:

# server context root /var/www/html;  fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php;  location /scripts {     fastcgi_pass unix:/var/run/php5-fpm.sock; }  location ~ \.php$ {     fastcgi_pass 127.0.0.1:9000; }  . . .  

Trong ví dụ trên, cả hai khai báo fastcgi_param và chỉ thị fastcgi_index đều có sẵn trong cả hai khối vị trí sau đó. Đây là một cách để loại bỏ các khai báo lặp lại.

Tuy nhiên, cấu hình trên có một nhược điểm nghiêm trọng. Nếu bất kỳ fastcgi_param nào được khai báo trong ngữ cảnh thấp hơn, thì không giá trị fastcgi_param từ ngữ cảnh mẹ sẽ được kế thừa. Bạn chỉ sử dụng các giá trị được kế thừa hoặc bạn không sử dụng chúng.

Chỉ thị fastcgi_param là một chỉ thị mảng theo cách nói của Nginx. Từ góc độ user , một chỉ thị mảng về cơ bản là bất kỳ chỉ thị nào được dùng nhiều lần trong một ngữ cảnh duy nhất. Mỗi khai báo tiếp theo sẽ thêm thông tin mới vào những gì Nginx biết từ các khai báo trước. Chỉ thị fastcgi_param được thiết kế như một chỉ thị mảng để cho phép user cài đặt nhiều tham số.

Các chỉ thị mảng kế thừa cho các ngữ cảnh con theo một cách khác với một số chỉ thị khác. Thông tin từ các chỉ thị mảng sẽ chỉ kế thừa đến các ngữ cảnh con nếu chúng không có ở bất kỳ vị trí nào trong ngữ cảnh con . Điều này nghĩa là nếu bạn sử dụng fastcgi_param trong vị trí của bạn , nó sẽ xóa hoàn toàn các giá trị được kế thừa từ ngữ cảnh root .

Ví dụ: ta có thể sửa đổi cấu hình ở trên một chút:

# server context root /var/www/html;  fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_index index.php;  location /scripts {     fastcgi_pass unix:/var/run/php5-fpm.sock; }  location ~ \.php$ {     fastcgi_param QUERY_STRING $query_string;     fastcgi_pass 127.0.0.1:9000; }  . . .  

Thoạt nhìn, bạn có thể nghĩ rằng các thông số REQUEST_METHODSCRIPT_FILENAME sẽ được kế thừa vào khối vị trí thứ hai, với thông số QUERY_STRING cũng có sẵn cho ngữ cảnh cụ thể đó.

Điều thực sự xảy ra là tất cả các giá trị fastcgi_param đều bị xóa sạch trong ngữ cảnh thứ hai và chỉ tham số QUERY_STRING được đặt. Các thông số REQUEST_METHODSCRIPT_FILENAME sẽ vẫn chưa được đặt.

Lưu ý về nhiều giá trị cho các tham số trong cùng ngữ cảnh

Một điều chắc chắn đáng nói ở thời điểm này là tác động của việc đặt nhiều giá trị cho cùng một tham số trong một ngữ cảnh duy nhất. Hãy lấy ví dụ sau làm điểm thảo luận:

# server context  location ~ \.php$ {     fastcgi_param REQUEST_METHOD $request_method;     fastcgi_param SCRIPT_FILENAME $request_uri;      fastcgi_param DOCUMENT_ROOT initial;     fastcgi_param DOCUMENT_ROOT override;      fastcgi_param TEST one;     fastcgi_param TEST two;     fastcgi_param TEST three;      fastcgi_pass 127.0.0.1:9000; }  . . .  

Trong ví dụ trên, ta đã đặt các tham số TESTDOCUMENT_ROOT nhiều lần trong một ngữ cảnh. Vì fastcgi_param là một chỉ thị mảng, mỗi khai báo tiếp theo sẽ được thêm vào các bản ghi nội bộ của Nginx. Tham số TEST sẽ có các khai báo trong mảng đặt nó thành one , twothree .

Điều quan trọng cần nhận ra tại thời điểm này là tất cả những thứ này sẽ được chuyển đến phần backend FastCGI mà không cần xử lý thêm từ Nginx. Điều này nghĩa là hoàn toàn phụ thuộc vào bộ xử lý FastCGI đã chọn để quyết định cách xử lý các giá trị này. Thật không may, các bộ xử lý FastCGI khác nhau xử lý các giá trị được truyền hoàn toàn khác nhau .

Ví dụ: nếu các tham số trên được PHP-FPM nhận, giá trị cuối cùng sẽ được hiểu để overrides lên bất kỳ giá trị nào trước đó. Vì vậy, trong trường hợp này, tham số TEST sẽ được đặt thành three . Tương tự, tham số DOCUMENT_ROOT sẽ được đặt thành override .

Tuy nhiên, nếu giá trị trên được chuyển cho thông tin như FsgiWrap, các giá trị được hiểu rất khác nhau. Đầu tiên, nó thực hiện một đường chuyền ban đầu để quyết định giá trị nào sẽ sử dụng để chạy tập lệnh. Nó sẽ sử dụng giá trị DOCUMENT_ROOT của initial để tìm kiếm tập lệnh. Tuy nhiên, khi nó chuyển các tham số thực cho script, nó sẽ chuyển các giá trị cuối cùng, giống như PHP-FPM.

Sự không nhất quán và không thể đoán trước này nghĩa là bạn không thể và không nên dựa vào chương trình backend để diễn giải chính xác ý định của bạn khi đặt cùng một tham số nhiều lần. Giải pháp an toàn duy nhất là chỉ khai báo mỗi tham số một lần. Điều này cũng nghĩa là không có cái gọi là overrides một cách an toàn giá trị mặc định bằng chỉ thị fastcgi_param .

Sử dụng Bao gồm để Nguồn Cấu hình FastCGI từ một file riêng biệt

Có một cách khác để tách các mục cấu hình chung của bạn. Ta có thể sử dụng include chỉ thị để đọc trong các nội dung của một file riêng biệt đến vị trí của việc kê khai chỉ thị.

Điều này nghĩa là ta có thể giữ tất cả các mục cấu hình chung của ta trong một file duy nhất và đưa nó vào bất kỳ đâu trong cấu hình của ta khi ta cần. Vì Nginx sẽ đặt nội dung file thực tại nơi include được gọi, ta sẽ không kế thừa từ ngữ cảnh mẹ sang ngữ cảnh con. Điều này sẽ ngăn các giá trị fastcgi_param bị xóa sạch, cho phép ta cài đặt các tham số bổ sung nếu cần.

Đầu tiên, ta có thể đặt các giá trị cấu hình FastCGI chung của bạn trong một file riêng trong folder cấu hình của ta . Ta sẽ gọi file này là fastcgi_common :

fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 

Bây giờ, ta có thể đọc trong file này ở bất kỳ đâu ta muốn sử dụng các giá trị cấu hình đó:

# server context root /var/www/html;  location /scripts {     include fastcgi_common;      fastcgi_index index.php;     fastcgi_pass unix:/var/run/php5-fpm.sock; }  location ~ \.php$ {     include fastcgi_common;     fastcgi_param QUERY_STRING $query_string;     fastcgi_param CONTENT_TYPE $content_type;     fastcgi_param CONTENT_LENGTH $content_length;      fastcgi_index index.php;     fastcgi_pass 127.0.0.1:9000; }  . . .  

Ở đây, ta đã chuyển một số giá trị fastcgi_param phổ biến vào một file có tên fastcgi_common trong folder cấu hình Nginx mặc định của ta . Sau đó, ta mã nguồn file đó khi ta muốn chèn các giá trị được khai báo bên trong.

Có một số điều cần lưu ý về cấu hình này.

Điều đầu tiên là ta đã không đặt bất kỳ giá trị nào mà ta có thể cần tùy chỉnh trên cơ sở từng vị trí trong file mà ta định nguồn. Do vấn đề với diễn giải mà ta đã đề cập ở trên xảy ra khi đặt nhiều giá trị cho cùng một tham số và vì chỉ thị không phải mảng chỉ có thể được đặt một lần cho mỗi ngữ cảnh, chỉ đặt các mục file chung mà bạn sẽ không muốn thay đổi. Mọi chỉ thị (hoặc khóa tham số) mà ta có thể cần tùy chỉnh trên cơ sở từng ngữ cảnh nên được bỏ ra khỏi file chung.

Một điều khác mà bạn có thể nhận thấy là ta đặt một số tham số FastCGI bổ sung trong khối vị trí thứ hai. Đây là khả năng mà ta hy vọng đạt được. Ta có thể cài đặt các tham số fastcgi_param bổ sung nếu cần mà không xóa các giá trị chung.

Sử dụng Tệp fastcgi_params hoặc Tệp fastcgi.conf

Với chiến lược trên, các nhà phát triển Nginx và nhiều group đóng gói phân phối đã làm việc để cung cấp một tập hợp các thông số chung lành mạnh mà bạn có thể đưa vào các vị trí vượt qua FastCGI của bạn . Chúng được gọi là fastcgi_params hoặc fastcgi.conf .

Hai file này phần lớn giống nhau, với sự khác biệt duy nhất thực sự là hệ quả của vấn đề ta đã thảo luận trước đó về việc chuyển nhiều giá trị cho một tham số duy nhất. Tệp fastcgi_params không chứa khai báo cho tham số SCRIPT_FILENAME , trong khi file fastcgi.conf thì có.

Tệp fastcgi_params đã tồn tại trong một khoảng thời gian dài hơn. Để tránh phá vỡ cấu hình dựa trên fastcgi_params , khi đưa ra quyết định cung cấp giá trị mặc định cho SCRIPT_FILENAME , một file mới cần được tạo. Không làm như vậy có thể dẫn đến việc tham số đó được đặt trong cả file chung và vị trí truyền FastCGI. Điều này được mô tả rất chi tiết trong bài đăng xuất sắc của Martin Fjordvald về lịch sử của hai file này .

Nhiều người bảo trì gói cho các bản phân phối phổ biến đã chọn chỉ bao gồm một trong những file này hoặc sao chép chính xác nội dung của chúng. Nếu bạn chỉ có một trong những cái này, hãy sử dụng cái bạn có. Vui lòng sửa đổi nó cho phù hợp với nhu cầu của bạn.

Nếu bạn có cả hai file này, đối với hầu hết các vị trí truyền FastCGI, có lẽ tốt hơn nên bao gồm file fastcgi.conf , vì file này bao gồm khai báo cho tham số SCRIPT_FILENAME . Điều này thường là mong muốn, nhưng có một số trường hợp bạn có thể cần tùy chỉnh giá trị này.

Chúng có thể được đưa vào bằng cách tham chiếu vị trí của chúng liên quan đến folder cấu hình Nginx root . Thư mục cấu hình Nginx root thường giống như /etc/nginx khi Nginx đã được cài đặt với trình quản lý gói.

Bạn có thể bao gồm các file như sau:

# server context  location ~ \.php$ {     include fastcgi_params;     # You would use "fastcgi_param SCRIPT_FILENAME . . ." here afterwards      . . .  } 

Hoặc như thế này:

# server context  location ~ \.php$ {     include fastcgi.conf;      . . .  } 

Các Chỉ thị, Tham số và Biến FastCGI quan trọng

Trong các phần trên, ta đã đặt một số lượng hợp lý các tham số, thường cho các biến Nginx, như một phương tiện để chứng minh các khái niệm khác. Ta cũng đã giới thiệu một số chỉ thị FastCGI mà không cần giải thích quá nhiều. Trong phần này, ta sẽ thảo luận về một số chỉ thị phổ biến để đặt, các tham số mà bạn có thể cần sửa đổi và một số biến có thể chứa thông tin bạn cần.

Các chỉ thị FastCGI phổ biến

Phần sau đại diện cho một số chỉ thị hữu ích nhất để làm việc với các đường chuyền FastCGI:

  • fastcgi_pass : Chỉ thị thực tế chuyển các yêu cầu trong ngữ cảnh hiện tại đến phần backend . Điều này xác định vị trí mà bộ xử lý FastCGI có thể được tiếp cận.
  • fastcgi_param : Chỉ thị mảng được dùng để đặt các tham số thành giá trị. Thông thường, điều này được sử dụng cùng với các biến Nginx để đặt các tham số FastCGI thành các giá trị cụ thể cho yêu cầu.
  • try_files : Không phải là chỉ thị dành riêng cho FastCGI mà là chỉ thị chung được sử dụng trong các vị trí vượt qua FastCGI. Điều này thường được sử dụng như một phần của quy trình vệ sinh yêu cầu đảm bảo rằng file được yêu cầu tồn tại trước khi chuyển nó đến bộ xử lý FastCGI.
  • include : , không phải là chỉ thị dành riêng cho FastCGI mà là chỉ thị được sử dụng nhiều trong các ngữ cảnh vượt qua FastCGI. Thông thường, điều này được sử dụng để bao gồm các chi tiết cấu hình chung, được chia sẻ ở nhiều vị trí.
  • fastcgi_split_path_info : Chỉ thị này xác định một biểu thức chính quy với hai group được bắt. Group được bắt đầu tiên được sử dụng làm giá trị cho biến $fastcgi_script_name . Group được bắt thứ hai được sử dụng làm giá trị cho biến $fastcgi_path_info . Cả hai phần này thường được sử dụng để phân tích cú pháp chính xác yêu cầu để bộ xử lý biết phần nào của yêu cầu là file để chạy và phần nào là thông tin bổ sung để chuyển đến tập lệnh.
  • fastcgi_index : Điều này xác định file index sẽ được thêm vào các giá trị $fastcgi_script_name kết thúc bằng dấu gạch chéo ( / ). Điều này thường hữu ích nếu tham số SCRIPT_FILENAME được đặt thành $document_root$fastcgi_script_name và khối vị trí được cấu hình để chấp nhận các yêu cầu có thông tin sau file .
  • fastcgi_intercept_errors : Chỉ thị này xác định xem các lỗi nhận được từ server FastCGI nên được xử lý bởi Nginx hay được chuyển trực tiếp đến client .

Các hướng dẫn trên đại diện cho hầu hết những gì bạn sẽ sử dụng khi thiết kế một thẻ FastCGI điển hình. Bạn có thể không sử dụng tất cả những thứ này mọi lúc, nhưng ta có thể bắt đầu thấy rằng chúng tương tác khá mật thiết với các tham số và biến FastCGI mà ta sẽ đề cập tiếp theo.

Các biến phổ biến được sử dụng với FastCGI

Trước khi ta có thể nói về các tham số mà bạn có thể sử dụng với các đường chuyền FastCGI, ta nên nói một chút về một số biến Nginx phổ biến mà ta sẽ tận dụng trong việc cài đặt các tham số đó. Một số trong số này được xác định bởi module FastCGI của Nginx, nhưng hầu hết là từ module Core.

  • $query_string hoặc $args : Các đối số được đưa ra trong yêu cầu khách hàng ban đầu.
  • $is_args : Sẽ bằng “?” nếu có đối số trong yêu cầu và sẽ được đặt thành một chuỗi trống.Điều này hữu ích khi xây dựng các tham số có thể có hoặc không có đối số.
  • $request_method : Điều này chỉ ra phương thức yêu cầu ban đầu của khách hàng. Điều này có thể hữu ích trong việc xác định xem một hoạt động có nên được phép trong bối cảnh hiện tại hay không.
  • $content_type : Điều này được đặt thành tiêu đề yêu cầu Content-Type . Thông tin này được proxy cần nếu yêu cầu của user là POST để xử lý chính xác nội dung tiếp theo.
  • $content_length : Giá trị này được đặt thành giá trị của tiêu đề Content-Length từ client . Thông tin này là bắt buộc đối với bất kỳ yêu cầu POST nào của khách hàng.
  • $fastcgi_script_name : Phần này sẽ chứa file script sẽ được chạy. Nếu yêu cầu kết thúc bằng dấu gạch chéo (/), giá trị của chỉ thị fastcgi_index sẽ được thêm vào cuối. Trong trường hợp chỉ thị fastcgi_split_path_info được sử dụng, biến này sẽ được đặt thành group được bắt đầu tiên được xác định bởi chỉ thị đó. Giá trị của biến này phải cho biết tập lệnh thực sự sẽ được chạy.
  • $request_filename : Biến này sẽ chứa đường dẫn file cho file được yêu cầu. Nó nhận giá trị này bằng cách lấy giá trị của root tài liệu hiện tại, tính đến cả chỉ thị rootalias , và giá trị của $fastcgi_script_name . Đây là một cách rất linh hoạt để gán tham số SCRIPT_FILENAME .
  • $request_uri : Toàn bộ yêu cầu khi nhận được từ khách hàng. Điều này bao gồm tập lệnh, bất kỳ thông tin đường dẫn bổ sung nào, cùng với bất kỳ chuỗi truy vấn nào.
  • $fastcgi_path_info : Biến này chứa thông tin đường dẫn bổ sung có thể có sau tên tập lệnh trong yêu cầu. Giá trị này đôi khi chứa một vị trí khác mà tập lệnh cần thực thi phải biết. Biến này nhận giá trị của nó từ group regex được bắt thứ hai khi sử dụng chỉ thị fastcgi_split_path_info .
  • $document_root : Biến này chứa giá trị root của tài liệu hiện tại. Điều này sẽ được đặt theo chỉ thị root hoặc alias .
  • $uri : Biến này chứa URI hiện tại có áp dụng chuẩn hóa. Vì một số lệnh viết lại hoặc chuyển hướng nội bộ có thể có tác động đến URI, nên biến này sẽ thể hiện những thay đổi đó.

Như bạn thấy , có khá nhiều biến có sẵn cho bạn khi quyết định cách cài đặt các tham số FastCGI. Nhiều trong số này tương tự nhau, nhưng có một số khác biệt nhỏ sẽ ảnh hưởng đến việc thực thi các tập lệnh của bạn.

Thông số FastCGI phổ biến

Các tham số FastCGI đại diện cho thông tin key-value mà ta muốn cung cấp cho bộ xử lý FastCGI mà ta đang gửi yêu cầu. Không phải mọi ứng dụng đều cần các thông số giống nhau, vì vậy bạn thường cần tham khảo tài liệu của ứng dụng.

Một số tham số này là cần thiết để bộ xử lý xác định chính xác tập lệnh để chạy. Những phần khác được tạo sẵn cho tập lệnh, có thể sửa đổi hành vi của nó nếu nó được cấu hình dựa trên các tham số đã đặt.

  • QUERY_STRING : Tham số này phải được đặt thành bất kỳ chuỗi truy vấn nào do khách hàng cung cấp. Đây thường sẽ là các cặp key-value được cung cấp sau dấu “?” trong URI. Thông thường, tham số này được đặt thành các biến $query_string hoặc $args , cả hai đều phải chứa cùng một dữ liệu.
  • REQUEST_METHOD : Tham số này cho bộ xử lý FastCGI biết loại hành động nào được khách hàng yêu cầu. Đây là một trong số ít các tham số bắt buộc phải được đặt để pass hoạt động chính xác.
  • CONTENT_TYPE : Nếu phương thức yêu cầu được đặt ở trên là “POST”, thì thông số này phải được đặt. Nó chỉ ra loại nội dung mà bộ xử lý FastCGI nên mong đợi. Điều này hầu như luôn chỉ được đặt thành biến $content_type , được đặt theo thông tin trong yêu cầu ban đầu.
  • CONTENT_LENGTH : Nếu phương thức yêu cầu là "POST", thì thông số này phải được đặt. Điều này cho biết độ dài nội dung. Điều này hầu như luôn chỉ được đặt thành $content_length , một biến nhận giá trị từ thông tin trong yêu cầu khách hàng ban đầu.
  • SCRIPT_NAME : Tham số này được sử dụng để chỉ ra tên của tập lệnh chính sẽ được chạy. Đây là một thông số cực kỳ quan trọng có thể được cài đặt theo nhiều cách khác nhau tùy theo nhu cầu của bạn. Thông thường, giá trị này được đặt thành $fastcgi_script_name , đây sẽ là URI yêu cầu, URI yêu cầu với fastcgi_index thêm vào nếu nó kết thúc bằng dấu gạch chéo hoặc group được bắt đầu tiên nếu sử dụng fastcgi_fix_path_info .
  • SCRIPT_FILENAME : Tham số này chỉ định vị trí thực tế trên đĩa của script để chạy. Do liên quan đến tham số SCRIPT_NAME , một số hướng dẫn khuyên bạn nên sử dụng $document_root$fastcgi_script_name . Một giải pháp thay thế khác có nhiều ưu điểm là sử dụng $request_filename .
  • REQUEST_URI : Phần này phải chứa URI yêu cầu đầy đủ, chưa sửa đổi, hoàn chỉnh với tập lệnh để chạy, thông tin đường dẫn bổ sung và bất kỳ đối số nào. Một số ứng dụng thích tự phân tích thông tin này. Tham số này cung cấp cho họ thông tin cần thiết để làm điều đó.
  • PATH_INFO : Nếu cgi.fix_pathinfo được đặt thành “1” trong file cấu hình PHP, file này sẽ chứa bất kỳ thông tin đường dẫn bổ sung nào được thêm vào sau tên tập lệnh. Điều này thường được sử dụng để xác định một đối số file mà tập lệnh sẽ hoạt động. Đặt cgi.fix_pathinfo thành “1” có thể có tác động bảo mật nếu các yêu cầu tập lệnh không được làm sạch thông qua các phương tiện khác ( ta sẽ thảo luận điều này sau). Đôi khi điều này được đặt thành biến $fastcgi_path_info , biến này chứa group thứ hai được bắt từ chỉ thị fastcgi_split_path_info . Những trường hợp khác, một biến tạm thời cần được sử dụng vì giá trị đó đôi khi bị che bởi quá trình xử lý khác.
  • PATH_TRANSLATED : Tham số này ánh xạ thông tin đường dẫn có trong PATH_INFO thành một đường dẫn hệ thống file thực tế. Thông thường, điều này sẽ được đặt thành thông tin như $document_root$fastcgi_path_info , nhưng đôi khi biến sau này phải được thay thế bằng biến được lưu tạm thời như đã chỉ ra ở trên.

Kiểm tra yêu cầu trước khi chuyển đến FastCGI

Một chủ đề rất cần thiết mà ta chưa đề cập là cách chuyển các yêu cầu động đến server ứng dụng của bạn một cách an toàn. Việc chuyển tất cả các yêu cầu tới ứng dụng backend , dù tính hợp lệ của chúng, không chỉ không hiệu quả mà còn nguy hiểm. Những kẻ tấn công có thể tạo ra các yêu cầu độc hại để cố gắng khiến server của bạn chạy mã tùy ý.

Để giải quyết vấn đề này, ta nên đảm bảo ta chỉ gửi các yêu cầu hợp lệ đến bộ xử lý FastCGI của ta . Ta có thể thực hiện việc này theo nhiều cách khác nhau tùy thuộc vào nhu cầu của cài đặt cụ thể của ta và liệu bộ xử lý FastCGI có sống trên cùng một hệ thống với version Nginx của ta hay không.

Một luật cơ bản sẽ thông báo cách ta thiết kế cấu hình của bạn là ta không bao giờ được phép xử lý và giải thích các file user . Tương đối dễ dàng cho những user độc hại nhúng mã hợp lệ vào các file có vẻ vô tội, chẳng hạn như hình ảnh. Sau khi một file như thế này được tải lên server của ta , ta phải đảm bảo nó không bao giờ đến được bộ xử lý FastCGI của ta .

Vấn đề chính mà ta đang cố gắng giải quyết ở đây là một vấn đề thực sự được chỉ rõ trong đặc tả CGI. Thông số kỹ thuật cho phép bạn chỉ định một file kịch bản để chạy, theo sau là thông tin đường dẫn bổ sung được dùng bởi tập lệnh. Mô hình thực thi này cho phép user yêu cầu một URI có thể trông giống như một tập lệnh hợp lệ , trong khi phần thực tế sẽ được thực thi sẽ sớm hơn trong đường dẫn.

Xem xét yêu cầu cho /test.jpg/index.php . Nếu cấu hình của bạn chỉ chuyển mọi yêu cầu kết thúc bằng .php đến bộ xử lý của bạn mà không kiểm tra tính hợp lệ của nó, bộ xử lý, nếu tuân theo thông số kỹ thuật, sẽ kiểm tra vị trí đó và thực thi nó nếu có thể. Nếu nó không tìm thấy file , sau đó nó sẽ làm theo thông số kỹ thuật và cố gắng thực thi file /test.jpg , đánh dấu /index.php là thông tin đường dẫn bổ sung cho tập lệnh. Như bạn thấy , điều này có thể dẫn đến một số hậu quả rất không mong muốn khi kết hợp với ý tưởng tải lên của user .

Có một số cách khác nhau để giải quyết vấn đề này. Cách dễ nhất, nếu ứng dụng của bạn không dựa vào thông tin đường dẫn bổ sung này để xử lý, chỉ cần tắt nó trong bộ xử lý của bạn. Đối với PHP-FPM, bạn có thể tắt tính năng này trong file php.ini của bạn . Ví dụ: trên hệ thống Ubuntu, bạn có thể chỉnh sửa file này:

sudo nano /etc/php5/fpm/php.ini 

Chỉ cần tìm kiếm tùy chọn cgi.fix_pathinfo , bỏ ghi chú và đặt nó thành “0” để tắt “tính năng” này:

cgi.fix_pathinfo=0 

Khởi động lại quy trình PHP-FPM của bạn để thực hiện thay đổi:

sudo service php5-fpm restart 

Điều này sẽ khiến PHP chỉ cố gắng thực thi trên thành phần cuối cùng của một đường dẫn. Vì vậy, trong ví dụ của ta ở trên, nếu file /test.jpg/index.php không tồn tại, PHP sẽ chính xác lỗi thay vì cố gắng thực thi /test.jpg .

Một tùy chọn khác, nếu bộ xử lý FastCGI của ta nằm trên cùng một máy với version Nginx của ta , là chỉ cần kiểm tra sự tồn tại của các file trên đĩa trước khi chuyển chúng đến bộ xử lý. Nếu file /test.jgp/index.php không tồn tại, thì lỗi. Nếu đúng, hãy gửi nó đến phần backend để xử lý. Trên thực tế, điều này sẽ dẫn đến nhiều hành vi giống như ta đã làm ở trên:

location ~ \.php$ {         try_files $uri =404;          . . .  } 

Nếu ứng dụng của bạn dựa trên con đường thông tin hành vi để giải thích đúng, bạn vẫn có thể an toàn cho phép hành vi này bằng cách thực hiện kiểm tra trước khi quyết định xem có nên gửi yêu cầu đến backend.

Ví dụ: ta có thể đối sánh cụ thể các folder mà ta cho phép tải lên không tin cậy và đảm bảo chúng không được chuyển cho bộ xử lý của ta . Ví dụ: nếu folder tải lên của ứng dụng của ta là /uploads/ , ta có thể tạo một khối vị trí như thế này phù hợp trước khi bất kỳ biểu thức chính quy nào được đánh giá:

location ^~ /uploads { } 

Bên trong, ta có thể vô hiệu hóa bất kỳ loại xử lý nào đối với các file PHP:

location ^~ /uploads {     location ~* \.php$ { return 403; } } 

Vị trí chính sẽ phù hợp với bất kỳ yêu cầu nào bắt đầu bằng /uploads và bất kỳ yêu cầu nào xử lý file PHP sẽ trả về lỗi 403 thay vì gửi nó đến chương trình backend .

Bạn cũng có thể sử dụng chỉ thị fastcgi_split_path_info để xác định thủ công phần yêu cầu sẽ được hiểu là tập lệnh và phần sẽ được xác định là thông tin đường dẫn bổ sung bằng cách sử dụng biểu thức chính quy. Điều này cho phép bạn vẫn dựa vào chức năng thông tin đường dẫn, nhưng xác định chính xác những gì bạn coi là tập lệnh và những gì bạn coi là đường dẫn.

Ví dụ: ta có thể cài đặt một khối vị trí coi version đầu tiên của thành phần đường dẫn kết thúc bằng .php là tập lệnh để chạy. Phần còn lại sẽ được coi là thông tin đường dẫn phụ. Điều này nghĩa là trong trường hợp yêu cầu /test.jpg/index.php , toàn bộ đường dẫn có thể được gửi đến bộ xử lý dưới dạng tên tập lệnh mà không có thông tin đường dẫn bổ sung.

Vị trí này có thể trông giống như sau:

location ~ [^/]\.php(/|$) {      fastcgi_split_path_info ^(.+?\.php)(.*)$;     set $orig_path $fastcgi_path_info;      try_files $fastcgi_script_name =404;      fastcgi_pass unix:/var/run/php5-fpm.sock;     fastcgi_index index.php;     include fastcgi_params;      fastcgi_param SCRIPT_FILENAME $request_filename;     fastcgi_param PATH_INFO $orig_path;     fastcgi_param PATH_TRANSLATED $document_root$orig_path; } 

Khối ở trên sẽ hoạt động cho các cấu hình PHP trong đó cgi.fix_pathinfo được đặt thành “1” để cho phép thêm thông tin đường dẫn. Ở đây, khối vị trí của ta không chỉ trùng với các yêu cầu kết thúc bằng .php mà còn đối sánh với các yêu cầu có .php ngay trước dấu gạch chéo (/) cho biết một thành phần folder bổ sung theo sau.

Bên trong khối, chỉ thị fastcgi_split_path_info xác định hai group được bắt bằng các biểu thức chính quy. Group đầu tiên trùng với phần của URI từ phần đầu đến phần đầu tiên của .php và đặt phần đó vào biến $fastcgi_script_name . Sau đó, nó đặt bất kỳ thông tin nào từ thời điểm đó trở đi vào một group đã thu $fastcgi_path_info thứ hai, group này sẽ lưu trữ trong một biến có tên là $fastcgi_path_info .

Ta sử dụng lệnh set để lưu trữ giá trị được giữ trong $fastcgi_path_info tại thời điểm này vào một biến có tên là $orig_path . Điều này là do biến $fastcgi_path_info sẽ bị xóa trong giây lát bởi try_files của ta .

Ta kiểm tra tên tập lệnh mà ta đã nắm bắt ở trên bằng cách sử dụng try_files . Đây là thao tác với file sẽ đảm bảo tập lệnh mà ta đang cố gắng chạy nằm trên đĩa. Tuy nhiên, điều này cũng có một tác dụng phụ là xóa biến $fastcgi_path_info .

Sau khi thực hiện vượt qua FastCGI thông thường, ta đặt SCRIPT_FILENAME như bình thường. Ta cũng đặt PATH_INFO thành giá trị mà ta đã giảm tải vào biến $orig_path . Mặc dù $fastcgi_path_info của ta đã bị xóa, giá trị ban đầu của nó vẫn được giữ lại trong biến này. Ta cũng đặt tham số PATH_TRANSLATED để ánh xạ thông tin đường dẫn bổ sung đến vị trí mà thông số tồn tại trên đĩa. Ta thực hiện việc này bằng cách kết hợp biến $document_root với biến $orig_path .

Điều này cho phép ta tạo các yêu cầu như /index.php/users/view để file /index.php của ta có thể xử lý thông tin về folder /users/view , đồng thời tránh các trường hợp trong đó /test.jpg/index.php sẽ được chạy. Nó sẽ luôn đặt tập lệnh thành thành phần ngắn nhất kết thúc bằng .php , do đó tránh được vấn đề này.

Ta thậm chí có thể làm cho điều này hoạt động với chỉ thị alias nếu ta cần thay đổi vị trí của các file kịch bản của bạn . Ta chỉ cần tính đến vấn đề này trong cả tiêu đề vị trí và định nghĩa fastcgi_split_path_info :

location ~ /test/.+[^/]\.php(/|$) {      alias /var/www/html;      fastcgi_split_path_info ^/test(.+?\.php)(.*)$;     set $orig_path $fastcgi_path_info;      try_files $fastcgi_script_name =404;      fastcgi_pass unix:/var/run/php5-fpm.sock;     fastcgi_index index.php;     include fastcgi_params;      fastcgi_param SCRIPT_FILENAME $request_filename;     fastcgi_param PATH_INFO $orig_path;     fastcgi_param PATH_TRANSLATED $document_root$orig_path; } 

Những điều này sẽ cho phép bạn chạy các ứng dụng sử dụng tham số PATH_INFO một cách an toàn. Lưu ý , bạn sẽ phải thay đổi tùy chọn cgi.fix_pathinfo trong file php.ini của bạn thành “1” để làm cho tùy chọn này hoạt động chính xác. Bạn cũng có thể phải tắt security.limit_extensions trong file php-fpm.conf .

Kết luận

Hy vọng rằng bây giờ bạn đã hiểu rõ hơn về khả năng proxy FastCGI của Nginx. Khả năng này cho phép Nginx thực hiện thế mạnh của bạn trong việc xử lý kết nối nhanh và cung cấp nội dung tĩnh, đồng thời giảm bớt trách nhiệm về nội dung động cho phần mềm phù hợp hơn. FastCGI cho phép Nginx hoạt động với một số lượng lớn các ứng dụng, trong các cấu hình hoạt động hiệu quả và an toàn.


Tags:

Các tin liên quan

Hiểu về Nginx HTTP Proxying, Cân bằng tải, Bộ đệm và Bộ nhớ đệm
2014-11-25
Hiểu cấu trúc tệp cấu hình và khung cấu hình Nginx
2014-11-19
Cách cài đặt MoinMoin với Nginx trên Ubuntu 14.04
2014-11-19
Hiểu server Nginx và các thuật toán lựa chọn khối vị trí
2014-11-17
Cách thiết lập server block Nginx trên CentOS 7
2014-11-05
Cách sử dụng tệp bản đồ Salt Cloud để triển khai server ứng dụng và reverse-proxy Nginx
2014-10-27
Cách triển khai ứng dụng Rails với Passenger và Nginx trên Ubuntu 14.04
2014-10-09
Cách tạo profile AppArmor cho Nginx trên Ubuntu 14.04
2014-10-06
Cách cấu hình Nginx với SSL làm Reverse Proxy cho Jenkins
2014-09-23
Cách thiết lập nhiều trang web WordPress với Nginx trên Ubuntu 14.04
2014-08-21