Web server security – Part 3: TLS and security headers

Web server security – Part 3: TLS and security headers

In the second part of this series, we showed you how to harden your web server software. In this part, we introduce TLS cipher suites, talk about OCSP and show you additional security-related HTTP response headers.


  1. Requirements
  2. Certificate
  3. TLS
  4. OCSP
  5. Certificate Transparency
  6. Security-related HTTP headers
  7. Upcoming and experimental headers
  8. Summary
  9. Links
  10. Changelog

Always stay in the loop!
Subscribe to our RSS/Atom feeds.

So far, we hardened the web server and already enabled TLS. However, there are even more security features and TLS needs to be hardened.


  • hardened web server as configured in part 2
  • SSH client on your computer
  • knowledge about your websites content to set the headers accordingly


In order to use HTTPS (and in some other cases), you need a digital certificate which you can obtain from Let’s Encrypt or other so-called “certificate authorities”. The basic idea of certificates is quite simple. Imagine the following scenario:

You want to buy alcoholic beverages and the store clerk asks for your identity card to check your age. Your ID card was issued by an authority. The store clerk trusts the authority which signed your ID card. Eventually, he trusts you due to the signed ID card you provided.

In our online world, servers provide certificates which are signed by certificate authorities. Clients trust certificate authorities. So, clients need to check if your server provides a valid certificate. The actual process is a bit more complicated and there are several pitfalls (e.g. certificate revocation), but this is the basic idea.

As a server administrator, you can obtain three different types of certificates: DV (domain validated), OV (organization validated) and EV (extended validation). Nearly all common web browsers don’t differentiate between DV and OV certificates. EV certificates should be more trustworthy in theory since you must provide more information to prove your identity. However, there are more and more security experts who are convinced that EV certificates will die soon. Our advice is to simply obtain a free DV certificate from Let’s Encrypt. It is totally sufficient for most websites.


What is TLS?

TLS (Transport Layer Security) is one possibility to encrypt data traffic in transit. In order to use TLS, all parties involved have to announce their supported cryptographic algorithms and must agree on which algorithms to use. In most cases, the server picks the strongest algorithms which are supported by both parties and the client checks the validity of the server’s certificate.

The cryptographic parameters are grouped and called “cipher suites”. Cipher suites prior to TLS version 1.3 (published this year) define:

  • key exchange algorithm (like ECDHE)
  • authenticity algorithm (like ECDSA or RSA)
  • encryption algorithm (like AES256-GCM or ChaCha20)
  • integrity algorithm (like SHA-384 or Poly1305)

Two examples are ECDHE-ECDSA-CHACHA20-POLY1305 and ECDHE-ECDSA-AES256-GCM-SHA384. However, TLS 1.2 comes with many legacy algorithms which are considered weak or insecure, nowadays. This not only requires you to simply enable TLS but also to disable older TLS versions and weak cipher suites.

Cipher suites before TLS 1.3 and TLS 1.3 cipher suites.
Cipher suites before TLS 1.3 and TLS 1.3 cipher suites. (🔍 Zoom in)

TLS 1.3 deprecates many legacy algorithms and introduces additional security features since there are many known attacks against older versions of TLS and its predecessor SSL. TLS 1.3 cipher suites define:

  • encryption algorithm (like AES-256-GCM or ChaCha20)
  • integrity algorithm (like SHA-384)

Authenticity algorithms are defined in TLS 1.3 extensions and the only relevant key exchange algorithm is ECDHE.

Do you need TLS?

Yes. Period. There are endless discussions about the need for TLS. We think that you always want at least authenticity and integrity: Being able to check that you are connected to the right server and to know that you receive data actually sent by the server. Encryption becomes important if sensitive information or personal data is exchanged.

Apache configuration

Go to /etc/letsencrypt/ and open options-ssl-apache.conf. This file already includes the default TLS configuration of your server. Change the configuration to:

# Modern configuration, tweak to your needs
SSLProtocol +TLSv1.2
SSLHonorCipherOrder on
SSLCompression off
SSLSessionTickets off
SSLOpenSSLConfCmd Curves secp521r1:secp384r1
SSLOpenSSLConfCmd ECDHParameters secp521r1

If you encounter problems, try “secp521r1:secp384r1:prime256v1” for SSLOpenSSLConfCmd.

Please note:

  • This configuration enables only TLS 1.2 cipher suites which support PFS and AEAD.
  • In order to enable ChaCha20 cipher suites, you need OpenSSL 1.1.0 or newer. Otherwise, ChaCha20 cipher suites are enabled in your configuration file but aren’t supported by your web server.
  • Cipher suites which use ECDSA instead of RSA require a ECDSA certificate. By default, Let’s Encrypt only issues RSA certificates at the moment. We will show you how you can obtain an ECDSA certificate in an upcoming article.

Save the file and restart Apache (e.g. sudo systemctl restart apache2).


What is OCSP?

The OCSP (Online Certificate Status Protocol) is important for certificate revocation information. Imagine an attacker who accessed your server and stole your private key to issue new certificates or did something malicious. Anyway, there are situations which require you to revoke your certificate. The problem is that most clients never receive this information by default.

Years ago, there were Certificate Revocation Lists (CRL) which listed all revoked certificates. However, clients had to regularly check for updates and lists became bigger and bigger. OCSP was invented, allowing clients to contact a third party (OCSP responder) and ask this trustworthy party if a certificate is still valid. However, this third party can log all requests of clients and track TLS connections of clients.

OCSP stapling fixes this: The server regularly contacts the OCSP responder and gets an authenticated OCSP response in advance. If a client connects to the server and asks for OCSP information, it gets the pre-authenticated OCSP response. The OCSP responder doesn’t learn about this connection and this process is faster. One problem remains: The client doesn’t know that a server can provide OCSP information.

This brings OCSP Must-Staple into play. OCSP Must-Staple is a certificate extension which allows the client to learn about the presence of OCSP information during the TLS handshake.

Do you need OCSP?

Maybe. Only a subset of clients supports OCSP and a subset of this subset supports OCSP stapling. However, support for OCSP stapling is required for Certificate Transparency and Certificate Transparency becomes more and more popular.

Apache configuration

Actually, we enabled OCSP stapling in part 2 (see lines 35 to 39). However, we still have to enable OCSP Must-Staple which is embedded in our certificate.

Obviously, a new certificate is needed. We need to tell Let’s Encrypt that we want “Must-Staple” embedded in the certificate by adding --must-staple. Furthermore, we must add --force-renewal to get a new certificate.

The full command is: sudo certbot renew --force-renewal --must-staple

If you want a ECDSA certificate with OCSP Must-Staple extension, you have to configure the csr.cnf file prior to your certificate request. We will show the steps in an upcoming article.

Certificate Transparency

Certificate Transparency (CT) is a more complex system which basically logs information about all certificates issued by trustworthy certificate authorities. This allows clients and other parties to validate certificates provided by servers.

Certificate authorities like Let’s Encrypt automatically embed SCTs (signed certificate timestamps) in certificates they issue. So, you don’t need to implement this. Your certificate should already contain SCTs.

Security-related HTTP headers

We already enabled several security-relevant HTTP headers in part 2. In this part, we discuss them and even more headers. Please note that we don’t want to explain every detail of each header. There are several blogs and many websites which explain every single possibility to configure the headers. Some of these headers are also very new and their format may be changed over time. Use search engines to learn about details, if necessary.

  • CSP Level 2 (part 2, line 18): The Content Security Policy (Level 2) header tells clients which elements of the website they should load and which are forbidden. The important part here is that a client must support and follow the CSP in order to be effective. Some server admins enable CSP but allow “unsafe” rules which renders the CSP header useless in some cases. Let us analyze our CSP configured in part 2:
    • default-src 'none': Disallow everything except explicitly defined rules.
    • img-src 'self': Load image files only from the server (no other domain names allowed).
    • style-src 'self': Load style files (CSS) only from the server (no other domain names allowed).
    • font-src 'self': Load font files only from the server (no other domain names allowed).
    • base-uri 'none': Disable base tags.
    • frame-ancestors 'none': Disable the possibility to embed your website in embed or iframe tags. This prevents clickjacking. Must match your X-Frame-Options header (see below).
    • form-action 'none': Disables the possibility of form tags to initiate an action which effectively disables forms.
    • Tip: If you need inline elements (script/style), try to use nonce only. Nonces must be at least 128 bits long (before encoding) and you must use a CSPRNG for generating them. Currently, this is the most secure way to handle inline code.
  • Referrer (part 2, line 21): The referrer is part of a HTTP request. Clients send the referrer header to the server. There are different use cases for this. However, it can be used for tracking of users. If you don’t need referrer information, you can disable it by setting:
    • Referrer-Policy "no-referrer"
  • HSTS (part 2, line 24): The HTTP Strict Transport Security header tells clients to always use HTTPS connections for this domain name. HSTS becomes only effective when sent over HTTPS. Our configuration example in part 2 contains:
    • max-age=31536000: Tells the client to store this HSTS information for 31536000 seconds (roughly 1 year).
    • includeSubDomains: Tells the client that the HSTS directive is valid for all subdomains of this domain.
    • preload: This is an extension. Nowadays, all modern web browsers contain so-called “HSTS preload lists”. Domain names on these lists are always loaded via HTTPS even if a client never visited the website before. You must set this in order to be listed on preload lists.
    • env=HTTPS: Ensures that this header will only be sent over HTTPS.
  • X-Frame-Options (part 2, line 27): The X-Frame-Options header serves the same purpose as the frame-ancestors directive of the CSP. However, some clients can’t handle CSPs, so you should also set this header:
    • X-Frame-Options "DENY": Disable the possibility to embed your website in embed or iframe tags. This prevents clickjacking. Must match your frame-ancestors directive (see above).
  • X-Xss-Protection (part 2, line 30): This header tells web browsers to act on reflected XSS. It can be considered useless if you don’t use any JavaScript and disabled JS in your CSP. Our example shows:
    • X-Xss-Protection "1; mode=block": Tells the client to stop rendering the website if reflected XSS is detected.
  • X-Content-Type-Options (part 2, line 33): This header tells the web browser to follow MIME types defined in the Content-Type headers of server responses. It is only valid for script and style types. We showed the following configuration:
    • X-Content-Type-Options "nosniff": Blocks all requests of style without MIME type text/css and all requests of script without JS MIME types.
Security-related HTTP headers require server-side AND client-side support. Due to this, you need to set additional headers like X-Xss-Protection and X-Frame-Options although already defined as part of the Content Security Policy.

We didn’t configure the following headers:

  • Subresource Integrity: SRI can and should be used to ensure integrity of third-party content embedded in your website. If you don’t embed any third-party content, you don’t need SRI. The basic idea is that you provide a hash per external resource. If the external resource is changed (which can put visitors of your website at risk), the hashes provided by your server and calculated by the client don’t match anymore and the resource is discarded.
  • HPKP: The HTTP Public Key Pinning header was deprecated due to being complex to implement and having dangerous side effects. Expect-CT (see below) is recommended now.

Follow us on Mastodon:

Upcoming and experimental headers

There are more HTTP headers, of course. Some of them are experimental, others are drafts. We implemented some of these headers on infosec-handbook.eu for testing and monitoring and will add them to our configuration example when most web browsers start to support them.

At the moment, the majority of web clients doesn't support the HTTP headers mentioned below. Clients without support will simply ignore the headers. This means that there is no additional protection if you set the headers. On the other hand, you must carefully configure and test them. So, we don't recommend to set these headers at the moment.
  • CSP Level 3: The 3rd version of CSP adds the manifest-src directive, and switches from the deprecated report-uri directive to report-to. Besides, there are several changes to reporting violations, and changes to directives.
  • Expect-Staple: Expect-Staple is a header defined by Scott Helme to monitor OCSP errors. This header can be used to discover misconfigurations or unreliability in your OCSP stapling deployment.
  • Expect-CT: Expect-CT tells clients to check for valid Certificate Transparency information. This header also allows monitoring of CT and detection of misconfigurations.
  • Feature Policy: The upcoming Feature Policy is like CSP, however, you can use it to enable or disable certain features like Geolocation.
  • NEL: The Network Error Logging (NEL) header defines a mechanism enabling web applications to declare a reporting policy that can be used by the user agent to report network errors for a given origin. The logged network errors even include problems occurred before a connection was successfully established.
  • Report-To: The Report-To header is defined in the Reporting API. It aims to provide a best-effort report delivery system that executes out-of-band with website activity and introduces new report types.
  • Clear Site Data: Clear Site Data allows web developers to instruct a user agent to clear a site’s locally stored data related to a host. This data contains cache (pages, script caches etc.), cookies, storage (sessionStorage, localStorage etc.) and executionContexts.
We regularly update this section to track the status of upcoming headers, and modify their description, if necessary.

This article is part of the "Web server security" series.
Read other articles of this series.


Nearly all security-related headers require client-side support. This means there is no guarantee that a client follows your directives. However, you can enforce the most secure TLS setup at the moment and set strict rules for clients which can handle those headers.

Another important part of server security is not only setting headers but monitoring their deployment to detect misconfiguration and other unexpected behavior. Some headers allow you to set report-uri or report-to directives. Use this to monitor your headers, if necessary. (And keep in mind that you let third parties process data of your users if you use third-party services like report-uri.com or uriports.com!)

In the next article, we introduce ModSecurity and Fail2ban. Furthermore, we will update this article if we are able to use TLS 1.3 for our test server.


  • May 15, 2019: Added notes regarding experimental/upcoming headers. Clarified difference between the current CSP Level 2 and the upcoming CSP Level 3.
  • Nov 30, 2018: Added Clear Site Data header.
  • Nov 11, 2018: Added NEL and Report-To headers.

See also