Skip to main content

Using Apache as (SSL) proxy server

If you are a using any Java web server (such as Tomcat) or app server (such as Wildfly, JBoss or Glassfish), then you know that they dont listen on http port 80.

Well, why does it have to listen on port 80? because if you want your user to specify only the url and not the port; for example,
http://mydomain.com and not http://mydomain:8080

Note: throughout this article mydomain.com can be replaced with localhost, if your product is in development stage.

Software used:
  • Ubuntu
  • apache2
  • Any Java web/app server

What we want to achieve

We are going to divert all user requests coming on port 80 to Tomcat running 8080 on the same host.

Installation

Run the following commands
sudo apt-get install apache2
sudo apt-get install libxml2-dev
sudo apt-get install libapache2-mod-proxy-html
Note: If your ubuntu OS is latest, then you may not have libapache2-mod-proxy-html package. Instead run the following command.
sudo a2enmod proxy_html

Configuration

1. Append the following to /etc/apache2/sites-available/000-default.conf inside <VirtualHost *:80> block
 ProxyHTMLEnable On
 ProxyHTMLInterp On
 ProxyPreserveHost Off
 ProxyPass /myapp http://localhost:8080/myapp
 ProxyPassReverse /myapp http://localhost:8080/myapp
 ProxyHTMLURLMap http://localhost:8080/myapp /myapp/

2. Create the following symlinks
cd /etc/apache2/mods-enabled
sudo ln -s ../mods-available/proxy.load
sudo ln -s ../mods-available/proxy_http.load

3. Create /etc/apache2/mods-enabled/proxy_http.conf file with the following content. 
LoadFile /usr/lib/x86_64-linux-gnu/libxml2.so
On some machines the libxml2.so is present in /usr/lib/libxml2.so location. If it is so, in your case, update the above file with /usr/lib/libxml2.so path

4. Restart apache2.
sudo service apache2 restart

Now (assuming that you have an app running at http://mydomain.com:8080/myapp) you can hit http://mydomain.com/myapp and see your application page.


Let's say that you are not satisfied. You want your users to hit http://mydomain.com and not http://mydomain.com/myapp. Well, that's easy.

Edit the /etc/apache2/sites-available/000-default.conf file with the following content
 ProxyPass / http://localhost:8080/myapp
 ProxyPassReverse / http://localhost:8080/myapp

That's it. you can now hit http://mydomain.com; you will see your application page. Hold on. There is one problem.

If you were servering any static content, such as image (e.g http://mydomain.com/data/banner.jpg) in your application myapp, then it will not work now. How do we address this? Read on.

Add the following line to /etc/apache2/sites-available/000-default.conf file 
ProxyPassMatch ^/data(.*)$ !
# This redirects everything except the above exceptions to tomcat.

Your final file will look like this.
<VirtualHost *:80>
        #ServerName www.example.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # This redirects everything except the above exceptions to tomcat.
        ProxyPassMatch ^/data(.*)$ !

        ProxyHTMLEnable On
        ProxyHTMLInterp On
        ProxyPreserveHost Off
        ProxyPass / http://localhost:8080/myapp                                 
        ProxyPassReverse / http://localhost:8080/myapp
        ProxyHTMLURLMap http://localhost:8080/myapp /myapp/
</VirtualHost>

Don't forget to restart apache server.



Great. Now you want to redirect HTTPS/SSL traffic as well. How do we do?

1. Run the following commands
sudo apt-get install openssl
sudo a2enmod ssl
sudo ln -s /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-enabled/default-ssl.conf

2. add the following to /etc/apache2/sites-available/default-ssl.conf
ProxyPassMatch ^/data(.*)$ !
SSLProxyEngine on
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off

ProxyPass / https://localhost:8443/myapp
ProxyPassReverse / https://localhost:8443/myapp

If you get any error related to SSLProxyCheckPeerName you can comment that line and restart apache.
You are done. You will get your HTTPS/SSL traffic to port 8443 now.


Let's say you want to have a beta release for some features of your application. i.e., you want your application to be available at beta.mydomain.com.

Few things to note here are,
  • You are already running a production application myapp at http://mydomain.com:8080/myapp (now it is available at http://mydomain.com)
  • You are running a beta version of myapp (with some additional features) at http://mydomain.com:9080/myapp (note the port here) and you want that to be available at http://beta.mydomain.com
Alright it is simple. I assume that domain-to-your-host mapping is already taken care and both mydomain.com and beta.mydomain.com are now pointing to the same machine (i.e., same IP address)

Add/uncomment the following line in /etc/apache2/sites-available/000-default.conf
ServerName mydomain.com

Append (not replace) the following to /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>
    ServerName beta.mydomain.com
    <-- remaining configuration for redirection of beta site goes here -->
</VirtualHost>

Your final file will look like this.
<VirtualHost *:80>
        ServerName mydomain.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # This redirects everything except the above exceptions to tomcat.
        ProxyPassMatch ^/data(.*)$ !

        ProxyPass / http://localhost:8080/myapp                                 
        ProxyPassReverse / http://localhost:8080/myapp                                  
</VirtualHost>

<VirtualHost *:80>
        ServerName beta.mydomain.com
        # This redirects everything except the above exceptions to tomcat.
        ProxyPassMatch ^/data(.*)$ !

        ProxyPass / http://localhost:9080/myapp                                 
        ProxyPassReverse / http://localhost:9080/myapp
</VirtualHost>
That's it. Now you have your production site at http://mydomain.com and beta site at http://beta.mydomain.com




Comments

Popular posts from this blog

Installing GoDaddy certificate in Wildfly/Keycloak

In the previous post we saw how to set up Keycloak . Here we will see how to generate and install GoDaddy.com certificate in Keycloak. The steps are similar for Wildfly as well. Step 1: Generate CSR file Run the following commands in your terminal. <mydomain.com> has to be replaced with your actual domain name. keytool -genkey -alias mydomain_com -keyalg RSA -keysize 2048 -keystore mydomain_com.jks keytool -certreq -alias mydomain_com -file mydomain_com.csr -keystore mydomain_com.jks Step 2: Generate certificate Upload  mydomain_com . csr  file content into GoDaddy.com, generate and download certificate for tomcat server (steps to generating SSL certificate is beyond the scope of this article). If you unzip the file, you will see the following files. gd_bundle-g2-g1.crt ..5f8c...3a89.crt   #some file with alphanumeric name gdig2.crt Files 1 and 2 are of our interest. Third file is not required. Step 3: Import certificate to key store Download r

Using Nginx as proxy server for Keycloak

I have used Keycloak  in its very early stage ( when it is was in 2.x version). But now it has come a long way (at this time of writing it is in 21.x) In this article let's configure Keycloak behind Nginx. Here are the points to consider.  If you want to configure Apache2 as a proxy server for your java application, please check  this article . We are going to use a domain name other than localhost Anything other than localhost will require Keycloak to run in production mode which requires SSL configurations etc. Or it requires a proxy server. Lets begin. Requirements Keycloak distribution Ubuntu 22.04 server Configuring Keycloak 1. Download Keycloak from here . 2. Extract it using tar -xvzf  keycloak-21.0.1.tar.gz 3. Create a script file called keycloak.sh with the following contents #!/bin/bash export KEYCLOAK_ADMIN=<admin-username-here> export KEYCLOAK_ADMIN_PASSWORD=<admin-password-here> nohup keycloak-21.0.0/bin/kc.sh start-dev --proxy edge --hostname-strict=fa

Hibernate & Postgresql

If you are using Hibernate 3.5 or above to talk to Postgresql database, have you ever tried to store a byte array? Let's take an example. Here is the mapping which will store and read byte[] from the database. @Lob @Column(name = "image") private byte[] image; Here is the JPA mapping file configuration. <persistence version="2.0"  xmlns="http://java.sun.com/xml/ns/persistence"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">   <persistence-unit name="testPU" transaction-type="JTA">     <provider>org.hibernate.ejb.HibernatePersistence</provider>     <jta-data-source>test</jta-data-source>     <properties>     </properties>   </persistence-unit> </persistence> When you try to save your entity you will get t

Dynamic SOAP Service Client

If you have written SOAP service client, you might know that you need the WSDL file; need to generate Java code for that,compile that Java classes and add it as dependency for your module. What would you do if you have to incorporate your code with a new SOAP service every now and then? What would you do if all you need is to consume the service and do a little processing on the output, i.e., you need the data in XML format? What would you do if you don't have a complete WSDL? What would you do if your service is in .NET whose WSDL is having problem while generating Java classes? Is there a way to write a dynamic client which can consume any SOAP service? .... YES!... there is a way. Let's quickly write a web (SOAP) service. Software used: Java 7 NetBeans IDE 7.4 GlassFish 4.0 Maven Create a web project and choose Glassfish as server. Now add web service (not a rest service) as below. Edit the SimpleService.java as follows. package com.mycom

How to retry a method call in Spring or Quarkus?

Have you ever come across a situation where you wanted to retry a method invocation automatically? Let's say you are calling a stock ticker service for a given stock and get a transient error. Since it is a transient error, you will try again and it may work in second attempt. But what if it doesn't? Well, you will try third time. But how many times can you try like that? More importantly after how much time will you retry? Imagine if you have a handful of methods like this. Your code will become convoluted with retry logic. Is there a better way? Well, if you are using spring/spring boot, you are in luck. Here is how you can do that using spring. Let's write our business service as follows. import java.time.LocalDateTime; import java.util.concurrent.CompletableFuture; import lombok.extern.slf4j.Slf4j; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.scheduling.annotation.Async; import