NGINX, in addition to being used as a static or dynamic web server, has other developed features like most web servers. It offers reverse proxy functionality, also known as a gateway server. This feature also allows you to configure a load-balancing scenario to distribute traffic for availability, scalability, and performance.
A reverse proxy server receives requests, generally from web clients, and retrieves and serves resources from one or more servers. In this article, we show you how to set up a reverse proxy with NGINX using a single Apache web server with FastCGI for interconnection, allowing it to handle more simultaneous requests.
What is a reverse proxy and how does it work?
A gateway or proxy server does not generate content or host data; instead, content is obtained from Backend servers, which normally have a direct connection to internal networks. For example, in a web scenario, when a proxy server receives traffic from a client, it sends the request to one of these Backend servers to handle it, generate the content, and provide a response. The proxy server then forwards the response to the client.
There are multiple reasons to implement this feature, including security, high availability, load balancing, and centralized authorization. Typically, in these implementations, the architecture and infrastructure design of the Backend servers are isolated/protected from the outside (DMZ); the only connection should be to the proxy server (which receives and provides the response to the client).
Installing and configuring the web server (Apache)
First, update the repositories and packages:
# apt update && apt upgrade -yThen, install apache and PHP:
# apt install apache2 php-fpm php-curl php-dompdf php-mbstring php-imagick php-zip php-gd -yThe fcgi module from Ubuntu's own repository is normally used, but it is outdated:
# apt show libapache2-mod-fcgidTherefore, you can use FastCGI with a more up-to-date version available in the FastCGI-Archives GitHub repository. To do this, first run the following command to install some required packages:
# apt install unzip build-essential apache2-dev gcc make m4 autoconf automake libtool -yThen download the compressed code:
# cd /tmp/ && wget https://github.com/FastCGI-Archives/fcgi2/archive/refs/heads/master.zipExtract it and enter the directory:
# unzip master.zip && cd fcgi2-master/Configure it:
# ./autogen.sh && ./configureAnd install it:
# make && make installNext, also download the rpaf module for headers:
# cd /tmp/ && wget https://github.com/gnif/mod_rpaf/archive/stable.zipThen, extract it, enter the extracted directory, and install it as well:
# unzip stable.zip && cd mod_rpaf-stable && make && make installTo load it, create the file:
# vi /etc/apache2/mods-available/rpaf.loadWith the following content:
LoadModule rpaf_module /usr/lib/apache2/modules/mod_rpaf.soAlso create another file for the configuration:
# vi /etc/apache2/mods-available/rpaf.confWith this content (IMPORTANT: Change the IP address):
<IfModule mod_rpaf.c>
RPAF_Enable On
RPAF_Header X-Real-Ip
RPAF_ProxyIPs DIRECCION_IP
RPAF_SetHostName On
RPAF_SetHTTPS On
RPAF_SetPort On
</IfModule>Now enable them, along with the following modules:
# a2enconf php7.4-fpm && a2enmod rpaf actions proxy_fcgi setenvif cache cache_disk expires headersThen, change Apache's default port from 80 to 8080:
# vi /etc/apache2/ports.confAnd also disable the default site:
# a2dissite 000-defaultThen copy the default site file (modify the example destination):
# cp /etc/apache2/sites-available/000-default.conf /etc/apache2/sites-available/ejemplo.es.confEdit the copied site file with a text editor:
# vi /etc/apache2/sites-available/ejemplo.es.confTo change the port, change "<VirtualHost *:80>" to "<VirtualHost *:8080>" and change the ServerName value to the FQDN in question:
<VirtualHost *:8080>
ServerName ejemplo.es
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/
CacheQuickHandler off
CacheLock on
CacheLockPath /tmp/
CacheIgnoreHeaders Set-Cookie
<Location />
CacheEnable disk
CacheHeader on
CacheDefaultExpire 800
CacheMaxExpire 64000
CacheIgnoreNoLastMod On
ExpiresActive on
ExpiresDefault A300
</Location>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Save your changes, exit the editor, and then enable the site:
# a2ensite ejemplo.es.confCheck that the Apache service configuration is correct:
# apachectl -tIf it is, reload the configuration by restarting the Apache service:
# systemctl restart apache2Then you can check that it is listening on the modified port:
# ss -tlpn sport :8080And also create a PHP information file to run a test to check FastCGI:
# echo "<?php phpinfo(); ?>" | tee /var/www/html/info.phpInstalling and configuring a reverse proxy with NGINX
First install NGINX and Certbot to obtain an SSL certificate with Let's Encrypt:
# apt install nginx python3-certbot-nginxNext, create the directory for the NGINX cache:
# mkdir -p /var/lib/nginx/cache && chown www-data /var/lib/nginx/cache && chmod 700 /var/lib/nginx/cacheRemove the default site:
# rm /etc/nginx/sites-enabled/defaultCreate the file for the site you will use (modify the example):
# vi /etc/nginx/sites-available/ejemplo.esWith the following content (modify the example server_name):
proxy_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=nginx_cache:8m max_size=50m;
proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args";
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
server {
listen 80;
server_name ejemplo.es;
location / {
proxy_pass http://[::1]:8080;
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;
proxy_cache nginx_cache;
proxy_cache_bypass $http_upgrade;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 16 16k;
proxy_busy_buffers_size 32k;
proxy_max_temp_file_size 0;
proxy_temp_file_write_size 32k;
gzip on;
gzip_min_length 1000;
gzip_comp_level 5;
gzip_proxied any;
gzip_vary on;
gzip_types
text/css
text/javascript
text/xml
text/plain
text/x-component
application/javascript
application/json
application/xml
application/rss+xml
font/truetype
font/opentype
application/vnd.ms-fontobject
image/svg+xml;
add_header X-Proxy-Cache $upstream_cache_status;
add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header X-Cache-Status $upstream_cache_status;
add_header X-Powered-By 'Clouding';
}
location ~ /\.ht {
deny all;
}
}Create a symbolic link to the available site in the active sites directory:
# ln -s /etc/nginx/sites-available/ejemplo.es /etc/nginx/sites-enabled/ejemplo.esFinally, restart the NGINX service:
# systemctl restart nginx.serviceAfterward, you can also use Certbot, which you installed earlier, to obtain the certificate and automatically configure nginx:
# certbot --nginx --redirect -d ejemplo.esAdditionally, if you have mixed content issues, you can modify the Apache configuration. To do this, edit the configuration file:
# vi /etc/apache2/apache2.confAnd add at the end:
<IfModule mod_setenvif.c>
SetEnvIf X-Forwarded-Proto "^https$" HTTPS
</IfModule>Performance test
As you can see from the following test comparing a Plesk server with the same configuration against a server configured with these features, the latter offers better response times.
Plesk server with an NGINX proxy (cache enabled) with PHP 7.4 served by FastCGI with Apache:
Running 30s test @ https://plesk.ejemplo.es/info.php
12 threads and 50 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 87.18ms 93.88ms 1.49s 95.08%
Req/Sec 51.35 32.85 646.00 96.83%
17578 requests in 30.07s, 1.70GB read
Socket errors: connect 0, read 0, write 0, timeout 33
Requests/sec: 584.66
Transfer/sec: 57.87MBServer configured using this article:
Running 30s test @ https://proxy.ejemplo.es/info.php
12 threads and 50 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 59.98ms 27.89ms 1.17s 96.40%
Req/Sec 66.73 12.59 150.00 60.93%
24035 requests in 30.10s, 1.70GB read
Requests/sec: 798.56
Transfer/sec: 57.78MBTest performed with the following PHP configuration on both servers:
pm.max.children 26
pm.start_servers 0
pm.min_spare_servers 2
pm.max_spare_servers 8Have you tried it? Leave us your comments! 🙂