Part 3 – DNS Records and Virtual Sites
October 20, 2024
!Series: Local Environment Apache
In this section, we will be adding DNS entries and configuring Apache to serve to simple local websites. These websites will be using static HTML and not PHP–we’ll be adding PHP in the next section.
Localhost
Start Apache using your preferred method. Go to http://localhost to see the “It works” message. How is Apache doing that?
Let’s look at the Apache configuration file, httpd.conf. Look for the section that says Listen 80
. What this means is that when you start Apache, it is listening for any connection that comes in on Port 80 (which is the default port for receiving HTTP requests). In theory, these requests could be from your computer or from an external device. However, by default, the Windows firewall keeps all ports closed to prevent malicious software from accessing your device.[1] This means that in practical terms, the only thing that can make a request to Port 80 is your own device. This is what we want, since we’re setting up a local environment.
That tells us that Apache is listening on Port 80 for any requests. But how is the request being sent?
In Part 1 of this series, we learned that when you try to access a website on your browser, the domain name is translated into an IP address through the process of DNS lookup. Localhost acts like a domain name but it is automatically translated into a special kind of IP address, called a loopback IP address. A loopback IP address is an IP address that refers to the machine it was made from. You can remember this because the request loops back to its point of origin. On Windows, the loopback IP address is 127.0.0.1.
In other words, when you go to http://localhost, your browser automatically sends the request to 127.0.0.1 on port 80. Since Apache is listening on port 80, it catches that request and responds.
Setting Up DNS Records
Since we want to develop two different websites, we will need to access them with different domain names. These sites aren’t actually hosted on the Internet, so we need some way for our browser to be able to perform DNS lookup.
When we learned about DNS lookup in Part 1, we learned that information in the hosts file will trump anything else, so we will use the hosts file to set up our local websites. Open your code editor in administrator mode, and then open the hosts file, which is located at C:\Windows\System32\drivers\etc
.
We want to create two different websites. We can use anything for our domain names, but if you use a real domain name, you won’t be able to visit that website because the hosts file will trump any other domain name records for your computer. We will add two different entries for our websites:
127.0.0.1 test-1.local
127.0.0.1 test-2.local
To be clear, you could use a .com ending on your websites, but using .local makes it extra clear that these are local websites. Save your hosts file.
With Apache running, go to http://test-1.local and http://test-2.local. You should get the same “It works” screen. This is happening because we used the hosts file to add records for those two domains so that when we go to either site, they resolve to the same loopback IP address as localhost. Of course, this isn’t very useful yet because localhost and both of our websites all display the same page. Let’s fix that.
Setting Up Simple Pages
Let’s create a directory in our Development folder that will house all of our pages. There are several common conventions you might see, but I’m going to call the directory www
.
Within this folder, let’s create a folder for each of our local pages. I would recommend keeping it consistent with the domain name of your site, so I’m using test-1 and test-2 as my folder names.
Inside each folder, we need to create an index.html file. This doesn’t need to be complicated, but you should have different content for the one in test-1 and the one in test-2. I’ll just put a header in each file to indicate which site it’s supposed to be. This way we’ll know when we’ve got the right domain name going to the right page.
Index file (index.html
) in C:\Development\www\test-1
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test 1</title>
</head>
<body>
<h1>Hello from test-1.local!</h1>
</body>
</html>
Index file (index.html
) in C:\Development\www\test-2
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Test 2</title>
</head>
<body>
<h1>Hello from test-2.local!</h1>
</body>
</html>
For that, we will need to edit the Apache configuration.
Introduction to Apache Configuration
Before we start editing are configuration file, let’s take a quick tour. Remember, the main Apache configuration file is in the conf
folder in the Apache 24 directory, and it’s called httpd.conf. Apache also has quite good online documentation and I would encourage you to read through it. There are links to some key sections in the Further Reading section below.
Instructions in an Apache configuration file are called directives. Directives can be global, meaning that they apply to the entire server. The directive to listen on port 80 is a global directive. Directives can also be nested inside other directives to limit their scope. For example, anything nested in a Directory directive will only apply to the selected directory.
You can also nest directives inside conditional If
directives so they will only happen if certain conditions are met. For example, you can use the IfModule
directive to enclose directives that should only happen if a particular module is installed.
Apache uses modules to handle more complicated functionality. You can load modules with the LoadModule
directive. Some modules are already being loaded by default in the default configuration.
And finally, you can include additional files with the Include
directive. All of the included files are commented out right now, but they’re available if we need them.
With that in mind, we have the information we need to configure Apache to run our two simple sites.
Main Configuration Tweaks
Let’s recap what we want to do. When we type localhost into our browser, we want it to show the fallback “It works” page. When we visit http://test-1.local, we want it to show the HTML file in the test-1
folder. When we visit http://test-2.local, we want it to show the HTML file in the test-2
folder.
In order to do this, we are going to set up some virtual hosts.
Let’s start with a few tweaks in our main configuration file (httpd.conf).
First, let’s explicitly specify our server name. This will act as the fallback if none of our virtual hosts are reached and it will also get rid of that error we’ve been seeing what we start our server. In the configuration file, add the line:
ServerName localhost
We’ll also define a variable that we can use to refer to the folder where we’re saving our website files, which in my case is:
Define DOCROOT "c:/Development/www"
Finally, we will keep the configuration for our virtual hosts in a separate file to keep things separated and modular. Find the “virtual hosts” section of the main configuration file, and uncomment the line:
Include conf/extra/httpd-vhosts.conf
Setting Up Virtual Hosts
Now that we’ve done that, the virtual hosts configuration file will be included. Let’s open that file. It’s called httpd-vhosts.conf and should be in C:\Development\Apache24\conf\extra
.
The virtual hosts configuration file includes two example virtual hosts, but we don’t want either of them so let’s comment them out.
Each virtual host will represent one site, so we’ll need one for the default condition (visiting localhost), one for test-1, and one for test-2. Each virtual host needs two things. First, we need to specify what port the virtual host is going to listen on. In this case, it’s going to be Port 80, since that’s where the server is listening.
Next, we need to add the server name. This needs to match the host header that comes in with the request, as we talked about back in Part 1. The host header is how Apache will know to dispatch a request to a particular virtual host. If there are multiple virtual hosts that match a request, then Apache will simply use the first match.
Let’s start by handling our default condition. Add this to the httpd-vhosts.conf file:
<VirtualHost *:80>
ServerName localhost
</VirtualHost>
As a recap, we’re enclosing everything in a VirtualHost
directive, which tells Apache that everything inside it applies only to a specific host. We’re telling the virtual host to listen on Port 80 (*:80
is a wildcard that means any IP address that ends with :80). Finally, we’re saying that this virtual host should apply to requests that come in with localhost
in the host header.
Now the real magic happens. We’re going to specify a document root for each virtual host. This is going to determine the directory Apache will use to serve files for this host. Since we already created a variable to hold the path to our www folder, the directives look like this:
<VirtualHost *:80>
ServerName localhost
</VirtualHost>
<VirtualHost *:80>
ServerName test-1.local
DocumentRoot "${DOCROOT}/test-1"
</VirtualHost>
<VirtualHost *:80>
ServerName test-2.local
DocumentRoot "${DOCROOT}/test-2"
</VirtualHost>
Let’s see what that did. Restart your server and try visiting each site. (Note that you’ll probably need to do a hard refresh, sometimes a couple of times.) You should see the “It works” page at localhost, but the other pages show you a message that says “Forbidden”. We’ve done something–but we haven’t quite succeeded yet.
In order to view the page, we need to grant permissions for the client (our browser) to actually see the files. We need to add some permissions to the directory. Update your directives to the below
<VirtualHost *:80>
ServerName localhost
</VirtualHost>
<VirtualHost *:80>
ServerName test-1.local
DocumentRoot "${DOCROOT}/test-1"
<Directory "${DOCROOT}/test-1/">
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:80>
ServerName test-2.local
DocumentRoot "${DOCROOT}/test-2"
<Directory "${DOCROOT}/test-2/">
Require all granted
</Directory>
</VirtualHost>
With this finished code, we can walk through what Apache does when a user visits http://test-1.local:
- Receive a request on Port 80 with a host header of test-1.local
- Process the request through the httpd.conf file, which will also pull in the contents of httpd-vhosts.conf (because of the
Include
directive) - Look for a match in the
VirtualHost
directives - Check first virtual host
- Is it coming in on Port 80? YES
- Is the host header ‘localhost’? NO – Not a match
- Check second virtual host
- Is it coming in on Port 80? YES
- Is the host header ‘test-1.local’? YES – Match found
- Since a match was found, process the remaining directives inside the matching
VirtualHost
directive - Serve files from the specified directory (
DocumentRoot
directive) - Grant permissions for the user to view all files within the specified directory (
Directory
directive)
Restart Apache, and our first virtual hosts should all be working. You should see the index pages from your test-1 and test-2 directories when you visit those sites.
Conclusion
That’s it for this section! In the next section, we’ll be adding PHP to each of our virtual hosts.
Further Reading
- More information about ports
- How-To Geek: “How to Check Open TCP/IP Ports in Windows”
- StackOverflow question about using Port 80
- Microsoft documentation about netstat command
- iTechTips: “How to Check if Windows Firewall is Blocking Ports” (includes information about viewing Windows Firewall settings)
- More information about Apache configuration
Notes
[1] To be more specific, Windows blocks inbound requests by default and then adds exceptions for apps and programs as needed. In addition, Windows may assign a specific port to a specific app, or it might allow requests to come in on any available port.
When you installed Apache, a firewall exception was added to allow Apache to receive requests. By default, the firewall exception lets Apache listen on any port. However, the Listen
directive(s) in httpd.conf determine what ports it’s actually listening to. You can look at the firewall exception by following these directions to look at the “Inbound Rules” section of the Windows Defender Firewall.
Essentially, when you visit http://localhost, the hosts file resolves it to back to the local machine on Port 80, and since Apache is listening on that port, it responds to the request.
Posted In: Tutorials