Set up your own web server and host your sites virtually for free

How to host your website on an old Mac mini with SSL

Roland Treiber
Geek Culture

--

Hello there. For a while I contemplated the idea of hosting my website on my home server. Among the reasons were curiosity, ease of making changes in the environment setup, full access and the learning experience.

The hardware

For this experiment, I have used an old Mac mini specifically to show you that it does not require cutting edge tech. I have bought this machine on Facebook Marketplace for a whopping £30(!) that was probably my luckiest purchase so far considering the specs.

2009 Mac Mini with SSD, 2.66Ghz Core 2 Duo cpu and 16Gb of ram for £30

It is a 2009 machine that has a 256Gb SSD and 16gb of ram and a 2.66Ghz Core 2 Duo processor. More than enough to run Apache and serve a simple React single page application!

2009 mac mini from Facebook Marketplace… Perfect!

Things to consider about the website

So at this point I was ready to create the website. Not every websites are created equal though. It is important to mention that high traffic and resource intensive sites are probably not the best candidate for this type of hosting solution.

In the other hand, it works out perfectly for any of the following:

  • personal website or portfolio
  • promotional site
  • survey
  • closed access web application

That said, I have decided to host my own personal website this way.

The reasons:

  • I am expecting moderate amount of traffic
  • It is a static React application

To access my site directly on my old Mac Mini in my living room, browse: https://thecaringdeveloper.com

Preparations

Creating the website

First off, I would advise against hosting a WordPress website at home, however it is entirely possible. Wordpress has a hybrid way of storing the page data. Even though you may create static template parts with hard-coded parts of things almost never change, there are still plenty of data that is loaded from the database. So every browser request would trigger a bunch of expensive php logic and database query before page loads. This type of a web application needs some juice to work fast. It will not be terrible, but you can achieve better results going for a cheap shared hosting.

So my website is static. It means that the pages are self-contained, and so do not load data from the database. It does not actually have any database connection. If you are not familiar with React: the way it works is that you create your site on your machine, then build it. At the build phase the code will be minified and optimised heavily, so your code is ready to be put to work.

The design

The work started with actually planning and designing the content of the site. I used Adobe XD for this. For someone having a design background, it is a relatively easy task. It only took me one morning. To get your website design ready, I would recommend starting with the content. Take a blank sheet of (Google docs) paper and pour onto it what you have to say to people visiting your website. It is easier said than done of course, however the ease you can do it is a strong indicator of how clear your message is.

I used Adobe XD to create the website design

The coding

As I mentioned, I have used React for the coding. It would go way out of scope to talk much about how to do it, so I keep it very short:

Code it up and compile it :)

At this point you have the application ready. I would highly recommend to store the codebase in Github, however it is not absolutely essential. You may simple copy it over to your server.

In my case, I have set up a repo to make it easier keeping track of changes.

Head back to your server

At this point I have had my site code ready waiting to be deployed. Let’s see what steps are required for the site to be served in your browser.

In the following sections, I’ll go into detail of the following steps:

  • Opening up port 80 and 443 in the router settings
  • Installing brew
  • Installing Apache
  • Configuring the domain DNS
  • Setting up the site to be served by Apache
  • Getting an SSL certificate
  • Installing the SSL certificate for the site
  • Optimisations

Opening up port 80 and 443 in the router settings

I am a Virgin Media customer, so my router can be accessed on 192.168.0.1 on the local network. Check what ip your router uses. If it is provided by your ISP, this information can be found on their website.

Access your router settings (most common ip-s are 192.168.0.1 or 192.168.1.1)

What we are interested in, is the port forwarding.

When you type a url into the address bar in your browser, you access the server behind the domain on port 80 or 443 without specifying the port. (80 for http and 443 for https)

If you try any other port, the server will most likely not respond at all.
This will hang and eventually timeout: http://thecaringdeveloper.com:412

Typing your own public ip address into your browser will try to access your router or modem on port 80. It depends on the settings what happens. By default, not much.

The magic starts when your router is configured to forward a port to whatever is listening to on your machine.

Let’s elaborate on what is exactly happening here at this stage:

  • A request comes in on port 80
  • Your router says: Ok, so anything coming in on port 80 should be forwarded to port 80 on the local network.
  • Is there anyone listening there? Apache? Nginx? Are you guys there to pick up the phone and respond? Alright then fine. — > error

Let’s install Apache (but before we do it, let’s install homebrew)

As my machine is wiped and not used for anything else (apart from being attached to a projector and netflix-ing occasionally), it does not yet have had the necessary packages to to the job.

If you’re on the Mac, your go-to tool for package management is Homebrew.

It takes a while and when done, I was ready to install Apache.

Use this command:

brew install httpd

You may read this article to troubleshoot if something goes wrong and deactivating the default apache installation if your system has one.

Once it finished and you can confirm that it is working, you can test it out by typing your public ip address in your address bar. You should see “It works!” (or something similar other than a 500 error)

It is not time to celebrate yet, but indeed port 80 is being forwarded and Apache is responding ok on the other end. Halfway there :)

Configuring the domain DNS

Point your domain to your public ip

Your public ip is where your machine can be contacted on the internet. It is much like a phone number. To make it easier for the users, it can be replaced by a human readable address such as google.com. What it’ll do, is to route your call to the appropriate ip.

So in this case, my next logical step was to buy a domain. You can buy a domain on many websites. I chose Godaddy for simplicity.

Once my domain purchase was complete, I changed the ip it points to, to my public ip, so when someone types this url, it will end up on port 80 at my router and be forwarded to Apache to respond.

It takes some time for the DNS settings to update, so it was my perect opportunity to have a coffee and breakfast with my family. By the time I finished my toast and eggs, I could see “It works!” when typing http://thecaringdeveloper.com in my browser on any network or device.

Setting up the site to be served by Apache

Ok, so far it was easy wasn’t it? Actually finding the right machine online took me the most effort. Design and coding was fun and all else was installing software and fiddling with settings. :)

The way Apache works is that you need to specify everything through config files.

What we are interested in right now is to make it listen to port 80 (insecure connection) and serve the website it is told to.

The first file I have had to look at is :
/private/etc/apache2/https-vhosts.conf. The location of these files may differ due to your system and version.

As the name suggests, this file is the home of vhosts (Virtual Hosts), so it is time to add one.

<VirtualHost *:80>
ServerName thecaringdeveloper.com
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / http://localhost:8082/
ProxyPassReverse / http://localhost:8082/
ErrorLog "/private/var/log/apache2/[YOUR SITE]-error_log"
CustomLog "/private/var/log/apache2/[YOUR SITE]-access_log"
</VirtualHost>

A note about reverse proxy

As I am looking to host other websites on the same machine (or at least keep the possibility open), so I need a way to let Apache know that different files should be served based on the domain.

To solve this, we have reverse proxy. Simply put, it preserves the request host and forwards the call to localhost on any port you specify.

As this happens internally, this port does not need to be open to the world.

So some additional configuration is required so my site is served on port 8082 on localhost.

To achieve this, I created the following configuration file:

$ /private/etc/apache2/extra/thecaringdeveloper.conf

To use this file, I had to include it in the https.conf file by adding the following line:

Include /private/etc/apache2/extra/thecaringdeveloper.conf

The actual config holds the details of what to serve in the browser. In my case it looks like this:

Listen 8082
<VirtualHost *:8082>
DocumentRoot "[YOUR SITE DIRECTORY]"
<Directory [YOUR SITE DIRECTORY]>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

To observe changes, Apache needs to be restarted.

To confirm that my config works as expected, I tried to access the site on port 8082 on the machine locally.

Once the files are on the server, having the virtual host in place, I could access the site locally on port 8082

Better yet, having the domain pointed to my ip, I was also able to access the site on the domain!

It is available but not secure :(

Getting an SSL certificate

As you can see, there is an issue with the site: The lack of the padlock or lack of the lock or locklack… All right all right it is a slippery slope on jokes, so let’s stop it right here. :)

Generate a CSR

openssl req -newkey rsa:2048 -keyout thecaringdeveloper.key -out thecaringdeveloper.csr

I have used the command above to generate a Certificate Signing Request (CSR) and a key file for it. It will ask a few questions. You should answer accurately.

If everything goes well, it will save a new file in your system with the specified name.

Purchase an SSL certificate

In my case, I will never process payments through my brochure website, so all I am interested in is the padlock. Therefore I went with the cheapest and simplest option, that in my opinion is GoGetSSL.

Once you are done with the steps of buying a certificate, you will be asked to complete your certificate by entering a CSR.

Take the content of the .csr file you have created in the previous step and copy it in the appropriate box.

The certificate needs then be validated. There are a few options to do this. I chose http as I knew that the site works through http and I can easily copy a file in its folder.

The http validation is simply checking that a file they give you can be accessed on the server.

In my case, this is what the validation looked like. It may be different for you.

Once the validation was successful, I have received an email, so I was ready to install the certificate.

There are 3 files that are needed to achieve the task:

  • The key file
  • The certificate
  • The CA Bundle file

The key file was generated at the same time when the CSR, so you can find it next to it. It has the .key extension. The other two are in the email.

Installing the SSL certificate for the site

I have created 3 empty files to get started. The location is entirely up to you. I like to keep them in my Project folder.

The files I needed to install the certificate.

In the next step I copied the content over from the appropriate files.

The SSL Apache config

So far my website is served just fine over http, so the config I have in place for port 80 is valid for sure.

What is needed now is another config that would do essentially the same but also uses Apache’s SSL engine and uses my certificate files.

So I have added a new virtual host into the appropriately named httpd-ssl.conf in the /etc/apache2/extra folder.

There are predefined config files for a range of use cases. You can also create your own ones.

My config file looks like this:

<VirtualHost *:443>
ServerName https://thecaringdeveloper.com
DocumentRoot "[LOCATION OF THE WEBSITE]"
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
SSLEngine on
SSLProxyEngine On
ProxyPreserveHost On
SSLProxyVerify none
ProxyRequests Off
SSLCertificateFile [PATH TO THE CERTIFICATE FILE]
SSLCertificateKeyFile [PATH TO THE CERTIFICATE KEY FILE]
SSLCertificateChainFile [PATH TO THE CA BUNDLE FILE]
ProxyPass / http://localhost:8082/
ProxyPassReverse / http://localhost:8082/
RequestHeader set X-Forwarded-Proto "https"
RequestHeader set X-Forwarded-Port "443"
ErrorLog "/private/var/log/apache2/thecaringdeveloper-ssl-error_log"
CustomLog "/private/var/log/apache2/thecaringdeveloper-ssl-access_log" common
<Directory [LOCATION OF THE WEBSITE]>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>

After restarting Apache, I could observe the site loading through https! Yay, is it time to celebrate yet? Well almost, but no :)

Optimisations

The site actually loads over http and https as well. That is not very professional I would say, so let’s make sure it always loads over https and so “Not secure” is never displayed to the user.

Do not edit .htaccess!

I have made the mistake of going into this bit before doing proper research, so here is where I may save you some time hopefully.

If you try to force https in your .htaccess file, it will result in a redirect loop. Why? Because the site request is internally redirected to http://localhost:8082 in my case. Notice the http there. The ssl certificate works for specific domains and cannot be installed therefore to localhost. The .htaccess is triggered when hitting the actual files in the directory, so it will try to redirect to https://localhost:8082 and will fail. (I am not 100% sure that this is exactly what happens, please correct me if I am wrong. This is how I understood by the symptoms and logs. It makes not much difference if it fails somewhat differently as failure is failure.)

So the solution is to add the redirection to the virtual host in the httpd.conf file as it is the first point of contact. As soon as Apache is presented with the request, it should know that it needs to be redirected to https.

The extra lines to add after the DocumentRoot:

RewriteEngine On
RewriteCond %{SERVER_PORT} !443
RewriteRule ^/(.*) https://%{HTTP_HOST}/$1 [R=301,L]

You may also remove what is after it, but I would advise against it. If you ever need to serve the site over http again temporarily for whatever reason (most common is a new cert due to be validated over http), you simply comment those 3 lines out and you’re good to go.

Conclusion

There you go! You have it! This is how you can set up your simple brochure website with SSL certificate on an old Mac Mini.

To do the same on a linux machine or a raspberry pi (!) is not too different. Give it a try! I am really pleased with this setup because I can add more sites should I want to as well as the machine itself is quiet and blends in with the rest of the interior.

If you have questions, please feel free to reach out on hello@thecaringdeveloper.com

Have a nice day and have fun with your own home web server!

--

--

Roland Treiber
Geek Culture

Hello. I am The Caring Developer. Web and mobile application developer, specialised in e-commerce, Laravel, React and React Native. I am naturally curious. :)