Hardware SetupYou'll need some hardware, and fortunately, a personal Web server doesn't require a lot of juice. You can cobble together a server out of spare parts and it will almost certainly be enough to do the job. If you're starting from scratch, consider something like an E-350-powered Foxconn NTA350. Coupled with 4GB of RAM and a 64GB SSD, you can get rolling for about $270. There are cheaper options, too, but I used just such a setup for more than a year and I can attest to its suitability.
If you're cannibalizing or cobbling, you really don't need much. We're going to be using a Linux server distro as our server operating system, so the hardware can be minimal. An old Core 2 Duo or Pentium box gathering dust in the corner should work fine. You don't need more than 1GB of RAM, and in fact 512MB would work without issue. Ten gigabytes of storage is more than you'll ever fill unless you're going to use the server for lots of other stuff as well, so a creaky old hard drive is fine. As long as you can install your Linux distro of choice on it, it will work without issue.
Don't Have Hardware Available Fear Not Setup With a Virtual Machine :
If you don't have hardware available or you don't want yet another computer clogging up your closet, fear not. For home use, a virtual machine works perfectly well. In fact, a VM is exactly what you'd be issued if you go with just about any hosting provider on the planet, unless you pony up some serious dollars to have your own dedicated server. Having your own physical machine is nice, but it's not always practical. Feel free to follow along at home inside a VM.
If you don't already own a desktop virtualization product of some sort (VMware Workstation for Windows, or VMware Fusion or Parallels for OS X), there are free alternatives: VMware VSphere is full-featured and rich, but it requires you to dedicate an entire computer as a virtualization host. The company's older standalone product, VMware Server, is still available but rapidly approaching its end-of-life for support. Windows 8 and Windows Server 2012 come with a built-in hypervisor, but you need to purchase the operating systems. There's also a standalone product, Hyper-V Server, but like VSphere it requires you to dedicate a whole computer to virtualization.
The least-complex, free solution is to download and install VirtualBox. That will run on an existing Windows or OS X or Linux host and will let you run a virtualized Linux server with a minimum of fuss. I won't go through the steps of downloading and installing a virtualization solution, but it's not terribly hard.
Operating SystemI've already given away the operating system choice a couple of times: the correct operating system for building a Web server is Linux or BSD. It's as simple as that. Windows Server is the correct tool for many things (particularly with Active Directory, which frankly is peerless for managing accounts, objects, and policies—OpenDirectory and other competitors are just laughably bad at scale) but building a Windows-based Web server is like bringing a blunt butter knife to a gunfight. The Internet and the services that make it run are fundamentally Unix-grown and Unix-oriented. Playing in this playground means you need a Linux or a BSD server .
So, Linux or BSD? That choice is probably an entire article in and of itself, but I'll keep it short: I'll be talking about using a Linux distro (that is, a Unix-style operating system composed of the Linux kernel and a curated collection of tools and packages) instead of a BSD variant (that is, a Unix-style operating system composed of a unified base system and tools and packages). There are a number of reasons for choosing to go with a Linux distro over a BSD variant but the most relevant factor is that Linux distros will be easier to install because of broader, better hardware support.
Quick OS InstallWhether you go physical or virtual, getting your Linux server stood up and ready to transform into a Web server is easy. Accept all of the standard Ubuntu installation options (or change the ones you feel you need to change) until you get to the "Software Selection" page. There's an option here titled "LAMP server," which will have Ubuntu automatically download and configure Apache, PHP, and MySQL. This is a great way to have a ready-made Web server, but we're not going to use this option because we're not going to use Apache.
Instead, simply select "OpenSSH server" from the list and nothing else. This will get us set up for easy remote administration. Why aren't we choosing to install Apache? Read on!
Our chosen OS is installed, and so what shall we pick for our Web server? There are two main choices: Apache, the flexible and powerful open-source Web server which powers the vast majority of sites on the Internet, or Nginx (pronounced "engine-ex"), the not-as-flexible but far more powerful open-source Web server which runs the second-largest chunk of sites on the Internet (IIS dropped to third place behind Nginx in 2012).The Web server we're going to select here is Nginx.
Why Nginx over Apache? Again, such a comparison could warrant an entire article, but Nginx will typically be faster than Apache in servicing requests and will use fewer resources when doing so. The most common version of Apache is called Apache MPM prefork, which trades speed for greater compatibility with the galaxy of Apache add-on modules that have grown up over the years. Apache prefork is non-threaded, handling Web requests with whole processes instead of spinning off threads as needed; under sufficient load, Apache prefork can use tremendous amounts of RAM and can become extremely inefficient at handling IO. There are other versions of Apache available, like MPM worker and MPM event, both of which use more efficient methods to service IO requests, but neither is as successful at doing so as Nginx.
Nginx is an entirely event-driven asynchronous Web server, originally designed and written by a Russian dev team to power some very large Russian websites. Unlike Apache prefork, where each HTTP connection to the Web server is handled by a separate process, Nginx uses a small number of single-threaded worker processes to grab the next bit of IO activity without regard to HTTP connection count. It's also important to note that Nginx doesn't utilize a thread pool—individual HTTP connections don't get assigned their own threads. Rather, each Nginx worker process runs in a tight and efficient event handling loop, watching a listen socket and grabbing the next discrete task presented on that socket. Process- or thread-based architectures like Apache prefork can spend large amounts of time with their processes or threads sitting idle, waiting for operations to complete, because the entire operating is assigned to that process or thread. Nginx's worker processes, by contrast, will busy themselves handling other tiny events until the slow request is complete, then pick up its results and deliver it.
Nginx lacks Apache's truly gargantuan ecosystem of add-on modules, but its efficiency and speed can't be beat. Chris Lea put it most succinctly when he said, "Apache is like Microsoft Word, it has a million options but you only need six. Nginx does those six things, and it does five of them 50 times faster than Apache."
We installed the "SSH server" option in order to make it easy to log
into your server from your desktop. If you're using a virtual machine
hosted on your main computer then it doesn't make much of a difference
but if you're using physical hardware, you can leave the computer in the
closet and do the rest of this from your desk and comfy chair. To log
in to your server via ssh, open a terminal window and type
substituting your user account for "yourname" and the server's name or
IP address for "server." Windows users who don't have a native ssh
client can use PuTTY; alternately, you can install cygwin and bring a small slice of sanity to your computing environment.
Installing NginxThe version of Nginx available in Ubuntu 12.04's repositories is outdated, so we'll need to add a repository to Ubuntu in order to get the latest version. To do that, we need to install the
add-apt-repositorytool. Now that you're logged onto your Ubuntu server or virtual machine, spawn a root-privileged shell by typing the following:
sudo /bin/bashYou'll notice the prompt change from a "$" to a "#", indicating that your commands will be executed with root privilege instead of as a standard user:
Then type the following two commands:
aptitude update aptitude install python-software-propertiesThe first command will run out and touch all of the repositories Ubuntu is currently configured to use and check for any updated software, and the second will install the
After it runs, it's probably also a good idea to let
aptituderun a regular update, too, since lots of the default installed Ubuntu packages probably have updates:
aptitude upgrade(As a quick aside, I'm using
aptitudeinstead of the more common
apt-get, and so should you. The
aptitudecommand is installed by default in Ubuntu Server. It's just plain better.)
Once you've installed
add-apt-repository, we need to use it to add the Nginx repository to Ubuntu's sources list so that we can install the current version of Nginx from it:
add-apt-repository ppa:nginx/developmentThen, update your
aptitudesources list and install Nginx!
aptitude update aptitude install nginx
If all you want to do is serve some static pages to your LAN, you can probably stop here. Nginx is up with a basic configuration in place and all of your website's files are located in
/usr/share/nginx/html. You can edit the default
index.htmlfile or drop your own stuff in there and it will be instantly visible.
However, the base configuration really does need some modification in order to be tuned for your environment, and there are also a number of security tweaks we need to make, both to Nginx and to the server itself. To get started on configuring things, head to
/etc/nginxand take a look at the files and directories there in.
Your WebsiteNow let's look at the actual website settings. The default website's settings are stored in a file named, appropriately enough,
default. It's probably a good idea to change this and leave the default file for reference, so let's take care of that right now.
Your Nginx install can support far more than a single website and the files that define your server's sites live in the
/etc/nginx/sites-availabledirectory. However, the files in this directory aren't "live"—you can have as many site definition files in here as you want but Nginx won't actually do anything with them unless they're symlinked into the
/etc/nginx/sites-enableddirectory (you could also copy them there, but symlinking ensures there's only one copy of each file to keep track of). This gives you a method to quickly put websites online and take them offline without having to actually delete any files—when you're ready for a site to go online, symlink it into
sites-enabledand restart Nginx.
These files are called "virtual host" files, the same as they are in Apache, because each of them defines a website that acts as if it were running on its own server (or host). To start with, we're going create a copy of the default site and then customize it, so head to the
sites-availabledirectory. It's a common practice to have each virtual host file named the same as the website it represents; for now, because this will be our only website, we can simply name the default file to "www" (though you can call it whatever you'd like).
cd /etc/nginx/sites-available cp default wwwNext, we need to activate our
wwwvirtual host file, which is done by creating a symbolic link for it in the
sites-enableddirectory. At the same time, we're going to deactivate the
defaultvirtual host by deleting its symbolic link:
cd /etc/nginx/sites-enabled rm default ln -s /etc/nginx/sites-available/www /etc/nginx/sites-enabled/wwwIf you check the contents of the
sites-enableddirectory, you should see only the
wwwsymbolic link, which points back to its original location in
Finally, tell Nginx to reload its configuration files to enact the changes you've made. This must be done after changing any configuration files, including
nginx.conflike we did above. The command to reload Nginx is:
/etc/init.d/nginx reloadNow that we've stashed the
defaultfile for safekeeping and switched to using the
wwwvirtual host file instead, let's take a look inside it
The "server" declaration indicates this file defines a specific virtual host. The
rootdirective indicates the actual path on your hard drive where this virtual host's assets (HTML, images, CSS, and so on) are located, and here it's set to
/usr/share/nginx/html. If you'd rather use a different location, you can add that here. The
indexdirective tells Nginx what file or files to serve when it's asked to display a directory. Here, Nginx is told to first try to show that directory's
index.htmlfile, and if that file doesn't exist, try instead to show
index.htm. You can add additional files here, too, like
index.phpif your site is using PHP. If none of the files listed are found, Nginx will either reply with a listing of all the files in that directory (if configured to do so) or with an error.
server_namedirective is a key setting. When you have multiple virtual hosts running on one server,
server_nameis how Nginx knows which site to serve up. If you had your
wwwvirtual host file and another virtual host file named, say,
forum(because maybe you're hosting a Web forum), you could set the
wwwvirtual host as the virtual host that gets loaded when users request
forumvirtual host as the one that gets served when a user requests
forum.yoursite.com, with Nginx keying off of the server name in the requested URL. For now, we can leave this alone.
Below this are "locations," which are the things your users are going to be interested in accessing. Defining locations gives you a way to apply different settings to different paths on the server. Right now, there are two locations: one at "/", called the "root location," and one at
/doc/, which points at
That useless location is now gone, so take a look back at the root location. The only directive it contains is a
try_filesdirective, which tells Nginx what to do for every incoming requests. As configured, the directive says that Nginx should take each incoming request and try to match it to a file with that exact name. If it doesn't find anything to then try to match it to a directory with that name, this directive tells it to finally just serve up the index page in response. So, a request to
http://yourserver/blahwould first check to see if the root location contained a file named "blah." If it didn't, it would then see if the root location contained a directory named "blah/", and if it didn't, it would then just redirect the user back to the index file. Go ahead and try it—tag a nonexistent file or directory name onto the end of your browser's URL bar and watch your Web server reply with its front root index page.
Meeting The Big Bad Internet
Your Web server works great at this point, but it's not reachable from outside your LAN—exposing it to the Internet will require you to do some configuration on your NAT router and/or your LAN's firewall.
However, you need to carefully consider what you want to expose. To actually let folks outside your LAN use your Web server requires only a single opening—if you have a NAT router, like most home users, you need to forward TCP port 80 to your Web server. Some ISPs block port incoming requests on TCP port 80 for their residential customers; if that's the case for you, pick another high-order port, like 8080 or 8088, and forward that to TCP port 80 on your Web server.
Don't forward any ports you don't have a reason to forward. It might be tempting to use the "DMZ host" function in your NAT router to open all of its ports to the Internet, but this is a terrible idea. It robs your host of much of the protection from attack it gains by being behind a NAT router.
There are a couple of additional ports you might consider opening up besides TCP port 80. If you're going to use SSL/TLS encryption for serving things via HTTPS, you should also forward TCP port 443 to your Web server. If you want to be able to connect via ssh from the Internet, you should forward TCP port 22. However, if you're going to do this, make sure to take the appropriate precautions (which we'll get to in just a moment).
Safety And SecuritySo far we've focused on getting you up and running, and now that's done. Having a functional Web server is one thing; now we need to secure it.
Most of the intrusions and security breaches that happen on Web servers don't come from vulnerabilities in the Web server but rather from flaws in the add-on software. So far, the only thing we have installed is Nginx itself. Assuming that only port 80 is exposed through your firewall to the Internet, there just aren't that many things a potential attacker can latch onto and break. However, there are several adjustments we can make in the configuration file and in the virtual host files to harden the site.
The first setting will stop Nginx from sending identifying information when it serves up error pages. By default, Nginx will include its version number on error pages. Hiding this gives potential attackers less information about your server.
You may already have a
keepalive_timeoutsetting; if so, replace it with this one. Each of these settings will alter a default bit of Nginx's behavior in order to make the site less vulnerable to distributed denial of service attacks.
Client_max_body_sizelimits the maximum size of an uploaded file;
client_body_timeoutset the maximum amount of time Nginx will wait around on the client to specify a request header or ask for an object to be served;
keepalive_timeoutspecifies that Nginx should hold open a
keep-aliveconnection for no more than 10 seconds and also suggests to the client that it should close its connections after the same interval; and
send_timeouttells Nginx to close its connection to a client if that client takes too long between successive requests.
These settings will limit the amount of resources Nginx spends responding to slow requests, so that it's considerably more difficult for an attacker to tie up the server's resources. Nginx's architecture makes it less vulnerable to this kind of denial of service attack than other Web servers, but it's perfectly possible to exhaust Nginx's resources with enough attacking machines. This will help mitigate that risk. The settings here should more than suffice for a personal site, though they can be tweaked as needed.
The virtual host files in
/etc/nginx/sites-available/need a bit of love, too. Even if you're planning on opening your Web server up to the Internet, you might want to keep some or all of a site only accessible on your LAN while you're testing. You can use Nginx's location directives to limit access.
/etc/nginx/sites-available/wwwfor editing and locate the root location directive.To lock this down so that the entire site is visible only on your LAN and not over the Internet, modify it .
If you're using something other than 192.168.1.0/24 for your LAN, then replace the netblock after the first
allowwith your LAN's IP address range. These
denydirectives ensure that any request from non-LAN and non-local IP addresses for anything on the server will be denied.
You can lock down individual directories or files, too—if you had a subdirectory called
personalwhich you only wanted to be visible on your LAN, you could create a location directive just for that directory.
Locations inherit properties from their parents, though, so the settings you apply to the root location affect all locations beneath it. However, if you wanted just the
personaldirectory to be LAN-only, this is how you'd do it.
Locations are very powerful and flexible in Nginx because you don't have to specify them explicitly—rather, Nginx lets you use regular expressions to define locations, which gives you a huge amount of configurability. A location doesn't have to be a directory or a file—anything that matches a regex can be a location. For example, if you are editing files in your Web root, your text editor might be keeping its temporary files in the Web root, and Nginx will happily serve those files up when asked unless it's told it shouldn't. Temporary files usually start with a dot or a dollar-sign, and so you can create the following two locations to make sure that Nginx never serves any files starting with either of those characters .
This also ensures that attempts to access them aren't logged in Nginx's main and error log files.
There are a lot of location-based tweaks you can apply to your site, but these should give you a good basis for getting started.
Stepping Out: Secure Remote AccessThe last thing we're going to cover is secure remote administration. There is great value in being able to ssh into your Web server from outside of your LAN, but it also carries with it some danger, since exposing your ssh port gives potential attackers a well-known hole to go after. However, there are many things you can do to leave ssh open for your use and at the same time minimize risk.
Key Based AuthenticationUsing key-based authentication for ssh means that an attacker needs more than just your username and password to log onto your server via ssh—he also needs a special cryptographic key. Linux and OS X can directly generate the needed keys. However, Windows users are again hobbled by their platform's lack of appropriate tools and will need to use a third-party utility like puttygen, or borrow a Linux or OS X machine temporarily.
Key-based authentication can greatly enhance a system's security, though it's certainly not fool-proof. It's not immune to man-in-the-middle attacks and more significantly, if someone steals the computer containing the private key, they gain the ability to log on as you unless you've protected your private key with a password. In general, ssh key authentication is more secure than just using a password, but you'll be most secure by protecting your key with a passphrase.
The Ubuntu documentation site has an excellent and easy-to-follow HOWTO on enabling key-based authentication right here.
No Remote Root Logon
Whether you're using key-based logons or sticking with regular passwords, you should carefully limit which accounts are allowed to log in via ssh. An exposed ssh port will rack up hundreds (if not thousands) of logon attempts in a given day from folks eager to kick down your virtual door, and most of those attempts will use common account names—like "root." Fortunately, the ssh server process can be instructed to allow logons from only certain accounts.
It's a good idea to embrace this functionality and to only allow one or two accounts the ability to log on via ssh. Further, "root" should not be on the list! If you need to do something with root privilege you should either use
sudoto execute single commands, or do as we've been doing in this guide and use
sudo /bin/bashto launch a new shell as the root user if you're going to be doing a lot of things that need privilege.
Wrap That RascalEven disallowing certain usernames might not protect you from brute force attacks, where an attacker spams ssh with a huge number of username and password combinations to try to gain access. You need to also implement a method to limit the number of remote logon tries a user is allowed to make. There are popular methods to do this: via an automated and configurable TCP wrapper like DenyHosts, or by using the built-in iptables firewall to drop all traffic from a given IP address if it makes too many connection attempts in a short amount of time.
Both methods have their advantages. Iptables is arguably more difficult to configure, while DenyHosts uses more resources and can sometimes lag a bit in keeping up with rapid attacks. I've written long blog articles about setting up both solutions; rather than bloat this feature further, you can go here for full instructions on making DenyHosts work or here for instructions on getting iptables working. My recommendation is to try DenyHosts first, especially if you don't have much experience using iptables. However, iptables is far more robust and is the better solution if you don't mind a bit more pain in setting it up.
Web served!After all of this, you'll end up with a fast, reasonably secure Web server running Nginx, capable of serving out static HTML pages. The setup described herein is perfect for hosting a personal site but what it can't do is host any dynamic content—we haven't yet installed PHP, or a database, or anything else that'll get you doing fancy-pants Web stuff.
That's OK, though. We'll get to all those things and more shortly.