Skip to main content.

Building a hardened, feature-rich webserver


Live demo in BSD Now Episode 051. | Based on a tutorial by Remy, with additions and editing by TJ for | Last updated: 2014/11/05

NOTE: the author/maintainer of the tutorial(s) is no longer with the show, so the information below may be outdated or incorrect.

The most common thing anyone has probably ever used BSD for is a webserver. There are a number of software and configuration choices you can use, but this tutorial will focus on our current favorite: nginx. We're going to set up a secure webserver that only accepts HTTPS connections, encrypting all the data sent between the server to the client. Our config file will redirect HTTP requests to their HTTPS counterpart automatically. First, we'll choose strong SSL settings on our webserver. We do this by disabling SSL compression to mitigate the CRIME attack, disable SSLv3 and below because of vulnerabilities in the protocol and set up a strong cipher suite that enables Perfect Forward Secrecy when possible. This way, we have a strong and future-proof configuration. This tutorial works with the stricter requirements of the SSL Labs test announced on the 21st of January, 2014. You can find more info on the topics by following the links below:

If you're running FreeBSD, the config file we'll be editing is located at /usr/local/etc/nginx/nginx.conf. It's recommended to run nginx in a jail. If you're running OpenBSD, it will be at /etc/nginx/nginx.conf. For our configuration, you need to edit the parts between the "server" block for the server config. At the end of the tutorial, you can find the complete example configuration. It may be a good idea to back up the files before editing them if this is your first time setting up a webserver. Let's discuss a few of the potential attacks against poor SSL configurations first before actually getting into the main part of the tutorial.


In short, by tampering with with an encryption algorithm's CBC modes, portions of the encrypted traffic can be decrypted. Recent browser versions have enabled client-side mitigation for the BEAST attack. The recommendation was to disable all TLS 1.0 ciphers and only offer RC4. However, RC4 has a growing list of attacks against it, many of which have crossed the line from theoretical to practical. Moreover, there is reason to believe that certain other people have broken RC4. Disabling RC4 has several ramifications:

  • Users with older browsers such as Internet Explorer on Windows XP will use Triple-DES. Triple-DES is more secure than RC4, but it is significantly more expensive CPU-wise. Your server will pay the cost for these users.
  • RC4 mitigates BEAST. Disabling RC4 makes TLS 1.0 users susceptible to the attack, by moving them to AES-CBC (the usual server-side BEAST "fix" is to prioritize RC4 above all else). The flaws in RC4 significantly outweigh the risks from BEAST. With client-side mitigation (which Chrome and Firefox both provide), BEAST is a non-issue. However, the risk from RC4 only grows: more cryptanalysis will surface over time.

CRIME and SSL Compression

The CRIME attack uses SSL compression to do its magic. SSL compression is turned off by default in nginx 1.1.6+/1.0.9+ (if OpenSSL 1.0.0+ used) and nginx 1.3.2+/1.2.2+ (if older versions of OpenSSL are used). If you are using an earlier version of nginx or OpenSSL, and your OS has not backported this option, then you need to recompile OpenSSL without zlib support. This will disable the use of OpenSSL using the DEFLATE compression method. If you do this, then you can still use regular HTML DEFLATE compression.

SSLv2 and SSLv3

SSLv2 is insecure, so we need to disable it entirely. We'll also disable SSLv3, as TLS 1.0 can suffer from a downgrade attack, allowing an attacker to force a connection to use SSLv3 and therefore disable forward secrecy. We'll want something like this in our configuration:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

You can find more info in the nginx documentation.

The Cipher Suite

Forward Secrecy ensures the integrity of a session key in the event that a long-term key is compromised. PFS accomplishes this by enforcing the derivation of a new key for each and every session. This means that when the private key gets compromised, it cannot be used to decrypt recorded SSL traffic. The cipher suites that provide Perfect Forward Secrecy are those that use an ephemeral form of the Diffie-Hellman key exchange. Their disadvantage is the overhead, which can be improved by using the elliptic curve variants. This is an example cipher suite:

ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:

That should all be on one line in your nginx.conf file. In short, the above ciphersuite:

  • Provides forward secrecy to most reasonably modern clients
  • Provides improved security as more clients enable TLS 1.2
  • Provides "some" security for people stuck on Windows XP (which is still a large percentage of traffic as of this writing)

If you want to use TLS 1.2, you need to have OpenSSL 1.0.1c or later. Now let's get down to some other preferences:

  • For the key exchange, we will prefer ephemeral KEX algorithms that provide PFS. We favor Elliptic Curve Diffie-Hellman (ECDHE) over the multiplicative version (DHE), as the former is less processor-intensive for a similar level of security. We will still support both, however.
  • For identity, we'll only support RSA. That's fine, as your certificate is probably RSA. DSA certs are very uncommon. For message ciphers, we favor AES over everything else. For AES, we favor the GCM version over CBC, as GCM is more efficient (and not susceptible to BEAST). We will also favor AES-256 over AES-128.
  • For message authentication, we favor SHA-2 with 256 or 384-byte digests. We'll disable non-authenticated and unencrypted suites, all of the legacy export-approved ciphers, Camellia, DES (but not 3DES), MD5 and pre-shared keys.

Extra Settings

Make sure you also add these lines:

ssl_prefer_server_ciphers on;
ssl_session_cache  builtin:1000  shared:SSL:10m;

When choosing a cipher during an SSLv3 or TLSv1 handshake, normally the client's preference is used. If this option is enabled, the server's preference will be used instead. The nginx documentation has more info on ssl prefer server ciphers and ssl ciphers.

Diffie Hellman Ephemeral Parameters

All versions of nginx as of 1.4.4 rely on OpenSSL for input parameters to Diffie-Hellman. Unfortunately, this means that Ephemeral Diffie-Hellman (DHE) will use OpenSSL's defaults, which include a 1024-bit key for the key exchange. Since we're using a 2048-bit certificate, DHE clients will use a weaker key exchange than non-ephemeral DH clients. We need generate a stronger DHE parameter. If you're on OpenBSD, run:

# mkdir -p /etc/ssl/certs
# cd /etc/ssl/certs
# openssl dhparam -out dhparam.pem 2048

On FreeBSD, you'll want to be in /usr/local/etc/ssl instead. This will tell nginx to use it for DHE key-exchange:

ssl_dhparam /etc/ssl/certs/dhparam.pem;

And that's that.

OCSP Stapling

OCSP stapling is an enhancement to the standard OCSP protocol that delivers OCSP responses from the server with the certificate, eliminating the need for relying parties (web users) to check OCSP responses with the issuing CA. This has the effect of reducing bandwidth, improving perceived site performance and increasing security for everyone involved in establishing the secure session. OCSP stapling is defined in the IETF RFC 6066. The term "stapling" is a popular term used to describe how the OCSP response is obtained by the web server. The web server caches the response from the CA that issued the certificate. When an SSL/TLS handshake is initiated, the response is returned by the web server to the client by attaching the cached OCSP response to the CertificateStatus message. When possible, you should enable HTTP Strict Transport Security (HSTS), which instructs browsers to communicate with your site only over HTTPS.

Keys and Certificates

You can get an SSL certificate from a real Certificate Authority or generate your own. If you use your own, the visitors to your site will get a big scary warning that the certificate is self-signed. We'll go through the process of generating a self-signed certificate now.

# cd /etc/ssl/certs
# openssl genrsa -des3 -out bsdnow.key 2048
# openssl req -new -key bsdnow.key -out bsdnow.csr
# cp bsdnow.key
# openssl rsa -in -out bsdnow.pem
# rm
# openssl x509 -req -days 365 -in bsdnow.csr -signkey bsdnow.key -out ca-bundle.pem

If you got the files from a real CA, use them instead. We'll plug these files into the configuration in the next section. Make sure the private key is not world-readable!

Config Example

Here is the final example of the "server" section of the nginx config file:

server {

listen 80; 
listen 443 default ssl;

#ssl on;
ssl_certificate_key /etc/ssl/certs/bsdnow.pem;
ssl_certificate /etc/ssl/certs/ca-bundle.pem;


ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_session_cache  builtin:1000  shared:SSL:10m;

ssl_stapling on;
ssl_stapling_verify on;

ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

add_header Strict-Transport-Security max-age=63072000;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;

client_body_buffer_size 1K;
client_header_buffer_size 1k;
client_max_body_size 1k;
large_client_header_buffers 2 2k;
client_body_timeout 10;
client_header_timeout 10;
send_timeout 10;

if ($host !~ ^(|$ ) {
    return 444;
if ($request_method !~ ^(GET|HEAD)$ ) {
    return 444;

root /var/www/htdocs;
index index.html index.htm;
autoindex on;
listen 80 default;
return 301 https://$server_name$request_uri;


Be sure to adjust the paths to point to the correct files. If you have applied the above config lines you need to restart nginx:

# /etc/rc.d/nginx restart

You can use the SSL Labs test to see how your setup scores. You may also want to read this page for more information on the ciphers used. There's an online config generation utility that may also be of interest to some people.

Latest News

New announcement


We understand that Michael Dexter, Brad Davis, and George Rosamond think there should be more real news....

Two Year Anniversary


We're quickly approaching our two-year anniversary, which will be on episode 105. To celebrate, we've created a unique t-shirt design, available for purchase until the end of August. Shirts will be shipped out around September 1st. Most of the proceeds will support the show, and specifically allow us to buy...

New discussion segment


We're thinking about adding a new segment to the show where we discuss a topic that the listeners suggest. It's meant to be informative like a tutorial, but more of a "free discussion" format. If you have any subjects you want us to explore, or even just a good name...

How did you get into BSD?


We've got a fun idea for the holidays this year: just like we ask during the interviews, we want to hear how all the viewers and listeners first got into BSD. Email us your story, either written or a video version, and we'll read and play some of them for...

Episode 281: EPYC Server battle


Direct Download:MP3 AudioVideo Headlines scp client multiple vulnerabilities Overview SCP clients from multiple vendors are susceptible to a malicious scp server performing unauthorized changes to target directory and/or client output manipulation. Description Many scp clients fail to verify if the objects returned by the scp server match those it asked for. This issue dates back to 1983 and...

Episode 280: FOSS clothing


Direct Download:MP3 AudioVideo Headlines A EULA in FOSS clothing? There was a tremendous amount of reaction to and discussion about my blog entry on the midlife crisis in open source. As part of this discussion on HN, Jay Kreps of Confluent took the time to write a detailed response — which...

Episode 279: Future of ZFS


Direct Download:MP3 AudioVideo Headlines The future of ZFS in FreeBSD The sources for FreeBSD's ZFS support are currently taken directly from Illumos with local ifdefs to support the peculiarities of FreeBSD where the Solaris Portability Layer (SPL) shims fall short. FreeBSD has regularly pulled changes from Illumos and tried to push...

Episode 278: The real McCoy


Direct Download:MP3 AudioVideo Interview - Kirk McKusick - 25 years of FreeBSD How Kirk got started in BSD, at the very beginning Predicting the Future How the code and community grew The leadership of the project, and how it changed over time UFS over the years (reading disks from 1982 in 2018) Conferences The rise and fall of...