Cài Nginx làm reverse proxy cho Apache

Giới thiệu

Apache và Nginx là hai máy chủ web mã nguồn mở phổ biến thường được sử dụng với PHP. Có thể hữu ích khi chạy cả hai trên cùng một máy ảo khi lưu trữ nhiều trang web có các yêu cầu khác nhau. Giải pháp chung để chạy hai máy chủ web trên một hệ thống là sử dụng nhiều địa chỉ IP hoặc số cổng khác nhau.

Trong hướng dẫn này, mình sẽ cấu hình Nginx vừa là máy chủ web vừa là Reverse Proxy cho Apache - tất cả trên một máy chủ duy nhất. Đồng thời cũng giới thiệu với các bạn về VirtualHost để cấu hình lưu trữ được nhiều website với nhiều domain khác nhau trên host của bạn.

Tùy thuộc vào ứng dụng web, các thay đổi mã có thể được yêu cầu để giữ cho Apache nhận biết được là Reverse Proxy hay không, đặc biệt khi các trang web được cấu hình SSL. Để tránh điều này, bạn sẽ cài đặt một module Apache được gọi là module mod_rpaf ghi lại các biến môi trường nhất định để có vẻ như Apache đang trực tiếp xử lý các yêu cầu từ các máy khách web.

Trong các bài trước "Hướng dẫn cài đặt LAMP" và "Hướng dẫn cài đặt Nginx" thì mình đã cấu hình như sau. Apache chạy ở cổng 8888 và Nginx chạy ở cổng 80.
Theo cài đặt đó, khi bạn truy cập vào trang http://your_server_IP/ thì bạn sẽ ra trang index của Nginx

còn khi vào http://your_server_IP:8888/ thì bạn mới có thể ra trang index của Apache

Như vậy, giả sử bạn cài wordpress vào thư mục /var/www/html trên Apache thì bạn muốn truy cập sẽ phải qua địa chỉ http://your_server_IP:8888/wordpress → rất là khó nhớ, mà người dùng khi truy cập vào http://your_server_IP/wordpress thì lại báo không tồn tại website đó.
Do đó, chúng ta sẽ cái Nginx làm Reverse Proxy để khi người dùng vào địa chỉ http://your_server_IP/wordpress sẽ vào đúng trang wordpress (đang chạy trên nền Apache) của chúng ta.

Ở ví dụ này, mình sẽ lưu trữ bốn tên miền trên một máy chủ. Hai sẽ được cung cấp bởi Nginx: (máy chủ ảo mặc định) và hai còn lại, sẽ được Apache phục vụ. Chúng tôi cũng sẽ định cấu hình Apache để phục vụ các ứng dụng PHP bằng PHP-FPM, mang lại hiệu suất tốt hơn.

Yêu cầu

  • Ubuntu Server
  • Apache, Nginx đã cài đặt
  • Firewall đã mở port 80, 443, 8888

Tiến hành cài đặt

Bước 1:

  • Cài đặt php-fpm
sudo apt install php-fpm

Cài đặt module FastCGI, mặc định không có trên repo của Ubuntu, tiến hành download từ kernel.org và cài đặt bằng lệnh dpkg

wget https://mirrors.edge.kernel.org/ubuntu/pool/multiverse/liba/libapache-mod-fastcgi/libapache2-mod-fastcgi_2.4.7~0910052141-1.2_amd64.deb
sudo dpkg -i libapache2-mod-fastcgi_2.4.7~0910052141-1.2_amd64.deb

Bước 2: Xác nhận lại cổng của Apache

Khi cài đặt Apache, chúng ta đã đổi để Apache hoạt động trên cổng 8888, để kiểm tra lại chúng ta cài đặt netstat

sudo apt install net-tools
#xác nhận lại
netstat -tlpn

Nếu hiển thị như hình là ok.

Bước 3

Cấu hình mặc định Apache để hoạt động với PHP-FPM bằng module mod_fastcgi.Apache phục vụ các trang PHP mặc định sử dụng mod_php, chúng ta phải cấu hình bổ sung để hoạt động với PHP-FPM.

Lưu ý: Nếu bạn đang thử hướng dẫn này về cài đặt hiện có của LAMP với mod_php, hãy tắt nó trước với sudo a2dismod php7.4. Nếu không tắt có thể sẽ xảy ra lỗi này
luffy@luffy:/etc/apache2$ sudo apachectl -t
AH00526: Syntax error on line 5 of /etc/apache2/mods-enabled/fastcgi.conf:
Invalid command 'Action', perhaps misspelled or defined by a module not included in the server configuration
Action '-t' failed.
The Apache error log may have more information

Bật module action

sudo a2enmod actions

Đổi tên tệp cấu hình mod_fastcgi hiện có

sudo mv /etc/apache2/mods-enabled/fastcgi.conf /etc/apache2/mods-enabled/fastcgi.conf.default

Tạo tệp cấu hình mới

sudo nano /etc/apache2/mods-available/fastcgi.conf

và nhập vào nội dung sau

<IfModule mod_fastcgi.c>
  AddHandler fastcgi-script .fcgi
  FastCgiIpcDir /var/lib/apache2/fastcgi
  AddType application/x-httpd-fastphp .php
  Action application/x-httpd-fastphp /php-fcgi
  Alias /php-fcgi /usr/lib/cgi-bin/php-fcgi
  FastCgiExternalServer /usr/lib/cgi-bin/php-fcgi -socket /run/php/php7.4-fpm.sock -pass-header Authorization
  <Directory /usr/lib/cgi-bin>
    Require all granted
  </Directory>
</IfModule>

Tạo ánh xạ đến thư mục gốc

sudo ln -s /etc/apache2/mods-available/fastcgi.conf /etc/apache2/mods-enabled/fastcgi.conf

Lưu các thay đổi và kiểm tra, không phát sinh lỗi khởi động lại Apache

sudo apache2 -t
#không lỗi khởi động lại Apache
sudo systemctl reload apache2

Bước 4: Xác nhận lại chức năng PHP

tạo file info.php trong thư mục /var/www/html ban đầu của Apache

sudo nano /var/www/html/info.php
# nhập vào nội dung sau
<?php
phpinfo();
?>

Lưu file và kiểm tra trên trình duyệt http://your_server_IP:8888/info.php
Kiểm tra "Server API" có giá trị "FPM/FastCGI" và "SERVER_SOFTWARE" là "Apache/2.4.41 (Ubuntu)", vậy là Apache đã hoạt động với PHP-FPM bằng module mod_fastcgi.

Bước 5: Tạo VirtualHost cho Apache

Ở ví dụ này, mình sẽ tạo 2 website là server1 và server2. Tạo thư mục chứa nội dung của 2 website trên

sudo mkdir -v /var/www/server1 /var/www/server2

Tạo file index.html

echo "<h1 style='color: green;'>Server 1</h1>" | sudo tee /var/www/server1/index.html
echo "<h1 style='color: red;'>Server 2</h1>" | sudo tee /var/www/server2/index.html

Tạo file info.php

echo "<?php phpinfo(); ?>" | sudo tee /var/www/server1/info.php
echo "<?php phpinfo(); ?>" | sudo tee /var/www/server2/info.php

Tạo VirtualHost file cho website server1

sudo nano /etc/apache2/sites-available/server1.conf

và chèn vào nội dung sau, chú ý: nhớ cổng sử dụng là 8888

<VirtualHost *:8888>
	ServerName server1.local
	ServerAlias www.server1.local
	DocumentRoot /var/www/server1
	<Directory /var/www/server1>
		AllowOverride All
	</Directory>
</VirtualHost>

Dòng "AllowOverride All" để hỗ trợ sử dụng .htaccess
Lưu và đóng tập tin. Sau đó, tạo một cấu hình tương tự cho server2

sudo nano /etc/apache2/sites-available/server2.conf

chèn nội dung

<VirtualHost *:8888>
	ServerName server2.local
	ServerAlias www.server2.local
	DocumentRoot /var/www/server2
	<Directory /var/www/server2>
		AllowOverride All
	</Directory>
</VirtualHost>

Bây giờ cả hai máy chủ ảo Apache đã được thiết lập, hãy kích hoạt các trang web bằng cách sử dụng a2ensite. Điều này tạo ra một liên kết tượng trưng đến VirtualHost trong thư mục sites-enabled:

sudo a2ensite server1
sudo a2ensite server2

Kiểm tra lại cấu hình, không có lỗi xảy ra khởi động lại

sudo apachectl -t
sudo systemctl restart apache2

Để xác nhận các trang web đang hoạt động, hãy mở và trong trình duyệt của bạn và xác minh rằng mỗi trang web hiển thị tệp của nó.

Chú ý: Do domain này chỉ là domain trên local của bạn nên nếu truy cấp vào liên kết http://server1.local:8888 hay http://server2.local:8888 thì sẽ không truy cập được. Bạn cần chỉnh sửa file host của máy (hiện đang dùng để ssh vào server).
Ví dụ, máy Window sửa file host ở (C:\Windows\System32\drivers\etc), máy Linux ở (/etc/hosts) và thêm vào nội dung

192.168.1.34 server1.local
192.168.1.34 server2.local

Giờ chúng ta đã có 2 website lưu trữ trên Apache và truy cập qua cổng 8888.

Bước 6: Nginx

Bây giờ chúng ta sẽ tạo máy chủ ảo cho Nginx bằng làm như đã làm cho Apache. Đầu tiên, hãy tạo thư mục gốc tài liệu cho cả hai trang web:

sudo mkdir -v /usr/share/nginx/server1 /usr/share/nginx/server2

Tạo index.html và info.php để kiểm tra:

echo "<h1 style='color: green;'>Nginx Server 1</h1>" | sudo tee /usr/share/nginx/server1/index.html
echo "<h1 style='color: red;'>Nginx Server 2</h1>" | sudo tee /usr/share/nginx/server2/index.html
echo "<?php phpinfo(); ?>" | sudo tee /usr/share/nginx/server1/info.php
echo "<?php phpinfo(); ?>" | sudo tee /usr/share/nginx/server2/info.php

Tạo VirtualHost Nginx cho server1

sudo nano /etc/nginx/sites-available/server1

Thêm nội dung sau:

server {
    listen 80 default_server;

    root /usr/share/nginx/server1;
    index index.php index.html index.htm;

    server_name server1.local www.server1.local;
    location / {
        try_files $uri $uri/ /index.php;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
        include snippets/fastcgi-php.conf;
    }
}

Nginx gọi server {. . .} là các cấu hình cho các Server Block. Tạo các Server Block tương tự như VirtualHost trong Apache. Giá trị default_server nghĩa là trở thành server chính khi truy cập vào. Giả sử bạn thêm nhiều domain vào server, khi các domain này chưa chỉ định thư mục cụ thể thì sẽ sử dụng default_server như là thư mục chính để truy cập.
Tương tự với server2, server1 làm default_server nên server2 không cần

sudo nano /etc/nginx/sites-available/server2

thêm nội dung

server {
    root /usr/share/nginx/server2;
    index index.php index.html index.htm;

    server_name server2.local www.server2.local;
    location / {
        try_files $uri $uri/ /index.php;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php7.4-fpm.sock;
        include snippets/fastcgi-php.conf;
    }
}

Kích hoạt 2 website này bằng cách tạo liên kết:

sudo ln -s /etc/nginx/sites-available/server1 /etc/nginx/sites-enabled/server1
sudo ln -s /etc/nginx/sites-available/server2 /etc/nginx/sites-enabled/server2

Kiểm tra cấu hình Nginx, không phát sinh vấn đề thì khởi động lại

sudo nginx -t
sudo systemctl reload nginx

Bây giờ, hãy truy cập thứ hai trang web http://server.local/info.phphttp://server2.local/info.php. Sẽ có khác biệt so với Apache, đó là:
"SERVER_SOFTWARE" có giá trị "nginx/1.18.0".
"DOCUMENT_ROOT" có giá trị "/usr/share/nginx/server1" đó là thư mục bạn đã tạo cho trang web trên Nginx.

Đến giờ, chúng ta đã cài đặt Nginx và tạo hai máy chủ ảo. Tiếp theo, chúng ta sẽ cấu hình Nginx để làm Reverse Proxy trỏ về các miền được lưu trữ trên Apache.
Có thể xóa 2 file cấu hình server1 và server2 đi để tránh xung đột, vì phần sau chúng ta sẽ gộp chung vào một file cấu hình.

sudo rm -rf /etc/nginx/sites-available/server1 /etc/nginx/sites-available/server2

Bước 7: cấu hình Nginx cho Apache VirtualHost

Tạo tệp cấu hình mới cho Nginx để trỏ về nội dung được lưu trữ trên Apache

sudo nano /etc/nginx/sites-available/apache

nhập vào nội dung sau

server {
    listen 80;
    server_name server1.local www.server1.local server2.local www.server2.local;

    location / {
        proxy_pass http://your_server_ip:8888;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Lưu tệp và kích hoạt máy chủ ảo mới này bằng cách tạo một liên kết tượng trưng:

sudo ln -s /etc/nginx/sites-available/apache /etc/nginx/sites-enabled/apache

Kiểm tra và khởi động lại Nginx

sudo nginx -t
sudo systemctl restart nginx

Kiểm tra lại http://server.local/info.phphttp://server2.local/info.php. Chúng ta lại có
"SERVER_SOFTWARE" có giá trị "Apache/2.4.41 (Ubuntu)".
"DOCUMENT_ROOT" có giá trị "/var/www/server1" đó là thư mục bạn đã tạo cho trang web trên Nginx.
Ngoài ra còn có thêm các giá trị "HTTP_X_REAL_IP" và "HTTP_X_FORWARDED_FOR"

Chúng ta đã thiết lập thành công Nginx làm Reverse Proxy cho các website cụ thể trên Apache. Tiếp theo, hãy cấu hình Apache để đặt biến REMOTE_ADDR làm cho nó như thể đang xử lý các yêu cầu này một cách trực tiếp.

Bước 8: cài đặt và cấu hình mod_rpaf

Trong bước này, bạn sẽ cài đặt một module Apache được gọi là mô-đun mod_rpaf, nhằm ghi lại các giá trị của REMOTE_ADDR , HTTPS và HTTP_PORT dựa trên các giá trị được cung cấp bởi Reverse Proxy. Một số ứng dụng PHP sẽ yêu cầu thay đổi mã để hoạt động liền mạch từ phía sau proxy. Nếu không có thì có thể khiến ứng dụng hoạt động không như mong muốn.
Module này hiện có trong kho lưu trữ của Ubuntu libapache2-mod-rpafnhưng nó đã khá là cũ và không hỗ trợ các chỉ thị cấu hình nhất định. Do đó, chúng ta sẽ cài đặt nó từ source.

#về thư mục chính
cd ~
#tải các gói cần thiết thể cài từ source
sudo apt install unzip build-essential apache2-dev
#tải xuống bản mới nhất từ Github
wget https://github.com/gnif/mod_rpaf/archive/stable.zip
#giải nén
unzip stable.zip
#vào thư mục
cd mod_rpaf-stable
#biên dịch và cài đặt
make
sudo make install

Tiếp theo, tạo file trong mods-available để load module

sudo nano /etc/apache2/mods-available/rpaf.load

Nhập vào nội dung sau

LoadModule rpaf_module /usr/lib/apache2/modules/mod_rpaf.so

Lưu và thoát, tạo tệp rpaf.conf chứa các chỉ thị cấu hình cho mod_rpaf:

sudo nano /etc/apache2/mods-available/rpaf.conf

nhập nội dung sau

<IfModule mod_rpaf.c>
	RPAF_Enable             On
	RPAF_Header             X-Real-Ip
	RPAF_ProxyIPs           your_server_ip 
	RPAF_SetHostName        On
	RPAF_SetHTTPS           On
	RPAF_SetPort            On
</IfModule>

Xem (https://github.com/gnif/mod_rpaf/blob/stable/README.md#configuration-directives) để biết thêm thông tin về cách cấu hình.
Lưu file rpaf.conf và bật module đó lên

sudo a2enmod rpaf

Kiểm tra cấu hình và khởi động lại Apache nếu không có lỗi

sudo apachectl -t
sudo systemctl reload apache2

Giờ hãy vào trang web và kiểm tra lại "REMOTE_ADDR" đã trở thành địa chỉ IP công cộng của server của bạn.

Như mình khi viết hướng dẫn này thì IP máy ảo là 192.168.1.34 và máy chính là 192.168.1.116, biến "REMOTE_ADDR" sẽ là 192.168.1.116

Bước 9: Thêm chứng chỉ SSL cho website

(Tùy chọn) Chúng ta sẽ sử dụng Certbot để tạo chứng chỉ TLS/SSL. Cài đặt Certbot bằng snap

sudo snap install --classic certbot

Sử dụng Certbot để lấy chứng chỉ SSL cho domain

#server1
sudo certbot --agree-tos --no-eff-email --email your-email --nginx -d server1.local -d www.server1.local
#server2
sudo certbot --agree-tos --no-eff-email --email your-email --nginx -d server2.local -d www.server2.local

Truy cập một trong các miền của Apache trong trình duyệt của bạn bằng https://, nhìn qua trên thanh địa chỉ chúng ta có thể thấy trang web của bạn đã có SSL.

Truy cập vào website/info.php, chún ta thấy biến "SERVER_PORT" đã được đặt thành 443 và HTTPS được đặt thành bật , như vậy Apache được truy cập trực tiếp qua HTTPS. Với các biến này được đặt, các ứng dụng PHP không phải được cấu hình đặc biệt để hoạt động sau Reverse Proxy.

Tiếp theo chúng ta sẽ chặn truy cập trực tiếp vào Apache.

Bước 10:

(Tùy chọn) Vì Apache đang lắng nghe trên cổng 8888 nên mọi người đều có thể truy cập nó. Nó có thể bị chặn bằng cách đưa lệnh IPtables sau vào bộ quy tắc tường lửa của bạn.

sudo iptables -I INPUT -p tcp --dport 8888 ! -s your_server_ip -j REJECT --reject-with tcp-reset

Bước 11:

(Tùy chọn)Điều hướng để xử lý file tĩnh bằng Nginx. Khi Nginx yêu cầu đến website host ở Apache, nó sẽ gửi mọi yêu cầu file cho website đó tới Apache. Tuy nhiên, Nginx lại nhanh hơn Apache trong việc cung cấp các file tĩnh như hình ảnh, JavaScript và stylesheets. Vì vậy, hãy cấu hình file máy chủ ảo của Nginx để phân phát trực tiếp các file tĩnh, và gửi yêu cầu PHP tới Apache.
Mở file /etc/nginx/sites-available/apache và sửa nội dung như sau, ví dụ với HTTPS được bật

#SERVER 1
server {
    listen 80;
    server_name server1.local www.server1.local;
    root /var/www/server1.local;
    index index.php index.htm index.html;

    location / {
        try_files $uri $uri/ /index.php;
    }

    location ~ \.php$ {
        proxy_pass http://your_server_ip:8888;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location ~ /\.ht {
        deny all;
    }

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/server1.local/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/server1.local/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

#SERVER 2
server {
    listen 80;
    server_name server2.local www.server2.local;
    root /var/www/server2.local;
    index index.php index.htm index.html;

    location / {
        try_files $uri $uri/ /index.php;
    }

    location ~ \.php$ {
        proxy_pass http://your_ip_address:8888;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location ~ /\.ht {
        deny all;
    }

    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/server2.local/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/server2.local/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

Lệnh try_files yêu cầu Nginx tìm kiếm các tệp trong thư mục website và trực tiếp phân phát chúng. Nếu tệp có phần mở rộng là php yêu cầu sẽ được chuyển đến Apache. Ngay cả khi tệp không được tìm thấy trong thư mục gốc, yêu cầu được chuyển đến Apache để Apache phân phát chúng.

Chú ý: "location ~ /.ht" rất quan trọng; điều này ngăn Nginx phân phát nội dung của các tệp cấu hình Apache như .htaccessvà .htpasswd, có chứa thông tin nhạy cảm.

Lưu tệp, kiểm tra và khởi động lại

sudo nginx -t
sudo service nginx reload

Kết luận

Giờ server của chúng ta đã có Apache để chạy các website động php, Nginx làm Reverse Proxy và phân phối các nội dung tĩnh khác. Sự kết hợp này sẽ làm tăng tốc đáng kể website của bạn. Ngoài ra, các website còn có chứng chỉ SSL tăng tính bảo mật cũng như tin cậy cho chúng. Bạn có thể áp dụng cách này để thêm nhiều website khác.
Ngoài ra, nhớ chức năng làm Reverse Proxy của Nginx. Chúng ta có thể sử dụng để Nginx làm Reverse Proxy cho các ứng dụng khác như NodeJS...
Nếu có khó khăn hay có câu hỏi gì hãy đừng ngại ngần mà viết comment ở phần bên dưới nhé.