Install and Configure Apache Web Server on Linux CentOS 8

John MacLean
10 min readJan 21, 2023

--

Photo by Markus Spiske on Unsplash

The Challenge!

Using CentOS 8:

  1. Update all packages on the server.
  2. Install Apache HTTP Web Server.
  3. Enable the Apache Web Server.
  4. Get the public IP address of the web server and verify it is accessible on the internet.

Optional steps:

5. Create an html page with a custom message.

6. Create and execute a bash script to automate all the above steps.

Pre-requisites

  1. An available vanilla build CentOS 8 Linux server. For simplicity, I am using a server spun up and hosted on A Cloud Guru’s Cloud Playground.
  2. Terminal access to and sudo privileges on your Linux server.

Please note that this process is detailing setting up a simple web server that could be used for test purposes. This would not be suitable for a production environment as it is not secure or resilient.

I did look into setting up https with a self signed certificate, but that would have resulted in an initial security warning when connecting via https, so it didn’t seem worth pursuing for this challenge.

The Script

Needless to say, I created a bash script that would complete all steps in the challenge. Perhaps I went a little overboard, but I wanted to create a site that looked decent, albeit simple. Initially I created custom html and css files, but I streamlined that and achieved a similar effect that I was happy with.

I also wanted to include some basic error handling. This will help me memorise some of the Linux studying I have been doing I hope!

If modifying the script for production, I would include logging to a file as well.

Perhaps the most important point — which I will expand on below — is that the script needs to be run using sudo. Most of the commands require elevated privileges and running the script as sudo will take care of that. I think it’s neater compared to pre-fixing each command line in the script with sudo.

So:

#!/bin/bash

This is the shebang! Essentially we are telling our environment to execute the script using bash.

dnf update -y

dnf is the successor to the yum package manager. yum was considered slow and memory hungry and dnf is more efficient, so I just went with that. yum still works and in practical terms for this exercise, either could be used. This command updates all the packages on the server.

dnf install -y httpd

Install the packages for Apache Web Server. The ‘-y’ option will essentially answer ‘yes’ to any prompts so that the script will continue without interruption.

systemctl enable --now httpd

This command enables and immediately starts the Apache Web Server. httpd is the name of the service. I initially included a separate start command for the service, but that is not necessary. This command will also ensure the service starts automatically following a reboot of the server.

# check the status of the httpd service
httpd_status=$(systemctl is-active httpd)

# check if httpd is running
if [ "$httpd_status" == "active" ]; then
echo "httpd is running. Success!"
else
echo "Error: httpd is not running. Exiting script."
exit 1
fi

This is quite a chunk of code, but it’s really very simple and the comment lines explain the function. However, we check the httpd service is running and assign the status to a variable httpd_status. We then check the status and if everything is good, we send a success message to the screen. If not, the script will end and we need to do some troubleshooting.

I used this same format to perform a status check on the firewall service too as shown below. I thought these were critical components to get running and it would ease any investigations if there were issues.

systemctl enable --now firewalld

We need to have a firewall on the server to manage the network traffic. This command enables and starts the service. It also starts the service immediately and ensures it will start on server reboot.

firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --reload

Next we need to enable the HTTP service so we can browse to our site. The firewall service needs to be restarted to apply the change.

As noted above, we are not enabling HTTPS access for this exercise as it requires certificate setup and installation.

# check the status of the firewall
firewall_status=$(systemctl is-active firewalld)

# check if firewall is running
if [ "$firewall_status" == "active" ]; then
echo "Firewall is running. Success!"
else
echo "Error: Firewall is not running. Exiting script."
exit 1
fi

Again, as with the check on the httpd service, we are doing a similar check to make sure the firewall is active and running. If it is, send a success message to the screen. If not, exit the script and find out why!

# check for an index.html file and make a backup if present
if [ -f "/var/www/html/index.html" ]; then
mv /var/www/html/index.html /var/www/html/index.html.bak
fi

The next couple of steps are optional. But I was experimenting with creating a simple html page and wanted to be sure I could revert to a default one or a prior version in case I messed something up.

wget -O /var/www/html/bg.jpg https://mdbootstrap.com/img/Photos/Horizontal/Nature/full%20page/img%20%283%29.jpg

Get a copy of a pretty picture to use as my website background!

# create a new index.html file and add content
echo "<html>" > /var/www/html/index.html
echo "<head>" >> /var/www/html/index.html
echo "<title>Level Up In Tech - Project 1 - John MacLean</title>" >> /var/www/html/index.html
echo "<style>" >> /var/www/html/index.html
echo "body { background-image: url('bg.jpg'); background-size: cover; text-align: center; }" >> /var/www/html/index.html
echo "</style>" >> /var/www/html/index.html
echo "</head>" >> /var/www/html/index.html
echo "<body>" >> /var/www/html/index.html
echo "<h1>Welcome to LUIT - Gold Team - DevOps - January 2023</h1>" >> /var/www/html/index.html
echo "</body>" >> /var/www/html/index.html
echo "</html>" >> /var/www/html/index.html

We are just sending some basic html formatting and content to a new index.html page. I wonder if there’s a more elegant way to do this? Considering the required site is very basic, it didn’t seem time efficient to investigate other options.

public_ip=$(curl ifconfig.me)
echo "Server's public IP address: $public_ip"

Finally, I wanted to include a little bit of checking to ensure the newly created site was accessible via the internet.

The first step is to assign the public IP address of the server to a variable public_ip. We can send the output to the screen for confirmation.

echo "Checking if web page is accessible..."
http_status=$(curl --write-out %{http_code} --silent --output /dev/null $public_ip)
if [ $http_status -eq 200 ]; then
echo "Web page is accessible. Success!"
else
echo "Web page is not accessible. Error!"
fi

Finally, we use the curl command to query the IP address stored in the public_ip variable and store the status code in another variable called http_status.

If http_status equals 200, that signifies the site can be reached successfully and a corresponding message will be returned.

Just to clarify why we are checking for a status of 200 — this is the standard response for a successful HTTP request.

To demonstrate, if we use the curl -i <server public ip address> command, this returns the HTTP header and page content. So it’s useful for troubleshooting.

The first line output shows the HTTP status code of 200, indicating everything is working and our server successfully serviced the HTTP request. If there were any issues, a different status code would be returned — like 404 for example.

http_status=$(curl --write-out %{http_code} --silent --output /dev/null $public_ip)

That’s the script completed, but I wanted to quickly break down this particular line. I’m not going to lie, getting the syntax correct here took a lot of attempts and research! In the end though, it’s quite straightforward.

We are using the curl command again here.

“ — write-out %{http_code}”: This option tells curl to write the HTTP status code to stdout (standard output — the default resource a program writes its output data to. This would typically be the display screen).

— silent”: This option tells curl to make the request without displaying any progress or error messages.

— output /dev/null”: This option tells curl to send the response from the request to the null device, which discards it instead of displaying it or saving it to a file.

$public_ip”: Of course, this is our variable that contains our servers public IP address.

So we are suppressing all output from our query in order that this step runs silently until the point in the script where we display our custom status message.

The status code returned by curl querying our public ip address will then be assigned to another variable, http_status. This in turn will be checked to determine if our site is accessible over the internet.

Executing the script

First, we create a file using the touch command. As we can see when I list the file, it only has read and write permissions.

I use nano as a text editor and copy and paste the code into the file I created, then save the file and exit nano.

Next we need to change the permissions on the file in order to make it executable. As shown, chmod +x <filename> will do that. We also run chmod using sudo as elevated permissions are required to alter the file.

Listing the file now shows the addition of ‘x’ in its permissions, confirming it’s executable.

Finally, it’s time to run the script: we’re running it directly from the directory we are in using which is why the command starts with the dot notation — ./sudo <script name>.

Again, we need sudo as most of the commands in the script require elevated permissions.

There’s a lot of output text, but we can see httpd is running, the firewall is running and at the bottom, we see the servers public IP address and a status message indicating the web page is accessible.

To confirm, check the site address on an external machine with internet access. As we can see, success — challenge complete!

Lessons Learned

The biggest issues I had were ensuring I was using the most up to date commands and getting permissions correct.

I went down a rabbit hole of using the iptables command to open up port 80 for HTTP traffic until I discovered the firewalld command for example. Linux distributions can change quite a bit over time, so try to ensure you reference recent documentation.

I was also expecting a default index.html to be created by the Apache install. There wasn’t one which threw me a bit and when I tried to create one, my user id did not have permission, even with sudo.

sudo chown -R $USER:$USER /var/www

I changed to root to create my first index.html, but subsequent research and testing has revealed that this command also works.

This recursively changes ownership of the /var/www directory and all sub contents to the current user and the current user’s group. From here, I can create and amend any files as necessary.

Conclusion

This was my first project with Level Up In Tech. I am pleased with how it turned out. Even though it initially seemed straightforward, there were a number of gotchas that had to be overcome and doing a task in a practical manner is the best way of learning how to deal with those.

Please feel free to contact me with any questions or to send me any feedback. I’m always happy to hear from anyone who reaches out.

Full Copy Of The Bash Script

# note - this script needs to be run with elevated privileges
# and is intended for CentOS 8.
#!/bin/bash

# update all packages on the server
dnf update -y

# install apache http web server
dnf install -y httpd

# enable and start apache web server. also ensure it starts automatically after a reboot
systemctl enable --now httpd

# check the status of the httpd service
httpd_status=$(systemctl is-active httpd)

# check if httpd is running
if [ "$httpd_status" == "active" ]; then
echo "httpd is running. Success!"
else
echo "Error: httpd is not running. Exiting script."
exit 1
fi

# enable firewall
systemctl enable --now firewalld

# add http service to firewall and restart to apply change
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --reload

# check the status of the firewall
firewall_status=$(systemctl is-active firewalld)

# check if firewall is running
if [ "$firewall_status" == "active" ]; then
echo "Firewall is running. Success!"
else
echo "Error: Firewall is not running. Exiting script."
exit 1
fi

# check for an index.html file and make a backup if it exists
if [ -f "/var/www/html/index.html" ]; then
mv /var/www/html/index.html /var/www/html/index.html.bak
fi

# download a royalty free landscape image and move it to /var/www/html
wget -O /var/www/html/bg.jpg https://mdbootstrap.com/img/Photos/Horizontal/Nature/full%20page/img%20%283%29.jpg

# create a new index.html file and add content
echo "<html>" > /var/www/html/index.html
echo "<head>" >> /var/www/html/index.html
echo "<title>Level Up In Tech - Project 1 - John MacLean</title>" >> /var/www/html/index.html
echo "<style>" >> /var/www/html/index.html
echo "body { background-image: url('bg.jpg'); background-size: cover; text-align: center; }" >> /var/www/html/index.html
echo "</style>" >> /var/www/html/index.html
echo "</head>" >> /var/www/html/index.html
echo "<body>" >> /var/www/html/index.html
echo "<h1>Welcome to LUIT - Gold Team - DevOps - January 2023</h1>" >> /var/www/html/index.html
echo "</body>" >> /var/www/html/index.html
echo "</html>" >> /var/www/html/index.html

# display server's public IP address and check web page is accessible
public_ip=$(curl ifconfig.me)
echo "Server's public IP address: $public_ip"
echo "Checking if web page is accessible..."
http_status=$(curl --write-out %{http_code} --silent --output /dev/null $public_ip)
if [ $http_status -eq 200 ]; then
echo "Web page is accessible. Success!"
else
echo "Web page is not accessible. Error!"
fi

--

--