Apache HTTP as a reverse proxy consists on setting an Apache HTTP server as a frontal access for one or multiple backend servers. In the recent years many have started using NGINX as a reverse proxy since this piece of software really shines for serving static content an acting as a cache server. This doesn’t mean Apache HTTP can’t act as such. For quite a few years now, the processing model in Apache has changed towards a very similar multi threaded processing model used in NGINX, called Event.
Many still think NGINX is significantly more performant than Apache HTTP, which isn’t true and hasn’t been for quite too long. Both are comparable for lots of applications. Many guides and how tos however, have still explained how to configure Apache using the old pre-fork multi-processing mode, which binds one connection to one child process. Configuring Apache in such way and comparing it to NGINX is like comparing a fully capable human with one with the legs tied, a hand in the back and a blind eye. A bit unfair to say the least. The fact that distributions like Ubuntu were defaulting to the pre-fork model in the past didn’t help either.
If you want to configure Apache with the Event MPM, an apples to apples way to configure it if you pretend to compare it to NGINX, you can also read a couple of guides I wrote some time ago, if you are using Ubuntu, and this other one if you are using FreeBSD. You may want to read the one I did on my own site, here at adminbyaccident.com.
So, what are we going to do in this ‘how to configure Apache HTTP as a reverse proxy’guide? We are basically going to set up a host with an Apache HTTP installation and on a different host we’re going to setup another Apache HTTP install but configured as a reverse proxy. This means the regular Apache HTTP host will be the backend server and the one set up as a reverse proxy is going to be the one at the front accepting all the connections from the outside world, also known as the frontal server.
If you find the articles in Adminbyaccident.com useful to you, please consider making a donation.
Use this link to get $200 credit at DigitalOcean and support Adminbyaccident.com costs.
Get $100 credit for free at Vultr using this link and support Adminbyaccident.com costs.
Mind Vultr supports FreeBSD on their VPS offer.
In this Apache HTTP as a reverse proxy guide we’re going to present the frontal connections served with TLS but we’ll be calling the backend in plain HTTP. This is the typical setup, although nowadays some are starting to embrace the zero trust architecture and the connection with the backend is encrypted too. More of this on a later article.
Pre-requisites.
Preferably a FreeBSD box with a simple Apache install. (For the sake of completion this will be Step 0 in here).
A second box where we will set up Apache HTTP as a reverse proxy.
Step 0. Install Apache HTTP on the backend servers.
This is a very simple operation. Just install Apache as you would do in your favourite GNU/Linux distribution. I’ll be using FreeBSD which is considered to be UNIX but without a fancy trademark.
# pkg install -y apache24
Once installed on FreeBSD you need to configure it to be started. For that one needs to edit the /etc/rc.conf file, which can be done manually or with the following command.
# sysrc apache24_enable="YES"
We can now start Apache HTTP.
# service apache24 start
And if we visit our server’s IP through our browser of choice we should be able to see a very welcomed message that reads as ‘It works!’.
You may want to use a different tool than a browser so there’s no need to abandon the command line. Use curl then.
[albert@BSDVM ~]$ curl http://192.168.1.220
<html><body><h1>It works!</h1></body></html>
[albert@BSDVM ~]$
Step 1. Install Apache HTTP on the frontal server.
On a different box, server or FreeBSD jail you may have set up, install another instance of Apache HTTP. We’re going to configure this one to reach the backend server in the next few steps.
Repeat the steps we’ve made on step 0. You already know how to do this.
As a result you should get the same message on that different IP.
Note the backend server is sitting on the address ended in 220 and the frontal is sitting on the 225.
With curl:
[albert@BSDVM ~]$ curl http://192.168.1.225
<html><body><h1>It works!</h1></body></html>
[albert@BSDVM ~]$
Step 2. Modify the index.html file on the backend.
This step is more of a tip than a step. However a very useful one. At this point there’s no difference on the output from both servers. So once you are making a conexion through the frontal box you want to know the one at the backend is the one spitting out its content. If both contain the same message you want be able to distinguish which is serving the content, if the one on the front or the one in the back.
For this difference to happen we will modify the file present by default in a fresh Apache HTTP install. So whenever we access the reverse proxy (frontal server) we shouldn’t see the content from the index.html on it but the one from the backend. Of course that will be the case once we have made the full reverse proxy setup.
On FreeBSD this index.html file just says ‘It works!’, however on Linux distributions you will find very different presentations. I’d recommend to change the filename on those and create your own index.html file in the appropiate path.
If you happen to use FreeBSD, as I am, the path for the file is:
/usr/local/www/apache24/data/index.html
However, for the GNU/Linux folks that may well be something close to:
/var/www/html/index.html
In either case you have to modify that file.
For FreeBSD users you may just want to modify the file content so instead of reading ‘It works!’, you may choose something different like ‘Backend Server 1’. Use your favourite editor to do so.
Before:
<html><body><h1>It works!</h1></body></html>
After:
<html><body><h1>Backend Server 1</h1></body></html>
For anyone using a GNU/linux option I’d recommend to change the name of the original file and then edit one from scratch that contains this:
<html><body><h1>Backend Server 1</h1></body></html>
To make sure the file has changed just point your browser to the backend IP or use curl again.
Now you are sure the backend displays a message ensuring that is the intended output from it. You can also modify the front one, if you wish to do so. If you don’t the default ‘It works!’ message will be displayed.
Step 3. Configure the frontal server as the reverse proxy.
To configure Apache HTTP as a reverse proxy we need to enable the proxy modules, but it’s also very useful to enable the virtual host module. By enabling the proxy modules, as one can easily deduce, the proxy capabilities on Apache HTTP are set to be used. Enabling the virtual host capacity allows a very interesting and useful feature. One can declare several backends as different virtual host configurations and all of them are served from the server at the front, the reverse proxy.
In order to enable the frontal Apache HTTP as the reverse proxy you will first need to enable a few modules. Specifically three of them:
mod_proxy
mod_proxy_http
mod_proxy_http2
All of them are found in the httpd.conf file, which is the main configuration file. On FreeBSD one just needs to uncomment the line where they are on to enable them (plus a restart), but on other systems like a few GNU/Linux ones, such as Debian and derivatives, there’s a special command called a2enmod. RHEL based distributions like Fedora, CentOS and the like don’t make use of it and are pretty similar to FreeBSD in that respect.
On RHEL or FreeBSD just look for the line containing the module, remove the ‘#’ at the beginning of the line with your editor of choice and apply a restart afterwards, so the module gets loaded.
On FreeBSD you can also use these:
sed -i -e '/mod_proxy.so/s/#LoadModule/LoadModule/' /usr/local/etc/apache24/httpd.conf
sed -i -e '/mod_proxy_http.so/s/#LoadModule/LoadModule/' /usr/local/etc/apache24/httpd.conf
sed -i -e '/mod_proxy_http2.so/s/#LoadModule/LoadModule/' /usr/local/etc/apache24/httpd.conf
On RHEL based systems adjuts the commands as needed but sed is also available. However, you may need to change the paths since Apache HTTP typically sits under /etc in almost every GNU/Linux system.
On Debian and derivatives just use the ‘a2enmod’ command to enable those modules.
Once the proxy modules are enabled make sure they are by using the following command.
root@infront:~ # apachectl -M | grep 'proxy'
proxy_module (shared)
proxy_http_module (shared)
proxy_http2_module (shared)
root@infront:~ #
You should get the three modules enabled independently of what type of system you are using.
Once the proxy modules are enabled it’s time to do the same with the virtual host. On FreeBSD one line has to be uncommented. The one containing the ‘httpd-vhosts.conf’ string. You can enable the module by typing the following command and restarting Apache HTTP.
sed -i -e '/httpd-vhosts.conf/s/#Include/Include/' /usr/local/etc/apache24/httpd.conf
Mind the virtual host configuration file has an example set of directives. When restarted Apache HTTP will complain about those lines but we’ll configure them now, so you can wait for a restart.
We will maintain the original ‘httpd-vhosts.conf’ file by renaming it.
mv /usr/local/etc/apache24/extra/httpd-vhosts.conf /usr/local/etc/apache24/extra/httpd-vhosts.original
Once renamed we can edit our file with our own set of Apache HTTP directives. Use something similar to the following VirtualHost entry in the ‘httpd-vhosts.conf’ file found in the /usr/local/etc/apache24/extra directory.
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Protocols h2 h2c http/1.1
ProxyPass / http://192.168.1.220:8080/
ProxyPassReverse / http://192.168.1.220:8080/
ErrorLog "/var/log/example.com-error.log"
CustomLog "/var/log/example.com-access.log"common
</VirtualHost>
As you can read we’ve set a few directives for this instance of Apache HTTP at the front to be used as a reverse proxy. Notice the backend entry identified by the protocol http, the IP as 192.168.1.220 and port 8080. The port on the backend is still number 80, so we need to change that.
Let’s now apply a restart so the changes into the virtual host configuration file are applied.
root@infront:~ # apachectl restart
Performing sanity check on apache24 configuration:
Syntax OK
Stopping apache24.
Waiting for PIDS: 2349.
Performing sanity check on apache24 configuration:
Syntax OK
Starting apache24.
root@infront:~ #
Now that we’ve configured the front end box as a reverse proxy let’s change the port where the backend server is listening.
Step 4. Configure the backend server for the reverse proxy
Let’s move to the backend server and adjust that port number to be 8080 instead of the default 80.
Find the line you need to edit using the following grep command.
root@backend:~ # grep -n 'Listen' /usr/local/etc/apache24/httpd.conf
44:# Listen: Allows you to bind Apache to specific IP addresses and/or
48:# Change this to Listen on specific IP addresses as shown below to
51:#Listen 12.34.56.78:80
52:Listen 80
root@backend:~ #
As you can see, the directive for the port where Apache HTTP is listening, is sitting on the 52th line. Let’s edit it. Grab your favourite editor or just use this next command.
sed -i -e '/Listen/s/80/8080/' /usr/local/etc/apache24/httpd.conf
Apply a restart so the server backend uses the new port 8080 instead of the old 80.
root@backend:~# apachectl restart
Now grab your browser and point it to your backend server’s IP address and port 8080. You shouldn’t get anything but an error message on port 80, but this on port 8080.
Step 5. Check the reverse proxy is working
Now that both boxes have been configured, the front as a reverse proxy of the backend, when we visit the domain name associated to the frontal box we should see the ‘Backend Server 1’ message. Why? Well, basically that is what a reverse proxy does, it takes a request and goes grab the content from where the correct backend sits.
Be careful because if you visit the frontal server’s IP through your browser, instead of using a domain name… you will read the ‘Frontal Server’ message again, even though the reverse proxy configuration has been applied. Confused? Hopefully not but let’s clarify this further, shall we?
Notice in the virtual host configuration for the frontal web server we have specified a domain name, in this example that is ‘example.com’. If we point the browser to a domain the reverse proxy is handling it will pull the content from the origin server, which is the backend server.
We can make our own ‘fake’ domain, there’s no need to purchase one for testing purposes. One needs to edit the ‘hosts’ file on the device one is using as a desktop.
If you are running your desktop on a Windows box you should open Notepad as administrator and look for the file named ‘hosts’ in the following path:
C:\Windows\System32\drivers\etc
Edit the file so the domain you want to visit points to the frontal server’s IP.
Whenever your local computer looks for that specific domain it will point to that IP you’ve specified in the ‘hosts’ file.
If you are running some kind of UNIX or GNU/Linux system the ‘hosts’ file is sitting in the /etc diretory. Use your favourite editor and add the same content as you would do in Windows.
Step 6. Add TLS termination
As many reverse proxy tutorials on the internet show the reverse proxy server, the one at the front, typically servers a second purpose and that is adding the needed encryption in the https communication.
To accomplish that we will need to generate a certificate, either self signed or from a certificate authority like Letsencrypt. We will also have to have a properly configured virtual host in the frontal server, the one acting as the reverse proxy. In this example we will use self signed certificates though.
We can start by enabling the mod_ssl module in Apache HTTP.
sed -i -e '/ssl_module/s/#LoadModule/LoadModule/' /usr/local/etc/apache24/httpd.conf
The module is enabled now but there is no configuration for it. FreeBSD has a default configuration file for SSL connections in the ‘extra’ folder. In order to activate such default configuration we will need to uncomment the line where it sits. The following command does that.
sed -i -e '/httpd-ssl.conf/s/#Include/Include/' /usr/local/etc/apache24/httpd.conf
Before applying these changes we need to enable another module, called ‘mod_socache_shmcb’ . This is needed for the SSLSessionCache directive to properly work. The configuration for this module is already present in the ‘httpd-ssl.conf’ file, so no changes other than enabling the module are needed.
This next command enables the ‘mod_socache_shmcb’ module.
sed -i -e '/mod_socache_shmcb/s/#LoadModule/LoadModule/' /usr/local/etc/apache24/httpd.conf
We have, so far, enabled the necessary bits in Apache HTTP to operate with SSL/TLS encrypted communications. We now need to complete two more steps. Getting a self signed certificate and configure our virtual host for TLS connections.
Let’s now create a self signed certificate with OpenSSL.
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /usr/local/etc/apache24/server.key -out /usr/local/etc/apache24/server.crt -subj "/C=ES/ST=Barcelona/L=Terrassa/O=Adminbyaccident.com/CN=example.com/[email protected]"
Let’s explain a few bits of this long one liner.
By invoking ‘openssl req’ we are invoking the openssl program in our system with a few options.
-x509 = this option outputs a self signed certificate
-nodes = with this option a private key is created without being encrypted
-days = place here the number of days this cerficiate will be valid
-newkey = the type of key and the number of bits it will be build with
-keyout = the path to place the key once it’s been created
-out = the path to the certificate that the key has signed
-subj = a series of options to add into the certificate such as follows:
Country Name (2 letter code) [AU]:
State or Province Name (full name) [Some-State]:
Locality Name (eg, city) []:
Organization Name (eg, company) [Internet Widgits Pty Ltd]:
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:
For further reference visit the OpenSSL official documentation.
And please use different options, otherwise your certificate will be signed with a domain you don’t want, a city your’re not in, etc.
Once the https port has been configured, the ssl module has been enabled, and the certificate has been created, we can make changes into the virtual hosts file ‘httpd-vhosts.conf’.
In the steps above we had configured something similar to this:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Protocols h2 h2c http/1.1
ProxyPass / http://192.168.1.220:8080/
ProxyPassReverse / http://192.168.1.220:8080/
ErrorLog "/var/log/example.com-error.log"
CustomLog "/var/log/example.com-access.log"common
</VirtualHost>
This above won’t be useful for our purposes which are described in this next paragraph.
First we need to redirect connections from port 80 to port 443. We can achieve this with a simple redirect or by a series or rewrite directives. Then we will have to add the encryption bits, with the correspondent directives. This time the ‘Proxy’ directives will be placed in the 443 block of the virtual host configuration. I’d recommend to empty the httpd-vhosts file and make a new one with the below configuration. I’ll explain each directive a few lines later.
First we eliminate our modified httpd-vhosts file.
root@infront:~ # rm /usr/local/etc/apache24/extra/httpd-vhosts.conf
root@infront:~ #
Now we’ll create an empty file with the same name.
root@infront:~ # touch /usr/local/etc/apache24/extra/httpd-vhosts.conf
root@infront:~ #
We will add this configuration below in the ‘httpd-vhosts’ file. Adjust the domain name values and the ‘ProxyPass’ and ‘ProxyPassReverse’ directives to your desired ones.
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
ServerSignature Off
Protocols h2 h2c http/1.1
Redirect / https://www.example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
ServerSignature Off
Protocols h2 h2c http/1.1
ProxyPass / http://192.168.1.220:8080/
ProxyPassReverse / http://192.168.1.220:8080/
Options -Indexes +FollowSymLinks -Includes
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
SSLHonorCipherOrder on
SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
SSLCertificateFile /usr/local/etc/apache24/server.crt
SSLCertificateKeyFile /usr/local/etc/apache24/server.key
ErrorLog "/var/log/example.com-error.log"
CustomLog "/var/log/example.com-access.log"common
Include /usr/local/etc/apache24/extra/httpd-headers.conf
</VirtualHost>
Let’s get into the first ‘VirtualHost’ block, the one related to port 80.
This is an almost barebones virtual host configuration, where the significant changes compared to the one that we had previously configured are: the removal of the ProxyPass directives to the next block and the add of the redirection to https.
The now added block for the 443, or https, connections is much more interesting. The directives worth commenting are two for the reverse proxy configuration.
ProxyPass. This directive allows the local server to find remote servers.
ProxyPassReverse. This directive helps the reverse proxy server not to be skipped due to any redirect present at the backend server.
There’s another interesing block of directives in this virtualhost, most of them dedicated to the SSL/TLS configuration. You could avoid placing this, since the default ‘httpd-ssl.conf’ configuration file already contains a working set. However that set of directives can be improved quite a lot by using a stronger set of cipher suites and disabling deprecated protocols such as TLSv1.1.
SSLEngine. This needs to be set to ‘on’ in order to use SSL/TLS.
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1. This configuration makes Apache HTTP to accept all the SSL protocols except those with minus symbol in front. In our case most of them are blocked and only the current TLSv1.2 and TLSv1.3 are accepted to use.
SSLCertificateFile. This directive points to the location where the certificate we’ve priorly created sits.
SSLCertificateKeyFile. The same as the one above but for the key file we created before.
Another worth mentioning directive is:
Include /usr/local/etc/apache24/extra/httpd-headers.conf
This directive is calling a file we haven’t placed inside our Apache HTTP configuration yet. We will now work on it.
What is it for? Well, in this file we will place a set of directives that will enhance the security of our communications with the clients avoiding a series of attacks such as downgrades to plain HTTP, cross-site scripting and cross-site request forgery.
So we will create a file named as follows
root@infront:~ # touch /usr/local/etc/apache24/extra/httpd-headers.conf
And then add the following content with our favourite editor:
<IfModule mod_headers.c>
# Add security and privacy related headers
Header set Content-Security-Policy "default-src 'https';"
Header always edit Set-Cookie (.*) "$1; HttpOnly; Secure"
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header set X-Content-Type-Options "nosniff"
Header set X-XSS-Protection "1; mode=block"
Header set X-Robots-Tag "all"
Header set X-Download-Options "noopen"
Header set X-Permitted-Cross-Domain-Policies "none"
Header always set Referrer-Policy: "strict-origin"
Header set X-Frame-Options: "deny"
Header set Permissions-Policy: "geolocation self https://example.com; midi none; camera none; notifications self https://example.com; microphone none; speaker none; payment none"
SetEnv modHeadersAvailable true
</IfModule>
With all these configuration bits in place it’s time to test our configuration.
root@infront:~ # apachectl configtest
Performing sanity check on apache24 configuration:
Syntax OK
root@infront:~ #
We should get a ‘Syntax OK’ to move forward. Otherwise we will get an error message with the line and file with the mistake to correct.
With the positive confirmation we can now restart the Apache HTTP server to all these changes are applied.
root@infront:~ # apachectl restart
Performing sanity check on apache24 configuration:
Syntax OK
Stopping apache24.
Waiting for PIDS: 8700.
Performing sanity check on apache24 configuration:
Syntax OK
Starting apache24.
root@infront:~ #
You can now get into your browser and point it to the domain/s of your choice. You should be greeted by the ‘Backend Server 1’ message through an encrypted connection, after accepting the warning message because of the self signed certificate we are using.
Conclusion.
In this how to configure Apache HTTP as a reverse proxy on FreeBSD we’ve configured two FreeBSD boxes with Apache HTTP installed on, one as a backend server and one as the frontal server getting all the connections. We’ve also added TLS connections between the clients and the frontal web server (the reverse proxy box). Furthermore we’ve add some security enhacing directives to protect the headers and cookies.
This configuration can be very handy when one has multiple sites. One may place different virtual hosts entries in a separate directory (remember to get an ‘Include’ directive into the main httpd.conf file for that) so the reverse proxy will act as a gateway for all those sites. This way one can be working on a backend server until it gets into production status. Placing other directives in the reverse proxy server can block connections to those ‘in the works’ sites but leave the already in production sites to work.
On a separate piece I will write about a series of rewrite rules that will enhance any Apache HTTP configuration, either standalone or as a reverse proxy. However that configuration can be very handy to use in a reverse proxy environment.
Another improvement that can be made and will get its own article is to also add encryption from the frontal server (the reverse proxy) to the backend server. In a no trust network environment this can be helpful. Intruders will not be able to sniff traffic between our servers at the backend and the gateway (the reverse proxy), even if they’ve been able to get inside our LAN.
If you find the articles in Adminbyaccident.com useful to you, please consider making a donation.
Use this link to get $200 credit at DigitalOcean and support Adminbyaccident.com costs.
Get $100 credit for free at Vultr using this link and support Adminbyaccident.com costs.
Mind Vultr supports FreeBSD on their VPS offer.