Banner image of 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.

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 website’s future content to set the HTTP response 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. Besides, major web browsers like Chrome/Chromium and Firefox started to remove visual indicators for EV certificates. So EV certificates look like DV/OV certificates for users.

Our advice is to simply obtain a free DV certificate from Let’s Encrypt. It is totally sufficient for most websites.

If you implemented part 2 of our series, you already got a DV certificate from Let’s Encrypt.


What is TLS?

TLS (Transport Layer Security) is one way to encrypt data in transit. The last two words are important here: “in transit”. TLS doesn’t protect data stored on your server (data at rest) or data that is processed by your server (data in use). TLS only protects data on its way from your server to clients and from clients to your server.

In order to use TLS, all parties involved have to announce their supported cryptographic algorithms and must agree on which algorithms to use. On the client side, this is automatically done by web browsers. On the server side, this is done by the web server. Then, only the client checks the certificate provided by the server for validity in most cases.

There is a risk here: As most web browsers support legacy algorithms, they can tell the web server to switch to weak legacy encryption. This is a so-called downgrade attack. Due to this, it was good practice to enforce the server-side order of encryption algorithms. Nowadays, it is considered good practice to disable all legacy algorithm on the server side and to let clients choose the algorithms again. This allows clients without hardware acceleration for encryption to switch to more efficient algorithms. Furthermore, all developers of major web browsers already announced to drop support for legacy algorithms in the next few years.

Cryptographic parameters are grouped and called “cipher suites". Cipher suites prior to TLS version 1.3 (published in August 2018) 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 cipher suites 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.

An image showing 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:

# Configuration for best compatibility
SSLProtocol +TLSv1.2

# Disable server-side preference if you don't offer any legacy cipher suites
SSLHonorCipherOrder off

# Disable TLS compression
SSLCompression off

# Disable TLS session tickets
SSLSessionTickets off

# Set curves to prime256v1 and secp384r1
# (X25519 isn't supported by the underlying OpenSSL version on Debian 9)
# (secp521r1 doesn't offer much more security)
SSLOpenSSLConfCmd Curves prime256v1:secp384r1
SSLOpenSSLConfCmd ECDHParameters secp384r1

Please note:

  • This configuration enables TLS 1.2 cipher suites which support PFS and AEAD only. If you are already on Debian 10 (or run newer versions of Apache), you can additionally enable TLS 1.3 (SSLProtocol +TLSv1.2SSLProtocol +TLSv1.3 +TLSv1.2).
  • 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: sudo systemctl restart apache2. Check if there are any errors: systemctl status 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 is able to 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 some web browsers support OCSP and even less clients support OCSP stapling. However, support for OCSP stapling is required for Certificate Transparency and Certificate Transparency becomes more and more popular.

Apache configuration

We already enabled OCSP stapling in part 2 (see lines 30 to 33). 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 response headers

We already enabled several security-relevant HTTP response headers in part 2. In this part, we discuss them and talk about 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-Content-Type-Options (part 2, line 27): 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.

Legacy HTTP response headers

HTTP response headers mentioned in this section are either legacy headers or already deprecated. This means you only need to set these headers if you want to support really old web browsers. If you allow modern TLS 1.2 ciphers suites only, it is very likely that these old web browsers are already unable to connect to your web sever. Due to this, we recommend to skip the following HTTP response headers.

  • X-Frame-Options (part 2, line 41): The X-Frame-Options header serves the same purpose as the frame-ancestors directive of the CSP Level 2 (or newer levels). It is only needed if you want to support legacy clients that don’t support Content Security Policy Level 2 directives. In this case, set it to X-Frame-Options "DENY". DENY disables the possibility to embed your website using embed or iframe tags on a third-party website. This prevents clickjacking. If you set this header, it must match the frame-ancestors directive of your Content Security Policy (see above).
  • X-Xss-Protection (part 2, line 46): The X-Xss-Protection header tells web browsers to act on reflected XSS. This header isn’t supported by all major web browsers (or they are about to drop support for it). If you still want to set it, set it to X-Xss-Protection "1; mode=block". This setting tells clients to stop rendering the website if reflected XSS is detected.
  • HPKP: The HTTP Public Key Pinning is a deprecated HTTP response header. Most web browsers dropped support for it. Simply do not use it.

Follow us on Mastodon:

Upcoming and experimental HTTP response headers

There are more HTTP headers, of course. Some of them are experimental, others are drafts. We implement some of these headers on for testing from time to time. Maybe, we add some of the following headers to the standard configuration if they become widely adopted by clients.

At the moment, most web browsers don'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.
  • Cross-Origin-Opener-Policy: The Cross Origin Opener Policy allows websites to request a new browsing context group to better isolate itself from other untrustworthy origins. The header aims to mitigate cross-window attacks and process-wide attacks. This header is highly experimental and not supported by clients.
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. Keep in mind that good practices change over time. So, regularly recheck if your web server is still configured according to current good practices.

You also have to check if there are any issues due to HTTP response headers set by you. Some headers allow you to set report-uri or report-to directives to get error reports from clients. Furthermore, you can use the developer mode of most web browsers to see such errors in your own web browser. However, this is more about technical misconfiguration and not about security issues.

In the next article, we introduce ModSecurity and Fail2ban.


  • Jul 27, 2019: Added information about legacy HTTP response headers (X-Xss-Protection and X-Frame-Options). Removed Subresource Integrity since this was never an HTTP response header.
  • Jul 14, 2019: Rewrote several sections due to the release of Debian 10 and part 0 of this series.
  • 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.

Read also