Main purpose of this article is to configure Keycloak Server to be proxied from Apache Web Server. Below you can find the steps:

Configure Keycloak for reverse proxy

You have to open the file KEYCLOAK_HOME/standalone/configuration/standalone.xml and do the changes shown below:

<subsystem xmlns="urn:jboss:domain:undertow:3.0">
...
<server name="default-server">
<!------- Change this line ----->
<http-listener name="default" socket-binding="http" proxy-address-forwarding="true" redirect-socket="proxy-https" />
...
<socket-binding-group name="standard-sockets" default-interface=" ...
...
<!------- Add this line ----->
<socket-binding name="proxy-https" port="443"/>
...

Set up Keycloak as a service

Create a file named keycloak-defaults with the following content (adjust the paths for your installation) and copy it to /etc/default/keycloak

## Location of JDK 
JAVA_HOME="/usr/lib/jvm/jdk8"

## Location of WildFly
JBOSS_HOME="/opt/keycloak"

## The username who should own the process.
JBOSS_USER=keycloak

## The mode WildFly should start, standalone or domain
JBOSS_MODE=standalone

## Configuration for standalone mode
JBOSS_CONFIG=standalone.xml

## Configuration for domain mode
JBOSS_DOMAIN_CONFIG=domain.xml
JBOSS_HOST_CONFIG=host-master.xml

## The amount of time to wait for startup
# If this is too low, the service might fail
STARTUP_WAIT=60

## The amount of time to wait for shutdown
SHUTDOWN_WAIT=60

## Location to keep the console log
JBOSS_CONSOLE_LOG="/var/log/keycloak/console.log"

## Additionals args to include in startup
JBOSS_OPTS="-b 127.0.0.1"

Create file name keycloak-init.d with the following content and copy it to /etc/init.d/keycloak

#!/bin/sh
#
# /etc/init.d/keycloak -- startup script for Keycloak
#
# Original written by Jorge Solorzano for /etc/init.d/Wildfly
# Modified for Keycloak by Markus Lehtonen
### BEGIN INIT INFO
# Provides: keycloak
# Required-Start: $remote_fs $network
# Required-Stop: $remote_fs $network
# Should-Start: $named
# Should-Stop: $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Keycloak Application Server
# Description: Provide Keycloak startup/shutdown script
### END INIT INFO

NAME=keycloak
DESC="Keycloak Application Server"
DEFAULT="/etc/default/$NAME"

# Check privileges
if [ `id -u` -ne 0 ]; then
echo "You need root privileges to run this script"
exit 1
fi

# Make sure keycloak is started with system locale
if [ -r /etc/default/locale ]; then
. /etc/default/locale
export LANG
fi

. /lib/lsb/init-functions

if [ -r /etc/default/rcS ]; then
. /etc/default/rcS
fi

# Overwrite settings from default file
if [ -f "$DEFAULT" ]; then
. "$DEFAULT"
fi

# Location of JDK
if [ -n "$JAVA_HOME" ]; then
export JAVA_HOME
fi

# Setup the JVM
if [ -z "$JAVA" ]; then
if [ -n "$JAVA_HOME" ]; then
JAVA="$JAVA_HOME/bin/java"
else
JAVA="java"
fi
fi

# Location of keycloak
if [ -z "$JBOSS_HOME" ]; then
JBOSS_HOME="/opt/keycloak/"
fi
export JBOSS_HOME

# Check if keycloak is installed
if [ ! -f "$JBOSS_HOME/jboss-modules.jar" ]; then
log_failure_msg "$NAME is not installed in \"$JBOSS_HOME\""
exit 1
fi

# Run as keycloak user
# Example of user creation for Debian based:
# adduser --system --group --no-create-home --home $JBOSS_HOME --disabled-login keycloak
if [ -z "$JBOSS_USER" ]; then
JBOSS_USER=keycloak
fi

# Check keycloak user
id $JBOSS_USER > /dev/null 2>&1
if [ $? -ne 0 -o -z "$JBOSS_USER" ]; then
log_failure_msg "User \"$JBOSS_USER\" does not exist..."
exit 1
fi

# Check owner of JBOSS_HOME
if [ ! $(stat -L -c "%U" "$JBOSS_HOME") = $JBOSS_USER ]; then
log_failure_msg "The user \"$JBOSS_USER\" is not owner of \"$JBOSS_HOME\""
exit 1
fi

# Startup mode of keycloak
if [ -z "$JBOSS_MODE" ]; then
JBOSS_MODE=standalone
fi

# Startup mode script
if [ "$JBOSS_MODE" = "standalone" ]; then
JBOSS_SCRIPT="$JBOSS_HOME/bin/standalone.sh"
if [ -z "$JBOSS_CONFIG" ]; then
JBOSS_CONFIG=standalone.xml
fi
else
JBOSS_SCRIPT="$JBOSS_HOME/bin/domain.sh"
if [ -z "$JBOSS_DOMAIN_CONFIG" ]; then
JBOSS_DOMAIN_CONFIG=domain.xml
fi
if [ -z "$JBOSS_HOST_CONFIG" ]; then
JBOSS_HOST_CONFIG=host.xml
fi
fi

# Check startup file
if [ ! -x "$JBOSS_SCRIPT" ]; then
log_failure_msg "$JBOSS_SCRIPT is not an executable!"
exit 1
fi

# Check cli file
JBOSS_CLI="$JBOSS_HOME/bin/jboss-cli.sh"
if [ ! -x "$JBOSS_CLI" ]; then
log_failure_msg "$JBOSS_CLI is not an executable!"
exit 1
fi

# The amount of time to wait for startup
if [ -z "$STARTUP_WAIT" ]; then
STARTUP_WAIT=30
fi

# The amount of time to wait for shutdown
if [ -z "$SHUTDOWN_WAIT" ]; then
SHUTDOWN_WAIT=30
fi

# Location to keep the console log
if [ -z "$JBOSS_CONSOLE_LOG" ]; then
JBOSS_CONSOLE_LOG="/var/log/$NAME/console.log"
fi
export JBOSS_CONSOLE_LOG

# Location to set the pid file
JBOSS_PIDFILE="/var/run/$NAME/$NAME.pid"
export JBOSS_PIDFILE

# Launch keycloak in background
LAUNCH_JBOSS_IN_BACKGROUND=1
export LAUNCH_JBOSS_IN_BACKGROUND

# Helper function to check status of keycloak service
check_status() {
pidofproc -p "$JBOSS_PIDFILE" "$JAVA" >/dev/null 2>&1
}

case "$1" in
start)
log_daemon_msg "Starting $DESC" "$NAME"
check_status
status_start=$?
if [ $status_start -eq 3 ]; then
mkdir -p $(dirname "$JBOSS_PIDFILE")
mkdir -p $(dirname "$JBOSS_CONSOLE_LOG")
chown $JBOSS_USER $(dirname "$JBOSS_PIDFILE") || true
cat /dev/null > "$JBOSS_CONSOLE_LOG"

if [ "$JBOSS_MODE" = "standalone" ]; then
start-stop-daemon --start --user "$JBOSS_USER" \
--chuid "$JBOSS_USER" --chdir "$JBOSS_HOME" --pidfile "$JBOSS_PIDFILE" \
--exec "$JBOSS_SCRIPT" -- -c $JBOSS_CONFIG $JBOSS_OPTS >> "$JBOSS_CONSOLE_LOG" 2>&1 &
else
start-stop-daemon --start --user "$JBOSS_USER" \
--chuid "$JBOSS_USER" --chdir "$JBOSS_HOME" --pidfile "$JBOSS_PIDFILE" \
--exec "$JBOSS_SCRIPT" -- --domain-config=$JBOSS_DOMAIN_CONFIG \
--host-config=$JBOSS_HOST_CONFIG $JBOSS_OPTS >> "$JBOSS_CONSOLE_LOG" 2>&1 &
fi

count=0
launched=0
until [ $count -gt $STARTUP_WAIT ]
do
grep 'WFLYSRV0025:' "$JBOSS_CONSOLE_LOG" > /dev/null
if [ $? -eq 0 ] ; then
launched=1
break
fi
sleep 1
count=$((count + 1));
done

if check_status; then
log_end_msg 0
else
log_end_msg 1
fi

if [ $launched -eq 0 ]; then
log_warning_msg "$DESC hasn't started within the timeout allowed"
log_warning_msg "please review file \"$JBOSS_CONSOLE_LOG\" to see the status of the service"
fi
elif [ $status_start -eq 1 ]; then
log_failure_msg "$DESC is not running but the pid file exists"
exit 1
elif [ $status_start -eq 0 ]; then
log_success_msg "$DESC (already running)"
fi
;;
stop)
check_status
status_stop=$?
if [ $status_stop -eq 0 ]; then
read kpid < "$JBOSS_PIDFILE"
log_daemon_msg "Stopping $DESC" "$NAME"

children_pids=$(pgrep -P $kpid)

start-stop-daemon --stop --quiet --pidfile "$JBOSS_PIDFILE" \
--user "$JBOSS_USER" --retry=TERM/$SHUTDOWN_WAIT/KILL/5 \
>/dev/null 2>&1

if [ $? -eq 2 ]; then
log_failure_msg "$DESC can't be stopped"
exit 1
fi

for child in $children_pids; do
/bin/kill -9 $child >/dev/null 2>&1
done

log_end_msg 0
elif [ $status_stop -eq 1 ]; then
log_action_msg "$DESC is not running but the pid file exists, cleaning up"
rm -f $JBOSS_PIDFILE
elif [ $status_stop -eq 3 ]; then
log_action_msg "$DESC is not running"
fi
;;
restart)
check_status
status_restart=$?
if [ $status_restart -eq 0 ]; then
$0 stop
fi
$0 start
;;
reload|force-reload)
check_status
status_reload=$?
if [ $status_reload -eq 0 ]; then
log_daemon_msg "Reloading $DESC config" "$NAME"

if [ "$JBOSS_MODE" = "standalone" ]; then
RELOAD_CMD=":reload"; else
RELOAD_CMD=":reload-servers"; fi

start-stop-daemon --start --chuid "$JBOSS_USER" \
--exec "$JBOSS_CLI" -- --connect --command=$RELOAD_CMD >/dev/null 2>&1

if [ $? -eq 0 ]; then
log_end_msg 0
else
log_end_msg 1
fi
else
log_failure_msg "$DESC is not running"
fi
;;
status)
check_status
status=$?
if [ $status -eq 0 ]; then
read pid < $JBOSS_PIDFILE
log_action_msg "$DESC is running with pid $pid"
exit 0
elif [ $status -eq 1 ]; then
log_action_msg "$DESC is not running and the pid file exists"
exit 1
elif [ $status -eq 3 ]; then
log_action_msg "$DESC is not running"
exit 3
else
log_action_msg "Unable to determine $NAME status"
exit 4
fi
;;
*)
log_action_msg "Usage: $0 {start|stop|restart|reload|force-reload|status}"
exit 2
;;
esac

exit 0

Set the permissions and enable services

sudo chown root:root /etc/init.d/wildfly
sudo chmod +X /etc/init.d/keycloak
# Enable the new service
update-rc.d keycloak defaults
update-rc.d keycloak enable
# Create logging directory 
sudo mkdir -p /var/log/keycloak
# Create user to run keycloak as 
sudo useradd --system --shell /bin/false keycloak
# Set permissions for the new user
sudo chown -R keycloak /opt/keycloak/
sudo chown -R keycloak /var/log/keycloak/
# Finally, start the service
sudo service keycloak start

Configure Apache to act as a reverse proxy

Configure your site under /etc/apache2/sites-available/yoursite.comf and ssl virtual host the following:

ProxyPreserveHost On
SSLProxyEngine On
SSLProxyCheckPeerCN on
SSLProxyCheckPeerExpire on
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/

If you want to keep admin console accessible only from localhost , you can set the ProxyPass parameter like this:

ProxyPass /auth/realms/ http://127.0.0.1:8080/

The site has to be https in order keycloak to work properly.

By admin

One thought on “Configure Keycloak with Apache Web Server”
  1. Great post! Thanks so much for sharing. I was running into a number of issues with retrieving resources over SSL/non-SSL connections (blocked mixed-content). The main piece I was missing was the first step that tells Keycloak that it is sitting behind a reverse proxy. Made the suggested edits to my standalone.xml and all of my problems are resolved and I have a working application. Thanks again for sharing. I spent days trying to figure this out.

Comments are closed.