// Tutorial //

How To Configure OCSP Stapling on Apache and Nginx

Published on June 12, 2014
Default avatar
By Jesin A
Developer and author at DigitalOcean.
How To Configure OCSP Stapling on Apache and Nginx

Introduction

OCSP stapling is a TLS/SSL extension which aims to improve the performance of SSL negotiation while maintaining visitor privacy. Before going ahead with the configuration, a short brief on how certificate revocation works. This article uses free certificates issued by StartSSL to demonstrate.

This tutorial will use the base configuration for Apache and Nginx outlined below:

About OCSP

OCSP (Online Certificate Status Protocol) is a protocol for checking if a SSL certificate has been revoked. It was created as an alternative to CRL to reduce the SSL negotiation time. With CRL (Certificate Revocation List) the browser downloads a list of revoked certificate serial numbers and verifies the current certificate, which increases the SSL negotiation time. In OCSP the browser sends a request to a OCSP URL and receives a response containing the validity status of the certificate. The following screenshot shows the OCSP URI of digitalocean.com.

OCSP URI

About OCSP stapling

OCSP has major two issues: privacy and heavy load on CA’s servers.

Since OCSP requires the browser to contact the CA to confirm certificate validity it compromises privacy. The CA knows what website is being accessed and who accessed it.

If a HTTPS website gets lots of visitors the CA’s OCSP server has to handle all the OCSP requests made by the visitors.

When OCSP stapling is implemented the certificate holder (read web server) queries the OCSP server themselves and caches the response. This response is “stapled” with the TLS/SSL Handshake via the Certificate Status Request extension response. As a result the CA’s servers are not burdened with requests and browsers no longer need to disclose users’ browsing habits to any third party.

Check for OCSP stapling support

OCSP stapling is supported on

  • Apache HTTP Server (>=2.3.3)
  • Nginx (>=1.3.7)

Please check the version of your installation with the following commands before proceeding.

Apache:

apache2 -v

Nginx:

nginx -v

CentOS/Fedora users replace apache2 with httpd.

Retrieve the CA bundle

Retrieve the root CA and intermediate CA’s certificate in PEM format and save them in a single file. This is for StartSSL’s Root and Intermediate CA certificates.

cd /etc/ssl
wget -O - https://www.startssl.com/certs/ca.pem https://www.startssl.com/certs/sub.class1.server.ca.pem | tee -a ca-certs.pem> /dev/null

If your CA provides certificates in DER format convert them to PEM. For example DigiCert provides certificates in DER format. To download them and convert to PEM use the following commands:

cd /etc/ssl
wget -O - https://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt | openssl x509 -inform DER -outform PEM | tee -a ca-certs.pem> /dev/null
wget -O - https://www.digicert.com/CACerts/DigiCertHighAssuranceEVCA-1.crt | openssl x509 -inform DER -outform PEM | tee -a ca-certs.pem> /dev/null

Both sets of commands use tee to write to the file, so you can use sudo tee if logged in as a non-root user.

Configuring OCSP Stapling on Apache

Edit the SSL virtual hosts file and place these lines inside the <VirtualHost></VirtualHost> directive.

sudo nano /etc/apache2/sites-enabled/example.com-ssl.conf
SSLCACertificateFile /etc/ssl/ca-certs.pem
SSLUseStapling on

A cache location has to be specified outside <VirtualHost></VirtualHost>.

sudo nano /etc/apache2/sites-enabled/example.com-ssl.conf
SSLStaplingCache shmcb:/tmp/stapling_cache(128000)

If you followed this article to setup SSL sites on Apache, the virtual host file will look this:

/etc/apache2/sites-enabled/example.com-ssl.conf

<IfModule mod_ssl.c>
    SSLStaplingCache shmcb:/tmp/stapling_cache(128000)
    <VirtualHost *:443>

            ServerAdmin webmaster@localhost
            ServerName example.com
            DocumentRoot /var/www

            SSLEngine on

            SSLCertificateFile /etc/apache2/ssl/example.com/apache.crt
            SSLCertificateKeyFile /etc/apache2/ssl/example.com/apache.key

            SSLCACertificateFile /etc/ssl/ca-certs.pem
            SSLUseStapling on
    </VirtualHost>
</IfModule>

Do a configtest to check for errors.

apachectl -t

Reload if Syntax OK is displayed.

service apache2 reload

Access the website on IE (on Vista and above) or Firefox 26+ and check the error log.

tail /var/log/apache2/error.log

If the file defined in the SSLCACertificateFile directive is missing, a certificate an error similar to the following is displayed.

[Fri May 09 23:36:44.055900 2014] [ssl:error] [pid 1491:tid 139921007208320] AH02217: ssl_stapling_init_cert: Can't retrieve issuer certificate!
[Fri May 09 23:36:44.056018 2014] [ssl:error] [pid 1491:tid 139921007208320] AH02235: Unable to configure server certificate for stapling

If no such errors are displayed proceed to the final step.

Configuring OCSP stapling on Nginx

Edit the SSL virtual hosts file and place the following directives inside the server {} section.

sudo nano /etc/nginx/sites-enabled/example.com.ssl
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/ssl/private/ca-certs.pem;

If you followed this article to setup SSL hosts on Nginx the complete virtual host file will look like this:

/etc/nginx/sites-enabled/example.com.ssl

server {

        listen   443;
        server_name example.org;

        root /usr/share/nginx/www;
        index index.html index.htm;

        ssl on;
        ssl_certificate /etc/nginx/ssl/example.org/server.crt;
        ssl_certificate_key /etc/nginx/ssl/example.org/server.key;

        ssl_stapling on;
        ssl_stapling_verify on;
        ssl_trusted_certificate /etc/ssl/private/ca-certs.pem;
}

Do a configtest to see if everything is correct.

service nginx configtest

Then reload the nginx service.

service nginx reload

Access the website on IE (on Vista and above) or Firefox 26+ and check the error log.

tail /var/log/nginx/error.log

If the file defined in ssl_trusted_certificate is missing a certificate an error similar to the following is displayed:

2014/05/09 17:38:16 [error] 1580#0: OCSP_basic_verify() failed (SSL: error:27069065:OCSP routines:OCSP_basic_verify:certificate verify error:Verify error:unable to get local issuer certificate) while requesting certificate status, responder: ocsp.startssl.com

If no such errors are displayed proceed to the next step.

Testing OCSP Stapling

Two methods will be explained to test if OCSP stapling is working - the openssl command-line tool and SSL test at Qualys.

The OpenSSL command

This command’s output displays a section which says if your web server responded with OCSP data. We grep this particular section and display it.

echo QUIT | openssl s_client -connect www.digitalocean.com:443 -status 2> /dev/null | grep -A 17 'OCSP response:' | grep -B 17 'Next Update'

Replace www.digitalocean.com with your domain name. If OCSP stapling is working properly the following output is displayed.

OCSP response:
======================================
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: 4C58CB25F0414F52F428C881439BA6A8A0E692E5
    Produced At: May  9 08:45:00 2014 GMT
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: B8A299F09D061DD5C1588F76CC89FF57092B94DD
      Issuer Key Hash: 4C58CB25F0414F52F428C881439BA6A8A0E692E5
      Serial Number: 0161FF00CCBFF6C07D2D3BB4D8340A23
    Cert Status: good
    This Update: May  9 08:45:00 2014 GMT
    Next Update: May 16 09:00:00 2014 GMT

No output is displayed if OCSP stapling is not working.

Qualys online SSL test

To check this online go to this website and enter your domain name. Once testing completes check under the Protocol Details section.

Qualys SSL report

Additional reading

If you’ve enjoyed this tutorial and our broader community, consider checking out our DigitalOcean products which can also help you achieve your development goals.

Learn more here


About the authors
Default avatar
Jesin A

author

Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?
10 Comments

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Thanks guys, great guide! Just enabled OCSP stapling on my Nginx cluster.

One thing I noticed is that the command to test the OCSP response does not work correctly with servers that use SNI. For that, you must specify the -servername flag, e.g.:

echo QUIT | openssl s_client -connect www.digitalocean.com:443 -servername www.digitalocean.com -status 2> /dev/null | grep -A 17 'OCSP response:' | grep -B 17 'Next Update'

Hi. I use Apache 2.4 and CloudFlare DNS and CloudFlare tls certificates. I have three certificate files:

  • private-key.key
  • intermediate.pem
  • root.pem

I use private-key.key for SSLCertificateKeyFile I use intermediate.pem + root.pem for SSLCACertificateFile

Which certificate file should I use in SSLCertificateFile?

I wait for help. Thanks

Can OCSP Stapling be configured globally (Apache 2.4) by editing /etc/apache2/mods-available/ssl.conf

SSLUseStapling On SSLStaplingReturnResponderErrors off SSLStaplingResponderTimeout 5 SSLStaplingCache shmcb:${APACHE_RUN_DIR}/ssl_stapling_cache(128000)

I got

OCSP response:

OCSP Response Data: OCSP Response Status: successful (0x0) Response Type: Basic OCSP Response Version: 1 (0x0) Responder Id: 3DD350A5D6A0ADEEF34A600A65D321D4F8F8D60F Produced At: Mar 27 06:42:00 2016 GMT Responses: Certificate ID: Hash Algorithm: sha1 Issuer Name Hash: 49F4BD8A18BF760698C5DE402D683B716AE4E686 Issuer Key Hash: 3DD350A5D6A0ADEEF34A600A65D321D4F8F8D60F Serial Number: 0F53BC042737F712F3856510758DE162 Cert Status: good This Update: Mar 27 06:42:00 2016 GMT Next Update: Apr 3 05:57:00 2016 GMT

But the qualys test show OCSP not enabled. Why is that ?

Thank you for great article but i cant enable OCSP stapling although i’ve got A+ on ssllabs but when i see openssl status it gives me this

 openssl s_client -connect mydomain.com:443 -status

CONNECTED(00000003)
OCSP response: no response sent
depth=0 C = XX, L = Default City, O = Default Company Ltd
verify error:num=18:self signed certificate
verify return:1
depth=0 C = XX, L = Default City, O = Default Company Ltd
verify return:1
---
Certificate chain
 0 s:/C=XX/L=Default City/O=Default Company Ltd
   i:/C=XX/L=Default City/O=Default Company Ltd
---
Server certificate
-----BEGIN CERTIFICATE-----
hidden for privacy issues
-----END CERTIFICATE-----
subject=/C=XX/L=Default City/O=Default Company Ltd
issuer=/C=XX/L=Default City/O=Default Company Ltd
---
No client certificate CA names sent
Server Temp Key: ECDH, prime256v1, 256 bits
---
SSL handshake has read 1534 bytes and written 382 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: 863864CB40C7A249E14AB4A88811C5544CD33D361420CAE
    Session-ID-ctx:
    Master-Key: 25607B28C61787443A444B2C143229A3E20387C578F2F4CC41A8D46A9D5FCA8A9BBE7995BDCFB
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    TLS session ticket lifetime hint: 300 (seconds)
    TLS session ticket:
    0000 - ac 6c ba 2a 0b -ec 48 12 ee 85 ea eb 94   .l..H......
    0010 - 46 4e c1 6d 42 a43 90 88 f1 bc 64 0f b4   FN.mB.{s.
    0020 - 58 9b 19 87 47 d-5b b9 5b 78 cf 82 c2 a6   X....[x....
    0030 - bd 5c a7 e2 22 -90 50 87 99 2b f2 5b e0   .\.."+.[.
    0040 - a4 18 ee ad 20  58-25 66 59 2d 59 02 a3 c6   .... <fX%Y...
    0050 - 86 51 a3 56 0c 0e 97 7c d7 1a ff   .Q.V..|...
    0060 - c2 53 c4 2-b8 6c db c0 f5 36 3d ef   .S.".6=.
    0070 - 0a cc ac ef 7c1 c0 db 46 1d   ....y.F.
    0080 - ad 49 e7 18 b6 f9 c0 db-e5 f0 40 a4 f0 9b 24 11   .I.....
    0090 - 06 0c 22 37 3d 51 f9 c4 b7 31 0a 03 d6   .."7=Q..1...
    00a0 - 3f 2a 73 c5 3 d1 58 29 b0 a9 75 b1   ?*.X)..u.

    Start Time: 15875
    Timeout   : 300 (sec)
    Verify return code: 18 (self signed certificate)

My .crt file in ssl_trusted_certificate has one server certificate, two intermediate and one root certi . (at least https://www.digicert.com/help/ shows that)

This comment has been deleted

Actual Retrieve the CA bundle for StartSSL

wget -O - https://www.startssl.com/certs/ca.pem https://www.startssl.com/certs/class1/sha2/pem/sub.class1.server.sha2.ca.pem | tee -a ca-certs.pem> /dev/null

@jesin I checked the error.log and it is returning this message:

2015/06/19 14:37:41 [error] 21257#0: OCSP_basic_verify() failed (SSL: error:27069065:OCSP routines:OCSP_basic_verify:certificate verify error:Verify error:unable to get issuer certificate) while requesting certificate status, responder: gk.symcd.com

I downloaded the “Root 3” from this address https://www.geotrust.com/resources/root-certificates/ and I concatenated it with the .crt intermadiate certificate that CA sent to me by e-mail.

Is this the right certificate for use?

Thanks again!

By the way, this is my config file.

  • I commented the ssl stapling lines because it isn’t working anyway.*

server { listen 80; server_name mysite.com; return 301 https://www.mysite.com$request_uri; }

server { listen 443 ssl spdy; keepalive_timeout 120; ssl on; ssl_certificate /etc/nginx/ssl/www.mysite.com.crt; ssl_certificate_key /etc/nginx/ssl/www_mysite_com.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; ssl_ciphers AES256+EECDH:AES256+EDH; ssl_dhparam /etc/nginx/ssl/dhparam.pem; ssl_session_cache shared:SSL:20m; ssl_session_timeout 20m; ssl_session_tickets on; ## ssl_stapling on; ## ssl_stapling_verify on; ## resolver 8.8.4.4 8.8.8.8 valid=300s; ## resolver_timeout 10s; ## ssl_trusted_certificate /etc/nginx/ssl/private/ca-certs.pem; add_header Strict-Transport-Security max-age=31536000; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY;

    root   /var/www/html;

    location / {
    index index.html index.php;
    try_files $uri $uri/ @handler;
    expires 30d;
    }

    ## These locations need to be denied
            location ^~ /app/                { deny all; }
            location ^~ /includes/           { deny all; }
            location ^~ /lib/                { deny all; }
            location ^~ /media/downloadable/ { deny all; }
            location ^~ /pkginfo/            { deny all; }
            location ^~ /report/config.xml   { deny all; }
            location ^~ /var/                { deny all; }

            location /var/export/ {
            auth_basic           "Restricted";
            auth_basic_user_file htpasswd; ## Defined at /etc/nginx/htpassword
            autoindex            on;
            }
	
            location ~* \.(asf|asx|wax|wmv|wmx|avi|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|mpp|odb|odc|odf|odg|odp|ods|odt|ogg|ogv|otf|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|t?gz|tif|tiff|ttf|wav|webm|wma|woff|wri|xla|xls|xlsx|xlt|xlw|zip)$ {
            expires 31536000s;
            access_log off;
            log_not_found off;
            add_header Pragma public;
            add_header Cache-Control "max-age=31536000, public, must-revalidate, proxy-revalidate";
            }

    ## Disable .htaccess and other hidden files
            location ~ /\. { deny all; error_log off; log_not_found off; }
            ##return 404;

    ## ignore common 404s
            location = /robots.txt  { access_log off; log_not_found off; }
            location = /favicon.ico { access_log off; log_not_found off; }

	## Magento uses a common front handler
            location @handler {
            rewrite / /index.php;
            }

    ## Forward paths like /js/index.php/x.js to relevant handler
            location ~ .php/ {
            rewrite ^(.*.php)/ $1 last;
            }

    ## php-fpm parsing
            location ~ \.php$ {
            limit_req zone=limit burst=10 nodelay;

    ## Catch 404s that try_files miss
            if (!-e $request_filename) { rewrite / /index.php last; }
            try_files $uri =404;

    ## Disable cache for php files
            expires        off;

	## php-fpm configuration
            fastcgi_pass   unix:/var/run/php5-fpm.sock;
            fastcgi_param  HTTPS on;
            fastcgi_param  HTTPS $fastcgi_https;
            fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
            fastcgi_param  SERVER_NAME      $host;
            fastcgi_index  index.php;
            include        fastcgi_params;

    ## Store code is located at Administration > Configuration > Manage Stores in your Magento Installat$
            fastcgi_param  MAGE_RUN_CODE default;
            fastcgi_param  MAGE_RUN_TYPE store;

    ## Tweak fastcgi buffers, just in case.
            fastcgi_buffer_size 128k;
            fastcgi_buffers 256 4k;
            fastcgi_busy_buffers_size 256k;
            fastcgi_temp_file_write_size 256k;
            }
            rewrite ^/minify/([0-9]+)(/.*.(js|css))$ /lib/minify/m.php?f=$2&d=$1 last;
            rewrite ^/skin/m/([0-9]+)(/.*.(js|css))$ /lib/minify/m.php?f=$2&d=$1 last;

}

Hi @jesin, they provided only the intermediate certificate. The other one has my name. I followed a tutorial from here to convert these two files and install it in my server. Thanks for your response and support.

Hi Jesin, I’m using a Geotrust SSL-EV in my site. The certificate comes in .crt and .key extentions.

May you help me with the command line to retrieve the CA bundle from Geotrust and convert it to .pem?

Thank you very much in advance!