Apache Web Server Ssl Configuration

And that’s it folks. With these settings, your web server should be as secure as Fort Knox 🙂 Let us now restart the server and check the configuration. Restart the server now as follows: service apache2 restart Now verify that SSL is working by checking with the browser. These directives are placed at global scope (i.e., not within a virtual host definition) wherever other global SSL configuration directives are placed, such as in conf/extra/httpd-ssl.conf for normal open source builds of httpd, /etc/apache2/mods-enabled/ssl.conf for the Ubuntu or Debian-bundled httpd, etc.

SSL is currently on of the standards of web security. Learn how to implement an SSL certificate on an Apache Web Server to keep your data safe.

Otherwise, your server may need you to manually restart Apache with the command 'apachectl startssl' during server re-boot, which typically involves the removal of the and that enclose your SSL configuration. Make sure you run the SSL Server Test at the end of the installation process to check your certificate configuration against SSL/TLS Best Practices. For more information on SSL/TLS Best Practices, click here. The installation is in four parts 1) Copy the certificate files to your server 2) Configure the Apache server to point to certificate files.

Join the DZone community and get the full member experience.

Join For Free

Before you start the installation process, please make sure that a CSR Code is generated, all validations are met, and the SSL Certificate is issued and downloaded.

To Install an SSL Certificate, Perform the Following Steps:

  • Copy the certificate into the shell text editor and name the file “mydomain.crt”

Note: Copy the contents of the certificate from (and including) the -----BEGIN CERTIFICATE---- line to the ---END CERTIFICATE--- line.

  • Copy the certificate to the Apache Server Directory in which you plan to store your certificates (by default: /usr/local/apache/conf/ssl.crt/ or /etc/httpd/conf/ssl.crt/)

Note: - If you have a custom installation, please locate the server directory.

  • Open the Apache Configuration file in a text editor. Apache configuration files are usually found in /etc/httpd. The main configuration file is usually named httpd.conf. In most cases, the <VirtualHost> blocks will be at the bottom of this httpd.conf file. Sometimes you will find <VirtualHost> blocks in a separate file in a directory like /etc/httpd/sites/ or in a file called ssl.conf.
  • Locate the SSL VirtualHost associated with your certificate. Verify that you have the following two directives within this virtual host. Please add them if they are not present.
    • SSLCertificateFile /usr/local/apache/conf/ssl.crt/domainname.crt (or server.crt)
    • SSLCertificateKeyFile /usr/local/apache/conf/ssl.key/domainname.key (or server.key)

Note: Some instances of Apache will store Virtual Host information in an ssl.conf file. If your httpd.conf contains no Virtual Host information then you will need to locate and amend the ssl.conf as performed above.

  • Save the changes and exit the shell editor.
  • Start or Restart your Apache web server using one of the following commands:
  • By default:

    /usr/local/apache/bin/apachectl startssl


    /usr/local/apache/bin/apachectl restart

    ssl certificate,apache web server,security,web security

    Published at DZone with permission of Kalpesh Patel. See the original article here.

    Opinions expressed by DZone contributors are their own.

    Popular on DZone

    There are many wordy articles on configuring your web server’s TLS ciphers. This is not one of them. Instead, I will share a configuration that scores a straight “A” on Qualys’s SSL Server Test in 2020.

    Disclaimer: I’m updating this post continually to represent what I consider the best practice at the moment – there are way too many dangerously outdated articles about TLS-deployment out there already.

    Therefore, it may be a good idea to check back from time to time because the crypto landscape is changing pretty quickly at the moment. You can follow me on Twitter to get notified about noteworthy changes.

    If you find any factual problems, please reach out to me and I will fix it ASAP.

    If you want to skip directly to the config snippets, here’re the anchors:


    A lot has changed since I wrote this article in 2013. Unusually enough, things got better and simpler.

    To have run a secure web server in 2020, all you have to do is:

    1. Enable TLS 1.2 and TLS 1.3only.
    2. Enable a few modern ciphers (mostly AES in GCM mode for devices with hardware acceleration and ChaCha20 for devices without1).
    3. Since all ciphers are secure enough, let the client pick.

    That’s it on the web server side. Things got more complicated on the application side, but that is out of scope for this article.

    Nowadays it’s much more difficult to actually weaken your security on modern distributions, which is why there’s a section at the end on this topic.

    Software and Versions

    On the server side you should update your OpenSSL to 1.0.1c+ so you can support TLS 1.2, GCM, and ECDHE as soon as possible. Fortunately, that’s already the case since Ubuntu 12.04 LTS. For TLS 1.3, you need OpenSSL 1.1.1 which you can have as of Ubuntu 18.04 LTS.

    On the client side the browser vendors have caught up years ago. As of now, Chrome 30, Internet Explorer 11 on Windows 8, Safari 7 on OS X 10.9, and Firefox 26 all support TLS 1.2. All modern browsers also support TLS 1.3.

    Apache Web Server Ssl Configuration Server

    TLS 1.3

    TLS 1.3 brings some nice improvements regarding performance and security, but complicates a few things and requires special configuration on most servers.

    To be clear: if you can only support TLS 1.2 for the time being, that’s perfectly fine.


    There used to be a bullet point suggesting using RC4 to avoid BEAST and Lucky Thirteen. And ironically that used to be the original reason for this article: when Lucky Thirteen came out the word in the streets was: “use RC4 to mitigate” and everyone was like “how!?”.

    Unfortunately, shortly thereafter RC4 was found broken in a way that makes deploying TLS with it nowadays a risk. While BEAST et al. require an active attack on the browser of the victim, passive attacks on RC4 ciphertext are getting stronger every day. In other words: it’s possible that it will become feasible to decrypt intercepted RC4 traffic eventually and the NSA probably already is. Microsoft even issued a security advisory that recommends disabling RC4. As of 2015, there’s also an RFC.

    The String

    You can test it against your OpenSSL installation using

    to see what’s supported.

    Two quick notes:

    • The string asks for ECDH which technically is the static version of ECDHE without perfect forward secrecy. That’s really bad. The reason it’s in there is that 1. No server ever supported them. 1. Before OpenSSL 1.0.2, you couldn’t ask for ECDHE specifically2. You can try it with Docker in ubuntu:trusty: you’ll get an empty list3.

      I assume that most of you dear readers have an up-to-date OpenSSL, however leaving it as it is has no security implications and is more compatible/less likely to cause me confused questions in my mailbox.

    • Qualys will complain about TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 and TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 being weak, but they’re the price to keep Java 8, Internet Explorer 11, and older Safaris around.

    The string allows AES-256 but does so mostly for liability reasons because customers may insist on it for bogus reasons.

    However, quoth a cryptographer:

    AES-128 isn’t really worse than AES-anythingelse, at least not in ways you care about

    The very simplified gist here is that the only reason for having 256-bit keys are quantum computers which are less likely to become a problem than the key scheduling issues in AES-256. But let me stress that both are fine. It’s just that adding AES-256 ciphers doesn’t improve your security in practice.

    So, if AES-128 is fine for you, feel free to add an ‘:!AES256’ to the end of the cipher string and many browsers will prefer AES-128.



    nginx added support for TLS 1.2 in 1.0.12 (all supported LTS distributions) and TLS 1.3 in 1.13.0 (Ubuntu 18.04 LTS and later).

    As of nginx 1.19.4, you can also configure TLS 1.3 ciphers:


    The oldest currently supported HAProxy is version 1.8 which is fine for both TLS 1.2 and TLS 1.3.

    As you might have noticed by the cipher suite names, the ssl-default-XXX-ciphersuites options are for TLS 1.3 and ssl-default-XXX-ciphers are for TLS 1.2 (and older).

    prefer-client-ciphers is always implied with OpenSSL 1.1.1 and the client preferring ChaCha20-Poly1305 (meaning it’s probably a phone with slow AES).

    TLS 1.3’s Zero Round Trip Time Resumption

    TLS 1.3 also introduced an optimization called Zero Round Trip Time Resumption (0-RTT). It makes TLS handshakes of returning clients faster, but has some inherent replay attack risks. I’m personally not allowing it on the outside, but if the backends are TLS 1.3-ready, I use the force-tls13 allow-0rtt server options.

    Bonus Points

    • If you have OpenSSL 1.1.0 or later, it’s also worth adding :@SECLEVEL=2 to your cipher string as a protection against weak keys (the other limits of the security levels are already handled by the cipher string).

    • In April 2014, Qualys have updated their requirements and the cipher suites here are still “A”–material. If you want an “A+” though, you’ll need to tweak your HTTP headers too. This goes way beyond the scope of this article, check out securityheaders.io to get started.

    Legacy Clients With Modern Distributions

    Unlike 2013, you have to put effort into making your server less secure. This can be necessary if the endpoint doesn’t serve browsers but (potentially ancient) IoT devices, routers, and other embedded systems. It also causes headaches to open source projects that need to maintain backward compatibility.

    So if you need to expose TLS 1.0 on a modern Ubuntu 20.04 LTS, you have to either tinker with /etc/ssl/openssl.cnf or create a new one by creating a copy, do the following changes, and pass the file name of your new config to the server using the OPENSSL_CONF environment variable4.

    In any case, you have to add openssl_conf = default_conf to the top of the config file and then append

    to the end5.


    Once done, you can use my old cipher string that is still reasonably secure:

    Make sure to restart the server that you are trying to affect.

    Unfortunately, the server won’t be able to tell you whether it worked. For example, I’ve spent hours trying to figure out why ssl-min-ver TLSv1.0 was doing nothing on my HAProxy 2.2 while getting my logs flooded with SSL handshake failure.

    You can test that everything is working as expected by forcing a TLS 1.0 client connection to your host:

    If it worked, you’ll see connection information. If not, you’ll get a handshake error.


    Make sure to test your server afterwards! A still common problem are weak DH parameters. Please refer to this guide on how to fix that, if you still have to use DHE. Sadly, except for HAProxy, it’s a bit more involved than just setting an option.

    Also test your HTTP headers. A lot has changed here in the past 7 years and you won’t get an A+ on Qualys if you don’t put at least some effort into this.

    Apache Web Server Ssl Configuration

    Apache Web Server Setup

    If you want to learn more about deploying SSL/TLS, the excellent book Bulletproof SSL and TLS has just (November 2020) published a preview of their second edition and I can’t recommend it enough (this is not an affiliate link).

    For investigating the SSL/TLS behavior of your browser, Qualys also has a page for that.


    I’ve been keeping this guide up to date since 2013 throughout the very tumultuous evolution of modern TLS (as the following history shows). It would be great if you’d consider a small token of appreciation – no account required!

    • 2021-02-11: Added TLS 1.3 configuration details for nginx 1.19.4 and later.
    • 2020-11-21: Rewrote and updated for TLS 1.3.
    • ‌2020-03-23: Added that you have to use the best cipher on the page to get an A. I will do more updates when I find the time.
    • ‌2019-06-23: Disabled AESCCM which is safe but uncommon and slow.
    • 2018-10-04: Small fix to the HAProxy next steps config: prefer-client-ciphers is neither needed nor allowed in ssl-default-server-options.
    • 2018-08-09: Added next steps to drop RSA, and going ECDHE-only. Added ChaCha20 to the default string for the case a client supports ChaCha20 but no AES-GCM (I’m not aware of any though).
    • 2017-06-12: Added DHE param size to HAProxy.
    • 2016-08-24: Removed 3DES because of SWEET32. This drops IE 8 on Windows XP support which shouldn’t be a concern in 2016.
    • 2015-05-20: The new weakdh/Logjam attack doesn’t affect you if you followed these instructions. It might be worthwhile though to create your own DH groups with at least 2048 bits as described in this guide.
    • 2015-01-16: Added a note on ECDSA because there seemed to be some confusion about it.
    • 2014-11-25: Added HAProxy, courtesy of Sander Klein.
    • 2014-10-24: Updated the TLS compression part about Red Hat/CentOS. TL;DR: it’s secure by default now.
    • 2014-10-21: Clarified that Internet Explorer 8 on Windows XP works fine with TLSv1-only. The original wrong claim stemmed from the fact that I double-checked using SauceLabs and it turned out that they don’t use Windows XP when you ask for it but Windows Server 2003 R2 instead. Since then, I re-tested using an actualWindows XP workstations running Internet Explorer 8 and it works fine.
    • 2014-10-15: Disabled SSLv3 because of POODLE.
    • 2014-04-11: Added note on CentOS/RHEL 6 and nginx+ECDHE.
    • 2014-01-17: RSA+AES has been split into RSA+AESGCM:RSA+AES. This is a very minor update that only matters if you have TLS 1.2 but neither ECDHE nor DHE (which is rather rare). It makes sure that RSA-AES-128-GCM is preferred over RSA-AES-256-CBC in these cases. Since both are just fallbacks and you should use PFS ciphers, this is just minutiae.

    Credits: The initial version and the big November 2020 update of this article has been kindly proof-read by Christian Heimes. The old cipher string was based on one by Zooko Wilcox-O’Hearn who – unlike me – is an actual cryptographer. The errors are all still mine.

    1. Nowadays mostly phones. ↩︎

    2. Instead, you had to set a flag additionally to the cipher string. ↩︎

    3. docker run --rm -it ubuntu:trusty openssl ciphers ECDHE


      docker run --rm -it ubuntu:trusty openssl ciphers ECDH↩︎

    4. This can be done using systemd unit files but goes beyond the scope of this article. ↩︎

    5. Shamelessly stolen and adapted from the linked Python bug. The workaround is from Vladyslav Bondar and Tal Einat. ↩︎