Tutorial

How To Secure Nginx with Let's Encrypt on Ubuntu 14.04

How To Secure Nginx with Let's Encrypt on Ubuntu 14.04
Not using Ubuntu 14.04?Choose a different version or distribution.
Ubuntu 14.04

Introduction

Let’s Encrypt is a new Certificate Authority (CA) that provides an easy way to obtain and install free TLS/SSL certificates, thereby enabling encrypted HTTPS on web servers. It simplifies the process by providing a software client, Certbot, that attempts to automate most (if not all) of the required steps. Currently, the entire process of obtaining and installing a certificate is fully automated on both Apache and Nginx web servers.

In this tutorial, we will show you how to use Certbot to obtain a free SSL certificate and use it with Nginx on Ubuntu 14.04 LTS. We will also show you how to automatically renew your SSL certificate.

We will use the default Nginx configuration file in this tutorial instead of a separate server block file. We recommend creating new Nginx server block files for each domain because it helps to avoid some common mistakes and maintains the default files as a fallback configuration as intended. If you want to set up SSL using server blocks instead, you can follow this Nginx server blocks with Let’s Encrypt tutorial.

Prerequisites

Before following this tutorial, you’ll need a few things.

  • An Ubuntu 14.04 server with a non-root user who has sudo privileges. You can learn how to set up such a user account by following our initial server setup for Ubuntu 14.04 tutorial.
  • Nginx installed, How To Install Nginx on Ubuntu 14.04 LTS
  • You must own or control the registered domain name that you wish to use the certificate with. If you do not already have a registered domain name, you may register one with one of the many domain name registrars out there (e.g. Namecheap, GoDaddy, etc.).
  • A DNS A Record that points your domain to the public IP address of your server. You can follow this hostname tutorial for details on how to add them. This is required because of how Let’s Encrypt validates that you own the domain it is issuing a certificate for. For example, if you want to obtain a certificate for example.com, that domain must resolve to your server for the validation process to work. Our setup will use example.com and www.example.com as the domain names, so both DNS records are required.

Once you have all of the prerequisites out of the way, let’s move on to installing Certbot, the Let’s Encrypt client software.

Step 1 — Installing Certbot

The first step to using Let’s Encrypt to obtain an SSL certificate is to install the certbot software on your server. The Certbot developers maintain their own Ubuntu software repository with up-to-date versions of the software. Because Certbot is in such active development it’s worth using this repository to install a newer Certbot than provided by Ubuntu.

First, add the repository:

  1. sudo add-apt-repository ppa:certbot/certbot

You’ll need to press ENTER to accept. Afterwards, update the package list to pick up the new repository’s package information:

  1. sudo apt-get update

And finally, install Certbot with apt-get:

  1. sudo apt-get install python-certbot-nginx

The certbot Let’s Encrypt client is now ready to use.

Step 2 — Setting up Nginx

Certbot can automatically configure SSL for Nginx, but it needs to be able to find the correct server block in your config. It does this by looking for a server_name directive that matches the domain you’re requesting a certificate for. If you’re starting out with a fresh Nginx install, you can update the default config file:

  1. sudo nano /etc/nginx/sites-available/default

Find the existing server_name line:

/etc/nginx/sites-available/default
server_name localhost;

Replace localhost with your domain name:

/etc/nginx/sites-available/default
server_name example.com www.example.com;

Save the file and quit your editor. Verify the syntax of your configuration edits with:

  1. sudo nginx -t

If that runs with no errors, reload Nginx to load the new configuration:

  1. sudo service nginx reload

Certbot will now be able to find the correct server block and update it. Now we’ll update our firewall to allow HTTPS traffic.

Step 3 — Obtaining an SSL Certificate

Certbot provides a variety of ways to obtain SSL certificates, through various plugins. The Nginx plugin will take care of reconfiguring Nginx and reloading the config whenever necessary:

  1. sudo certbot --nginx -d example.com -d www.example.com

This runs certbot with the --nginx plugin, using -d to specify the names we’d like the certificate to be valid for.

If this is your first time running certbot, you will be prompted to enter an email address and agree to the terms of service. After doing so, certbot will communicate with the Let’s Encrypt server, then run a challenge to verify that you control the domain you’re requesting a certificate for.

If that’s successful, certbot will ask how you’d like to configure your HTTPS settings:

Output
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. ------------------------------------------------------------------------------- 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. ------------------------------------------------------------------------------- Select the appropriate number [1-2] then [enter] (press 'c' to cancel):

Select your choice then hit ENTER. The configuration will be updated, and Nginx will reload to pick up the new settings. certbot will wrap up with a message telling you the process was successful and where your certificates are stored:

Output
IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/example.com/fullchain.pem. Your cert will expire on 2017-10-23. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le

Your certificates are now downloaded, installed, and configured. Try reloading your website using https:// and notice your browser’s security indicator. It should represent that the site is properly secured, usually with a green lock icon. If you test your server using the SSL Labs Server Test, it will get an A grade.

Step 4 — Verifying Certbot Auto-Renewal

Let’s Encrypt’s certificates are only valid for ninety days. This is to encourage users to automate their certificate renewal process. The certbot package we installed takes care of this for us by running ‘certbot renew’ twice a day via a systemd timer. On non-systemd distributions this functionality is provided by a script placed in /etc/cron.d. This task runs twice a day and will renew any certificate that’s within thirty days of expiration.

To test the renewal process, you can do a dry run with certbot:

  1. sudo certbot renew --dry-run

If you see no errors, you’re all set. When necessary, Certbot will renew your certificates and reload Nginx to pick up the changes. If the automated renewal process ever fails, Let’s Encrypt will send a message to the email you specified, warning you when your certificate is about to expire.

Conclusion

In this tutorial we’ve installed the Let’s Encrypt client certbot, downloaded SSL certificates for our domain, configured Nginx to use these certificates, and set up automatic certificate renewal. If you have further questions about using Certbot, their documentation is a good place to start.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
170 Comments
Leave a comment...

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!

Great article, and you were crazy-fast with the publication - a jiffy after the public beta is out. Kudos!

This comment has been deleted

    great job, i was looking for how to do it on nginx, set it up easy enough for an owncloud server running apache, this made it super easy to secure a reverse proxy i am running using nginx, which i prefer. thanks!

    Line

    netstat -na | 'grep :80.*LISTEN'
    

    should be

    netstat -na | grep ':80.*LISTEN'
    
    Mitchell Anicas
    DigitalOcean Employee
    DigitalOcean Employee badge
    December 18, 2015

    Thanks for catching that. Fixed.

    Does port 80 have to be always open in order for this to work? I mean, what if I have some websites that are still http, so nginx would be listening to port 80 for them…

    Do I have to set https for all my websites and make sure the the port 80 is not used by nginx ever, in order for this standalone plugin to work?

    Mitchell Anicas
    DigitalOcean Employee
    DigitalOcean Employee badge
    December 18, 2015

    The standalone plugin can work either on port 80 or 443 (if you specify a different option). Because it runs its own web server, one of those ports needs to be open when you request/renew a certificate.

    The webroot plugin, that is used in the renew section, requires that your web server responds on port 80 and can serve the hidden directory is created.

    This is a great tutorial on lets encrypt with nginx. I have got pretty much everything working as expected. I’m not certain if it’s required but I made the ‘root’ directory as specified in the .ini file to be writeable by the user under which the web server runs. I will experiment further to see if this is really necessary.

    A question :

    The script that is run under cron to check for the renewal status of the certificates - does this need to be run as a user that can ‘sudo’ without a password ? I would have thought this to be the case but it is not mentioned here or in the tutorial for setting sudo up to which is referred. If so, would it be wise to limit the commands that can be run by this user to be just the renewal script and nothing else ?

    Mitchell Anicas
    DigitalOcean Employee
    DigitalOcean Employee badge
    December 21, 2015

    Hey there. Yes, the script needs to be run by a user that has superuser privileges without a password. The sudo crontab -e command edits the root user’s crontab, so the script will run as root.

    It is possible to set up a separate user with passwordless sudo, if you want.

    Great article!

    Small detail: you are downloading the le-renew-webroot script to /usr/local/sbin, but have /usr/sbin/le-renew-webroot in the crontab

    Great article, thanks!

    Great article, thank you very much!

    I’ve followed the steps to allow only the most secure SSL protocols and ciphers. However, Chromium says my web is encrypted with an obsolete ciphersuite (roughly, because I’m using the Spanish version). It uses AES-256-CBC, HMAC-SHA1 and ECDHE_RSA. Should it use HMAC-SHA256 instead? Why is this happening?

    The same issue I am facing and I would appreciate to get advice

    I was getting the same message from Chrome. After reading this article, I changed the ssl_ciphers configuration to something like:

    ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
    

    And it works fine. BTW, I recommend reading the full article. And I also recommend this tool to verify your SSL configuration.

    That test is awesome, thank you!

    I solved it using Mozilla SSL Configuration Generator. I think it’s really useful.

    The ./letsencrypt-auto certonly --standalone command seems to fail, telling me that the domain name contains invalid characters.

    I believe this is supposed to be: ./letsencrypt-auto certonly --standalone -d example.com

    Really easy, I’m loving it!

    i’m having errors, I believe it’s because my nginx configuration (from the one-click-app drupal install) blocks hidden directories. Could you perhaps add a block to the nginx configuration that allows the .well-known/acme-challenge directory

    I had the same problem but found this elegant solution:

    server { listen 80; listen [::]:80; server_name example.net example.org; location ‘/.well-known/acme-challenge’ { default_type “text/plain”; root /tmp/letsencrypt-auto; }

    location / { return 301 https://$server_name$request_uri; } }

    https://gist.github.com/renchap/c093702f06df69ba5cac

    This way you can also remove the location from your ssl sever configuration.

    I’m also having problems, not sure if its the same. Its says “The server could not connect to the client to verify the domain”

    I tried the solution as provided by the link, but also doesnt work. Now I have a few questions, do you keep the server_name as example.net and example.org? Or do you change it to your domain names? I tried both and didn’t work. Also, with $server_name$request_uri, do you change anything?

    Im using a Rails droplet on Ubuntu 14.04 with Nginx. All help appreciated!

    First of all, thank you for this very nice article! I used this on a Centos 7.2 box and it worked like a charm!

    I ran into an ‘issue’, I got only a B at SSL labs using this configuration. I fixed it by generating a custom diffie hellman 2048 primes set as described on this page.

    https://weakdh.org/sysadmin.html

    Maybe you can update your guide with this, if you find it relevant.

    Thanks, great article! It worked on my Debian 8 minimal installation, with only one little glitch: I had to install bc tool manually, in order to get the le-renew-webroot script to work.

    Thanks for this great tutorial!

    I’m sure that, with advanced bash scripting skills, it should be possible to adapt the le-renew-webroot script to handle multiple virtual hosts, but my bash skills are not what they should be.

    In the meantime, will it be okay to create a script and a matching cli config file for each virtual host?

    E.g.

    /usr/local/sbin/le-renew-mywebsite.com
    /usr/local/sbin/le-renew-anotherwebsite.org
    ...
    

    with corresponding config files:

    /usr/local/etc/le-renew-mywebsite.com.ini
    /usr/local/etc/le-renew-anotherwebsite.org.ini
    ...
    

    Thanks.

    I created a simple script for multiply domains github gist here

    Thanks for this. If I’m reading the code correctly, it looks as if I’ll need a .ini file for each domain, but I will need just one bash script. That will help a lot.

    Sorry, i forgot the instructions: in script the var ‘config_path’, by default it is ‘/usr/local/letssl/’. Put there .ini file for each domain/subdomain(if it have another root directory).

    Great article, thanks !

    I guess there is a little mistake here :

    30 2 * * 1 /usr/sbin/le-renew-webroot >> /var/log/le-renewal.log
    

    It should be

    30 2 * * 1 /usr/local/sbin/le-renew-webroot >> /var/log/le-renewal.log
    

    Mitchell,

    Excellent write up! Can’t give you enough kudos for this article, great stuff : )

    I have a nginx reverse-proxy listening to port 80 and 443 (with HTTPS enabled), it routes the traffic to another nginx web server sitting behind. In this scenario, am I able to use the script to auto-renew certs? I would assume the answer is no since the website has no port80 directly exposing to internet?

    or is the reverse-proxy able to take the role to renew the certs?

    I think yes. You just need to map the location /.well-known to static file, while keeping other urls proxied.

    Could you provide a piece of config file related to this? I can’t figure how to use renew with nginx reverse proxy

    I would also like to see a little more detail regarding this if possible, as I have the same set up.

    This comment has been deleted

      This is great - thank you very much! It helped me a lot… :)

      You may want to add in ‘Nginx configuration additions — 3 of 3’ your domain with www before (to prevent errors in the renewal step):

          server_name example.com www.example.com;
      

      Also I had to install bc to make your script work:

      sudo apt-get install bc
      

      I need to publish both 80 and 443 ports in a docker container in order to successfuly build the certs. Is it missed in the tutorial (in verfy ports open section) or am I doing something wrong?

      This comment has been deleted

        Awesome tutorial. Exactly what I needed. Thanks!

        If you are moving your server and domain name to new hardware, will it be sufficient to restore your /etc/letsencrypt dir from backup? Assuming the nginz configuration is the same. I’m thinking about how to migrate an existing setup. Specially an EC2 instance with elastic IP.

        Thanks.

        For the renewal script to execute correctly in my environment, I needed “bc” to be installed. For Ubuntu:

        sudo apt-get install bc
        

        Hi there, I’m getting an error when trying to follow Step 4. Here’s my terminal:

        [redacted]:/opt/letsencrypt# ./letsencrypt-auto certonly -a webroot --agree-tos --renew-by-default --webroot-path=/usr/share/nginx/html -d ghostlyco.de -d www.ghostlyco.de
        Updating letsencrypt and virtual environment dependencies......
        Requesting root privileges to run with virtualenv: /root/.local/share/letsencrypt/bin/letsencrypt certonly -a webroot --agree-tos --renew-by-default --webroot-path=/usr/share/nginx/html -d ghostlyco.de -d www.ghostlyco.de
        Failed authorization procedure. www.ghostlyco.de (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://www.ghostlyco.de/.well-known/acme-challenge/[redacted] [45.55.95.134]: 404, ghostlyco.de (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://ghostlyco.de/.well-known/acme-challenge/[redacted] [45.55.95.134]: 404
        
        IMPORTANT NOTES:
         - The following 'urn:acme:error:unauthorized' errors were reported by
           the server:
        
           Domains: ghostlyco.de, www.ghostlyco.de
           Error: The client lacks sufficient authorization
        

        Not sure what’s causing that, any ideas?

        EDIT: I suspect the webroot is incorrect, cding in to /usr/share/nginx/html and runing ls doesn’t appear to contain the files for my ghost installation. I used the single click installer for Ghost 0.7.4 on Ubuntu 14.04 LTS. Looking in to why the grep would be returning that path if that’s not the path used…

        EDIT 2: Okay, I think the correct web root for me is /var/www/ghost, but putting that in the command throws the same error. Hmm.

        @revxx14 See this comment above for a possible explanation of what you might be experiencing.

        Thanks much! Adding a new location like so fixed the issue!

        location /.well-known {
               autoindex on;
        }
        

        Not 100% sure this is the correct way to do it, but it works so I’m happy for now.

        Actually, I think I found the proper configuration:

        location /.well-known {
               allow all;
        }
        

        This lets the certificate renew, but also throws a 403 for anyone trying to access the directory from a browser.

        Source: http://stackoverflow.com/a/34262192/654480

        Did you add this to the configuration of the https or the regular http?

        I am having same / similar problem - started with one click drupal - I have the certificate working but the ‘auto renew’ won’t work. I tried adding the @revxx14 location /.well-known block to both the 443 and 80 server sections…

        the script does create the .well-known folder in the docroot - so it has the “power” … but i think for some reason later in the script it cant find what it wrote?? http vs https maybe???

        Good question :) I’m terrible with all this server junk, but I do see that the original post has this information in it already. I must’ve either missed it initially or maybe they updated the post? Not sure. Anyway, it appears this is in the SSL block, in /etc/nginx/sites-enabled/ghost (for me at least, “ghost” may be something different for you).

        Mitchell Anicas
        DigitalOcean Employee
        DigitalOcean Employee badge
        January 11, 2016

        @revxx14 I updated the tutorial. I’ll be adapting the guide with generally useful configuration as I come across it.

        @manicas good stuff, thanks for letting me know I’m not crazy :) You did a great job on this tutorial if even a server noob like me could follow along!

        I had the same problem! This worked for me! Thx alot!

        Domain must be pointed to the server when running letsencrypt.

        I created a simple script for multiply domains github gist here

        This comment has been deleted

          Great tutorial. Here’s a few things that I learned during the process, hopefully to help others:

          • If your domain is having a connection issue, make sure you have www.example.com and example.com setup in DO’s Networking -> Domains section. I only had the example.com setup, thus Let’s Encrypt wasn’t able to properly connect.
          • Also, adding the following when running the Let’s Encrypt script will help debug: --text -vv

          Thanks for the tutorial!

          There’s a typo on the “well-known” location block. The path is currently written as /.well_known (with an underscore), but it should be /.well-known (with a dash).

          Is it possible to revoke certs after playing around with letsencrypt ?! I want to delete all created certs and create new certs. thanks so far.

          I’m not sure what the underlying issue is, but I found that I was getting 403 errors until I changed the location block for well-known to:

          location ~ /.well-known { allow all; }

          Hope that helps someone (or at the very least the future me who comes back to look at this article!)

          This fixed my drupal problem, not the above solution, thanks!

          You’re very welcome! I don’t actually know why it makes a difference. I found the answer somewhere else on the web but forgot to make a note of it.

          This suggests that the tilde simply forces a RegEx match (and as a result a case-sensitive match), but I’m not sure why that would solve the problem… Anyone?

          I’ve read through all the comments, but I keep getting the following error.

          Failed authorization procedure. www.xxxxx.xxxxx (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://www.xxxxx.xxxxx/.well-known/acme-challenge/5ZcvS9HmCGKTz8EzxNEvJPfgPNCBgTyjCS75RvWzTxU [xxx.xxx.xxx.xxx]: 404

          I’ve added the necessary code (see below) to the server block and restarted nginx. I’ve checked that the .well-known folder is in my web root which is reflected in server block. Does anyone have any suggestions? I’m at a complete loss!

                allow all;
             }```
          
          I just looked in the acme-challenge directory and I don't see any files. Is 5ZcvS9HmCGKTz8EzxNEvJPfgPNCBgTyjCS75RvWzTxU supposed to be in there per the error?

          OK, I figured out my error. It was a typo in the DNS cname so www.xxxxxx.xxx wasn’t going to the proper location hence the 404 error.

          Really helpful! Question though, if say I’ve added a new sub domain, how do I add it in Let’s Encrypt? Note that all the previous sub domains shares the same cert.

          One small problem I had, when I got to “How To Use the Webroot Plugin” the

          ./letsencrypt-auto certonly -a webroot --agree-tos --renew-by-default --webroot-path=/usr/share/nginx/html -d example.com -d www.example.com did not want to execute.

          Solved this problem was that we had setup a redirect from port 80 to 443 earlier, remove this and it will work if anyone else has this problem.

          Hi can you elaborate on it ? please , I have a similar problem :

          ./letsencrypt-auto certonly -a webroot --agree-tos --renew-by-default --webroot-path=/usr/share/nginx/html -d example.com -d www.example.com
          
          Updating letsencrypt and virtual environment dependencies......
          
          Requesting root privileges to run with virtualenv: sudo /home/yves/.local/share/letsencrypt/bin/letsencrypt certonly -a webroot --agree-tos --renew-by-default --webroot-path=/usr/share/nginx/html -d example.com -d www.example.com
          
          Failed authorization procedure. www.example.com (http-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Could not connect to http://www.example.com/.well-known/acme-challenge/khtmOTs56K5sRXSmOpHJawQzAlefXMsx8Q918zJnnrY, example.com (http-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: Could not connect to http://example.com/.well-known/acme-challenge/9ANsS0S3B1oQCSvH05UDNh-cDPmGF6_bu-0jBppcCe8
          
          IMPORTANT NOTES:
           - The following errors were reported by the server:
          
             Domain: www.example.com
             Type:   urn:acme:error:connection
             Detail: Could not connect to http://www.example.com/.well-known
             /acme-challenge/khtmOTs56K5sRXSmOpHJawQzAlefXMsx8Q918zJnnrY
          
             Domain: example.com
             Type:   urn:acme:error:connection
             Detail: Could not connect to http://example.com/.well-known/acme-
             challenge/9ANsS0S3B1oQCSvH05UDNh-cDPmGF6_bu-0jBppcCe8
          

          Did you hide your domain for this post and change it with www.example.com?

          If this is the original text swap www.example.com with your website.

          For me it would be:

          ./letsencrypt-auto certonly -a webroot --agree-tos --renew-by-default --webroot-path=/usr/share/nginx/html -d dulic.me
          

          Note I don’t have a “www” version because its disabled for my domain, if its enabled for yours include it.

          yes … I’m using my domain… I reread the post and re-run …; using sudo , and it works fine , now but I have a warning which doesn’t seems important about my user home .cache directory and pip… I guess it’s linked to the Webfoot plugin. The certificate update est correctly performed however…

          The directory '/home/yves/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
          The directory '/home/yves/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
          

          Glad it worked out :) Hope you can solve the .cache error.

          I’m using Drupal 7 with NGINX droplet. Our test/dev server all works well. However when I enable this on the live production site all of the http does indeed forward to https but when I try the login to the Drupal admin the page stays “stuck” and does not load the admin menu. Is there a directive/parameter that could be causing this?

          I enabled debug on the log and I get these error: “recv() not ready (11: Resource temporarily unavailable)”

          Could there be a problem where a dev and live site are running on the same instance of NGINX? The dev site Drupal logs in without any problems.

          Entire log snip:

          2016/01/20 21:53:45 [debug] 9058#0: *72 write new buf t:1 f:0 0000000002578D60, pos 0000000002578D60, size: 215 file: 0, size: 0 2016/01/20 21:53:45 [debug] 9058#0: *72 http write filter: l:0 f:0 s:215 2016/01/20 21:53:45 [debug] 9058#0: *72 http output filter “/user?” 2016/01/20 21:53:45 [debug] 9058#0: *72 http copy filter: “/user?” 2016/01/20 21:53:45 [debug] 9058#0: *72 image filter 2016/01/20 21:53:45 [debug] 9058#0: *72 xslt filter body 2016/01/20 21:53:45 [debug] 9058#0: *72 http postpone filter “/user?” 0000000002578EA8 2016/01/20 21:53:45 [debug] 9058#0: *72 write old buf t:1 f:0 0000000002578D60, pos 0000000002578D60, size: 215 file: 0, size: 0 2016/01/20 21:53:45 [debug] 9058#0: *72 write new buf t:0 f:0 0000000000000000, pos 00000000006C6460, size: 132 file: 0, size: 0 2016/01/20 21:53:45 [debug] 9058#0: *72 write new buf t:0 f:0 0000000000000000, pos 00000000006C6740, size: 61 file: 0, size: 0 2016/01/20 21:53:45 [debug] 9058#0: *72 http write filter: l:1 f:0 s:408 2016/01/20 21:53:45 [debug] 9058#0: *72 http write filter limit 0 2016/01/20 21:53:45 [debug] 9058#0: *72 writev: 408 2016/01/20 21:53:45 [debug] 9058#0: *72 http write filter 0000000000000000 2016/01/20 21:53:45 [debug] 9058#0: *72 http copy filter: 0 “/user?” 2016/01/20 21:53:45 [debug] 9058#0: *72 http finalize request: 0, “/user?” a:1, c:1 2016/01/20 21:53:45 [debug] 9058#0: *72 set http keepalive handler 2016/01/20 21:53:45 [debug] 9058#0: *72 http close request 2016/01/20 21:53:45 [debug] 9058#0: *72 http log handler 2016/01/20 21:53:45 [debug] 9058#0: *72 free: 0000000002577EE0, unused: 8 2016/01/20 21:53:45 [debug] 9058#0: *72 free: 0000000002515720, unused: 2667 2016/01/20 21:53:45 [debug] 9058#0: *72 free: 0000000002517FC0 2016/01/20 21:53:45 [debug] 9058#0: *72 hc free: 0000000000000000 0 2016/01/20 21:53:45 [debug] 9058#0: *72 hc busy: 0000000000000000 0 2016/01/20 21:53:45 [debug] 9058#0: *72 tcp_nodelay 2016/01/20 21:53:45 [debug] 9058#0: *72 reusable connection: 1 2016/01/20 21:53:45 [debug] 9058#0: *72 event timer add: 19: 65000:1453344890997 2016/01/20 21:53:45 [debug] 9058#0: *72 post event 000000000254D208 2016/01/20 21:53:45 [debug] 9058#0: *72 delete posted event 000000000254D208 2016/01/20 21:53:45 [debug] 9058#0: *72 http keepalive handler 2016/01/20 21:53:45 [debug] 9058#0: *72 malloc: 0000000002517FC0:1024 2016/01/20 21:53:45 [debug] 9058#0: *72 recv: fd:19 -1 of 1024 2016/01/20 21:53:45 [debug] 9058#0: *72 recv() not ready (11: Resource temporarily unavailable) 2016/01/20 21:53:45 [debug] 9058#0: *72 free: 0000000002517FC0 2016/01/20 21:53:46 [debug] 9058#0: epoll del event: fd:10 op:2 ev:00000000 2016/01/20 21:53:46 [debug] 9060#0: epoll add event: fd:10 op:1 ev:00000001 2016/01/20 21:53:46 [debug] 9060#0: epoll add event: fd:12 op:1 ev:00000001 2016/01/20 21:53:46 [debug] 9058#0: epoll del event: fd:12 op:2 ev:00000000

          Not sure how it works with Drupal, but in WP you need to enable SSL in the wp-admin.php configuration file in order for it to work.

          Maybe there is a similar fix for Drupal?

          Thank you so much, great article!

          Can a novice user to solve this problem? After; –standalon and ./letsencrypt-auto certonly

          Failed authorization procedure. mydomain.com (tls-sni-01): urn:acme:error:tls : : The server experienced a TLS error during domain verification :: Failed to con nect to host for DVSNI challenge, www.mydomain.com (tls-sni-01): urn:acme:error :tls :: The server experienced a TLS error during domain verification :: Failed to connect to host for DVSNI challenge

          IMPORTANT NOTES:

          • The following errors were reported by the server:

            Domain: mydomain.com Type: urn:acme:error:tls Detail: Failed to connect to host for DVSNI challenge

            Domain: www.mydomain.com Type: urn:acme:error:tls Detail: Failed to connect to host for DVSNI challenge

          thanks!

          This comment has been deleted

            I’m a bit of a novice especially with linux, can anyone help me solve me issue. After I run ./letsencrypt-auto certonly --standalone I get the following error: Deserialization error: The following field are required: uri,new_authzr_uri more of log here

            Same thing with the webroot plugin.

            I have tried searching but I cannot find anyone with similar issue.

            Edit: I turns out after completely removing all letsencrypt folders (~/.local/share/letsencrypt, /var/lib/letsencrypt etc) etc and reinstallig it seems to be working correctly now. I think it broke because I didn’t have enough memory/swap when I originally installed

            Thanks for the article If node js server with express is working in nginx Its mean that only nginx need to have this ssl procedure? I saw tuts where the https was done at node express side but nginx was not there.

            Mitchell Anicas
            DigitalOcean Employee
            DigitalOcean Employee badge
            January 21, 2016

            If Nginx is handling the SSL, you don’t need to set up SSL on your Node server. However, the reverse proxy traffic (between Nginx and Node) will not be encrypted.

            This comment has been deleted

              I’m getting SNIMissingWarning. Does anyone know how to solve this? I followed the instructions on the site but no dice. Thanks!

              Response from the standalone plugin was:

              Type:   urn:acme:error:connection
              Detail: Failed to connect to host for DVSNI challenge
              

              Everything looks ok and I have an a record for each of the domains

              PORT     STATE  SERVICE
              80/tcp   closed http
              

              Any ideas?

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              February 1, 2016

              Is your firewall blocking 80 or 443?

              Yes, 443 wasn’t open. Opened it and it worked thanks.

              Just a small issue:

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

              Only specifies the domain and is not listening for subdomain: www

              If you receive the misleading error “Failed to connect to host for DVSNI challenge”, it means that LetsEncrypt is unable to connect to YOUR server for validation, not the other way around.

              Check your DNS propagation and firewall status (I had to completely disable UFW before it would work).

              Also if you have iptables firewall enabled, make sure that port 443 is enabled to get the certificate. -A INPUT -p tcp -m tcp --dport 443 -j ACCEPT

              I think it’s worth mentioning in the post. Could have saved a lot of my time :) Thanks!

              What a great tutorial ! Everything went smoothly.

              I follwed these instructions and everything is working but only if I run nginx as root? Are there any steps I missed that might resolve this?

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              February 10, 2016

              Maybe change the ownership of your cert files

              I’ve been spending the last few hours banging my head trying to figure out how to give rx permission to the /etc/letsencrypt/live directory to www-data.

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              February 11, 2016

              You could just do something like:

              1. sudo chown -R username /etc/letsencrypt

              Hi, quick two questions,

              if I am using multiple server blocks. Commenting out the default server statement, would it be a problem?

              Also using multiple server blocks and multiple domains, do I put all the domains into that single cert creation, or do I follow the tutorial multiple times to create a few of them?

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              February 11, 2016

              If your setup works fine, just add the relevant cert lines to your existing server blocks. This tutorial was written for a new Nginx install.

              If they are completely different domains (e.g. example.com and whatever.com), follow the tutorial multiple times. If they are simply subdomains with related content, you can go either way.

              Thanks for your reply!!! =D But what about the part that says to delete out/comment out the default server line. Don’t we still need a default server set up? Or with https, we don’t set one?

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              February 16, 2016

              Slightly later in the tutorial, you will add a server block that listens on port 80 and redirects the traffic to port 443 (HTTPS).

              But what about the default server?

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              February 17, 2016

              You don’t need to specify default_server unless you’re using name-based resolution, but you can add it to whichever server block you want.

              This is a great tutorial. Exactly what I am looking for. I am curious. Is there a way I can do a dry run of using vagrant or something.

              I would like to try this out before doing this on my one and only live server.

              Fantastic!. One of the smoothest tutorials ever :)

              Thanks for the great tutorial!

              One small typo here (/.well-known vs ./well-known)

              Thanks for the great article!

              One small typo here

              Hello

              Great article, however I installed it on the worng server. I controlled-c to stop the ./letsencrypt-auto certonly --standalone process. Can I just delete it?

              I did similar to what the article said to do, and it gives me a redirect loop when trying to access https or http:

              server {
              	listen 443 ssl;
              
              	server_name example.com www.example.com;
              
              	ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
              	ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
                      
              	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
                      ssl_prefer_server_ciphers on;
                      ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
              }
              
              server {
              	listen 80 default_server;
              
              	root /var/www/html;
              
              	# Add index.php to the list if you are using PHP
              	index index.php;
              
              	server_name example.com;
              	
              	return 301 https://$server_name$request_uri;
              	
              	error_page 500 /500.html;
              	error_page 404 /404.php;
              
              	location / {
              		try_files $uri $uri/ =404;
              	}
              
              	location ~ \.php$ {
              	    try_files $uri =404;
              	    fastcgi_split_path_info ^(.+\.php)(/.+)$;
              	    fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;
              	    fastcgi_index index.php;
              	    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
              	    include fastcgi_params;
              	}
              }
              
              

              Am I doing something wrong?

              When I run the renew command, it returns a 404 error.

              I also fixed the redirect loop.

              This is a great tutorial! Thank you for that good work!

              I’d like to ask you for some additional help:

              I’ve added a header for HPKP on nginx. If the automatic renewal cronjob does his job, the HPKP-line has to be updated to. What should I add to the script you provided to do something like that (I don’t know the scripting things only the commands for getting the sha-pin:

              # First the actual sha-pin has to be calculated from the actual cert.pem (that what was created in your tutorial:
              
              openssl x509 -noout -in cert.pem -pubkey | \
              openssl asn1parse -noout -inform pem -out public.key;
              openssl dgst -sha256 -binary public.key | openssl enc -base64
              
              # This command will give a sha-pin which maybe stored in a variable called var_old_cert
              # The commands above must be run again after the certificate renewal and that answer will be stored in var_new_cert
              # Then the nginx config files must be searched for var_old_cert and have to be changed to var_new_cert
              

              Does that make sense for you? How to make that in your provided script. It is just an idea…

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              February 25, 2016

              Probably the easiest way to do this is to redirect the output to a file including the proper Nginx directives. Then include the file in your Nginx configuration, in the appropriate location.

              Great article! Everything works like charm!

              That was really helpful Thank you.

              Thank a lot for great tutorial!

              Hi, thanks for this tutorial.

              I got this message: sudo: curl: command not found after adding this line:

              sudo curl -L -o /usr/local/sbin/le-renew-webroot https://gist.githubusercontent.com/thisismitch/e1b603165523df66d5cc/raw/fbffbf358e96110d5566f13677d9bd5f4f65794c/le-renew-webroot
              

              I’m using Ubuntu 15.10 x64 Nginx + php-fpm 7.0.3

              Just to make some of our lives a little more easy, I’ve created a little script to automatically create all the Nginx conf files, directories en all the Let’s Encrypt certificates, with one little command.

              https://github.com/cryptocake/le-nginx-site-adder

              Be sure to have letsencrypt in: /opt/letsencrypt/ and your webroot: /var/www

              Just by executing: sudo ./add-website example.com You will get the website up and running instantly in SSL.

              Hope you guys find it useful. Regards

              great guide… you are a hero…

              Thank you so much for this tutorial!

              I do have to note:

              “Under the ssl server block, add this location block”

              I understood that as below the ssl server block. I did that and nginx would silently fail while restarting. Once I put the location block in Step 4 inside the server block, everything started working.

              Mitchell, you might want to modify this to be more clear. Let me know if I’m wrong though!

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              March 1, 2016

              Thanks! I edited it for clarity.

              Thank for this. I have an issue at step 3. Nginx redirect to a https connection but I cannot connect to my web site.

              ‘Web page not available’

              It was working well before.

              Any idea why ?

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              March 4, 2016

              Check that your server block config is linked in /etc/nginx/sites-enabled.

              Great article.

              Thank you so much.

              But I get error when access site:

              The abc.com page isn’t working
              abc.com redirected you too many times.
              ERR_TOO_MANY_REDIRECTS
              

              Could you please help me?

              Thanks & Best Regards.

              Guys, do you plan to make a similar guide for Cent OS as well ? I’m using Nginx on CentOS and a guide would be very helpful.

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              March 5, 2016

              Since most people do this in a fresh Ubuntu install, it’d better to mention that a “sudo apt-get install -y curl” is necessary in “Create a Renewal Script” section.

              I am using nginx to host a meteor app.

              I don’t understand what look up your document root is set to by searching for the root directive is for a meteor app.

              I am using nginx to host a meteor app.

              I don’t understand what look up your document root is set to by searching for the root directive is for a meteor app.

              #note I had to issue as / to get ./letsencrypt-auto to run w/out errors $ sudo ./letsencrypt-auto certonly --standalone

              I might be asking a silly question, but I am making a nginx setup for someone else, and trying to run it through the Ajenti V console.

              I added the webroot config to the Advanced > Custom configuration section in ajenti, but when I run the ./letsencrypt-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d testmywebsite.us.to it gives me

              Failed authorization procedure. testmywebsite.us.to (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://testmywebsite.us.to/.well-known/acme-challenge/XDy9uTYug8Aya6XALPe2I6AhkZ4h07Uc0Qi3SwqQCEU [...(Erased for obvious reasons) ]: 404

              If you get python errors: resizing the droplet to 1GB solved the issue!

              Great tutorial again, thanks!

              Two questions…

              1. How best to configure a script(s) to renew multiple domains? Not like example.com and sub.example.com, more like example.com and example2.com. Would this necessitate running a separate cron per domain or is there a way to run one cron and iterate over domains?

              2. I use CloudFlare and because of this the renewal fails as LE attempts renewal against the proxy, not the host. Is there a way to configure LE (or even CF) to work together on this?

              Thanks in advance!

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              March 23, 2016

              I haven’t tested it but I think that the renew command will attempt to renew all of your LE certificates.

              I think if you want to renew and you’re using Cloudflare, you need to serve the .well-known location over plain HTTP without the redirect.

              Thanks for the article. I have a question, how do I add a timestamp to the log, and record the error.

              To record both the error and the result I can use the code below? /opt/letsencrypt/letsencrypt-auto renew &>> /var/log/le-renew.log

              How do I add a timestamp before, just incase I need to see a progression.

              Getting odd results after nginx configuration addition #3. Firstly, redirecting to https was not working unless I provided two distinct blocks: one for port 443, the second for port 80.

              Having 8 domains to handle, i simply copied and pasted the server_name entry into the second block.

              However not all domains are being automatically handled via port 443; only about one in two do…

              @cryptocake thx, can you confirm whether it’s safe to use on easy engine installs? I don’t think the conf’s are in the default paths

              Would love to know if anyone can confirm using this guide or @cryptocake 's script w/ easy engine thx

              Thank you for the article. I used it in the Ruby on Rails One-Click Application on DigitalOcean.

              sudo nano /etc/nginx/sites-available/default
              

              Part should go into the

              sudo nano /etc/nginx/sites-available/rails
              

              Just a heads up.

              I am getting an error when running the ./letsencrypt-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d example.com -d www.example.com (obviously chnaged to my cercumstances.

              This is my error:

              x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fno-strict-aliasing -D_FORTIFY_SOURCE=2 -g -fstack-protector-strong -Wformat -Werror=format-security -fPIC -I/usr/include/python2.7 -c build/temp.linux-x86_64-2.7/_openssl.c -o build/temp.linux-x86_64-2.7/build/temp.linux-x86_64-2.7/_openssl.o
                  x86_64-linux-gnu-gcc: internal compiler error: Killed (program cc1)
                  Please submit a full bug report,
                  with preprocessed source if appropriate.
                  See <file:///usr/share/doc/gcc-4.9/README.Bugs> for instructions.
                  error: command 'x86_64-linux-gnu-gcc' failed with exit status 4
              
                  ----------------------------------------
              Command "/home/user/.local/share/letsencrypt/bin/python2.7 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-0iujgs/cryptography/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('
              ', '
              '), __file__, 'exec'))" install --record /tmp/pip-swRxmp-record/install-record.txt --single-version-externally-managed --compile --install-headers /home/user/.local/share/letsencrypt/include/site/python2.7/cryptography" failed with error code 1 in /tmp/pip-build-0iujgs/cryptography
              You are using pip version 8.0.3, however version 8.1.1 is available.
              You should consider upgrading via the 'pip install --upgrade pip' command.
              

              Any ideas?

              Note: the --webroot-path I specify is used by it’s own independant non sudo user. Therefore I run the command and a different sudo user. Not sure if this would be the problem?

              Thanks

              Did you ever figure this out? I am seeing the exact same error.

              By the way the problem was not enough memory: https://github.com/certbot/certbot/issues/1151

              I killed mongo at the cert install worked.

              Does anyone have an example of how the /etc/nginx/sites-available/default file is supposed to look after adding the location:

              location ~ /.well-known { allow all; }

              I get an error message when I try to run “sudo service nginx reload”:

              • Reloading nginx configuration nginx [fail}

              Thank you!

              Sorry, answered above. It doesn’t go in the SSL server block. It goes in the server block as you clarified.

              will be great to see tutorial lighttpd+debian+letsencrypt with autorenew function :)

              Hello guys.

              I just want to share with all of you a recently released new course about how to create and configure a secure VPS with DigitalOcean, Nginx and Letsencrypt. Learn more here.

              I hope that all of you found it useful.

              I followed the guide to the letter and once finished, when I go to my domain name in my browser, it doesn’t load and I get a “ERR_CONNECTION_TIMED_OUT” error.

              https://www.ssllabs.com/ssltest/analyze.html is giving me an error of “Assessment failed: Unable to connect to the server”

              Of course I rebooted nginx and even rebooted the whole droplet, but that didn’t change anything… still not working.

              What could be the issue here?

              Super helpful. Was struggling with this so huge thanks

              Hi, Thanks a lot for this great article. This worked for me. Just a quick question: do I need to let the part

              location ~ /.well-known {
                           allow all;
                      }
              
              

              in my nginx conf file once it works? Or is it better to delete this part? Maybe it will be mandatory to still have it for renewals?

              Mitchell Anicas
              DigitalOcean Employee
              DigitalOcean Employee badge
              April 25, 2016

              You need to leave it in for the renewals.

              This comment has been deleted

                wow… this is super awesome.

                Great! Big thx >:D~

                Thank you for this!

                Awesome documentation!

                Hello,

                I would suggest adding a warning and separating the addition of the Strict-Transport-Security header to a separate section as it has non-trivial implications for users who may still have sites under HTTP.

                Similarly, it would be wise to warn users about the difference between temporary (302) and permanent (301) redirects.

                Recently a user on IRC has been found unable to access their HTTP site because of addition of this header from the tutorial without having any idea it would prevent HTTP access.

                Thank you,

                Thanks a lot for this guide! I have been running test based on it for some time.

                The recommended letsencrypt ACME client has now changed to certbot – see https://letsencrypt.org/getting-started/ and https://certbot.eff.org

                Another small tip: It is possible to get a timestamp in the log by piping the result through a loop adding it line by line. So my crontab looks like this with certbot-auto:

                35 2 * * 1 /etc/init.d/nginx reload``` 
                
                If you want a less verbose log, you can append ```-q``` for quiet mode after renew.

                Could you write a tutorial of how to run zeronet under https on vps? e.g. https://proxy.zeroexpose.com

                Great article! Got everything working except for the part that nginx always fails when restarting, and as a result the web address has no https:. The certificates are downloaded and are on the server but nginx seems to fail at configuring the certificate with the website. Any idea on how to solve this issue?

                Thanks.

                Hi Mitchell,

                Thanks very much for the tutorial.

                I’m getting this error,

                nginx: [emerg] BIO_new_file("/etc/ssl/certs/dhparam.pem") failed (SSL: error:02001002:system library:fopen:No such file or directory:fopen('/etc/ssl/certs/dhparam.pem','r') error:2006D080:BIO routines:BIO_new_file:no such file)
                nginx: configuration file /etc/nginx/nginx.conf test failed
                

                Any idea how to fix that?

                Also only getting a B rating in Qualys SSL Labs Report [https://snag.gy/R8F4k5.jpg] How can I make it a A+?

                Ok I’ve managed to solve the issue myself. I’ll write what I did below if that helps anyone.

                So on the tutorial, I had missed to add this line sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

                That was all and once that was added error went away and got a A+ rating as well.

                This comment has been deleted

                  This comment has been deleted

                    hi i can’t configure right the webroot path for some reason. I keep getting the error:

                    Domain: mydomain.com Type: unauthorized Detail: invalid response from http://mydomain.com/.well-known/acme-challenge/4_lvjanfdçvna: "<!DOCTYPE html> <html lang=“en-US” class=“nojs”> <head> <meta charset=“UTF-8”> <meta name=“viewport” content =“width=device-wi”

                    Domain: www.mydomain.com Type: unauthorized Detail: invalid response from http://www.mydomain.com/.well-known/acme-challenge/4_lvjanfdçvna: "<!DOCTYPE html> <html lang=“en-US” class=“nojs”> <head> <meta charset=“UTF-8”> <meta name=“viewport” content =“width=device-wi”

                    does any one know what I am doing wrong?

                    Great article! My only question - does letsencrypt/certbot need access to .well-known for renewal? Or is that a one time thing?

                    Hi, thank for great tutorial. Acutualy I have letsencrypt certification already. But it’s expired and I’d like to renew it. However when I run './letsencrypt … that come with following error, please help me. Thanks!!!

                    x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c build/temp.linux-x86_64-2.7/_openssl.c -o build/temp.linux-x86_64-2.7/build/temp.linux-x86_64-2.7/_openssl.o x86_64-linux-gnu-gcc: internal compiler error: Killed (program cc1) Please submit a full bug report, with preprocessed source if appropriate. See <file:///usr/share/doc/gcc-4.8/README.Bugs> for instructions. error: command ‘x86_64-linux-gnu-gcc’ failed with exit status 4

                    ----------------------------------------
                    

                    Command “/home/lempme/.local/share/letsencrypt/bin/python2.7 -u -c “import setuptools, tokenize;file=‘/tmp/pip-build-ozo9hx/cryptography/setup.py’;exec(compile(getattr(tokenize, ‘open’, open)(file).read().replace(’ ', ’ '), file, ‘exec’))” install --record /tmp/pip-xKgmRP-record/install-record.txt --single-version-externally-managed --compile --install-headers /home/lempme/.local/share/letsencrypt/include/site/python2.7/cryptography” failed with error code 1 in /tmp/pip-build-ozo9hx/cryptography /home/lempme/.local/share/letsencrypt/local/lib/python2.7/site-packages/pip/vendor/requests/packages/urllib3/util/ssl.py:120: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning. InsecurePlatformWarning You are using pip version 8.0.3, however version 8.1.2 is available. You should consider upgrading via the ‘pip install --upgrade pip’ command.

                    Did you find a resolution to this? I have the same issue.

                    Hi Can someone let me know how to fix the issue mentioned in here

                    https://community.letsencrypt.org/t/f-rating-for-helloworld-letsencrypt-org-on-qualys-ssl-labs-fixed/16679

                    My site got a F rating after installing lets encrypt

                    https://www.ssllabs.com/ssltest/analyze.html?d=thetascript.com

                    Same here. It’s reporting this issue: “This server is vulnerable to the OpenSSL Padding Oracle vulnerability (CVE-2016-2107) and insecure. Grade set to F.”

                    I managed to fix it by upgrading the libssl package. I’m at an A+ grade now.

                    sudo apt-get install --only-upgrade libssl1.0.0 [sudo] password for letsencrypt: Reading package lists… Done Building dependency tree Reading state information… Done libssl1.0.0 is already the newest version. 0 upgraded, 0 newly installed, 0 to remove and 204 not upgraded.

                    This is what I see when I upgrade

                    and when I check

                    zgrep -ie “(CVE-2016-2108|CVE-2016-2107)”

                    its doing nothing

                    Should I run this as root

                    It did nothing for me too. I just quit it and re-checked the site on SSL Labs.

                    Also, I ran a full sudo apt-get upgrade and upgraded all the packages just in case.

                    I follow the instruction but I am getting into an infinite loop :(

                    wow…very detailed script

                    I’m unable to auto renew the certificate. This error occurs:

                    The program nginx (process ID 22235) is already listening on TCP port 80. This will prevent us from binding to that port. Please stop the nginx program temporarily and then try again. For automated renewal, you may want to use a script that stops and starts your webserver. You can find an example at https://letsencrypt.org/howitworks/#writing-your-own-renewal-script. Alternatively you can use the webroot plugin to renew without needing to stop and start your webserver

                    Thanks for the article, Mitchell… :)

                    Thanks for the great tutorial. It took a bit to get started, especially around the configuration of the .well-known/acme-challenge directory. But after reading through and re-familiarising myself with Nginx again, it worked perfectly. Thanks.

                    Hmm… After the cert expires I can’t seem to get it back up and working. I tried manually updating the cert, it works, but it doesn’t seem to be reflecting and the site is still in the reds… =( Anyone else got this issue?

                    I’m using virtual hosts and have several sites running on the server and all of them are now dead… Sobs!

                    Great tutorial. everything works like a charm. But I’m facing a weird problem. From any mobile device its not redirecting to https. By the way when I’m browsing with ‘sttps’ its working fine.

                    Can anyone help me regarding this.

                    sen spo di se mu kan tkisha dit po se spo di be une spo ma qet si ti

                    I was amazed when everything actually worked on the first try without any errors. Great tutorial!!!

                    WARNING: THE AUTO UPDATE WILL NO LONGER WORK ACCORDING TO THIS GUIDE

                    letsencrypt-auto has been replaced with Certbot (https://certbot.eff.org/#ubuntutrusty-nginx). You might see a warning about this, but it doesn’t make it clear that letsencrypt-auto does not work. Worse still you may still receive a success message when attempting an update with letsencryt-auto (I was getting the congrats message but my certs weren’t actually updating) - so you may not see any errors, and the crontab job will succeed (this is how I missed the issue until my certs actually expired this morning… on a Sunday).

                    An update to this guide with this in mind would be great - as although I’ve restored my certs I’m not sure how to work Certbot into the auto-renew process with crontab.

                    If auto renewal doesn’t work with this guide, then that’s pretty important and it should be updated.

                    I looked at the Certbot link but I’m not sure if I’ve set everything up correctly. I guess I’ll find out in 90 days…

                    Getting this when I run ./letsencrypt-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d example.com -d www.example.com (I have replaced example.com with my domain)

                    403 Forbidden [IP: 2604:a880:0:1::4 80] E: Failed to fetch http://mirrors.digitalocean.com/ubuntu/pool/universe/p/python-virtualenv/python-virtualenv_1.11.4-1ubuntu1_all.deb 403 Forbidden [IP: 2604:a880:0:1::4 80]

                    Letsencrypt never initializes.

                    Any suggestions?

                    I followed this tutorial for a web server, and it worked fine. (example.com)

                    I followed the same steps for (mail.example.com) and none of the sites were reachable. I was unable to see any errors in the servers nginx error log or access log. In the successful setup, I was able to use example.com and www.example.com when instructed. On the 2nd server, I can’t enter a www.mail.example.com, as this doesn’t work.

                    Can anyone offer some guidance? Thanks!

                    great article … running fine however checking my site (relaxason.com) with ssllabs
                    https://www.ssllabs.com/ssltest/analyze.html?viaform=on&d=relaxason.com

                    DOES NOT rate A+ , only a B stating this server support weak Diffie-Helmann (DH) key exchange parameters. Grade capped to B. read the doc ,but could not find why ?

                    This article was very helpful. This is a great approach to SSL.

                    I need your help! I followed the to set up KingsKidsClub.com and SacramentoKingsKidsClub.com.

                    Installation went well except for testing the security on ssllabs at the end of step 3. See: https://www.ssllabs.com/ssltest/analyze.html?d=kingskidsclub.com https://www.ssllabs.com/ssltest/analyze.html?d=sacramentokingskidsclub.com

                    I failed both tests, apparently I have a vulnerability called: “CVE-2016-2107”

                    So far I’ve tried the following without luck:

                    https://bobcares.com/blog/fix-high-severity-openssl-bugs-memory-corruption-padding-oracle-ubuntu-centos-redhat-opensuse-linux/

                    https://gist.github.com/ArturT/bc8836d3bedff801dc324ac959050d12

                    Anyone know how I can fix this issue? Thank you in advance for any help!

                    What a great tutorial. I followed it on a fresh MEAN droplet and all went well up until the testing of the site via the Qualsys SSL Labs site or even just hitting the site on my own.

                    Qualsys tells me: Assessment failed: No secure protocols supported. And in a browser, it tells me: ERR_SSL_PROTOCOL_ERROR.

                    My end goal is to run Express apps on port 3000 and put that behind the SSL, but I first wanted to try letsencrypt.

                    Any help would be GREATLY appreciated.

                    Great tutorial, thanks a lot :)

                    I had to add this ^ key into the location block. I kept getting a 403 forbidden error without it.

                            location ^~ /.well-known {
                                    allow all;
                            }
                    

                    I am running NodeJS under a proxy with Nginx as suggested on the tutorial https://www.digitalocean.com/community/tutorials/how-to-set-up-a-node-js-application-for-production-on-ubuntu-16-04. But as of Nginx is just a proxy, I am not able to get **letsencrypt **to configure properly. Any clues?

                    I’m in the same boat. If anyone has a bit more info then that would be great. Think I read earlier that it is something to do with creating a static file for the .well-known location, but I am unsure on this (although reading up now).

                    What a great tutorial. Thank you so much for sharing this amazing guide. Worked like a charm

                    This article contain to made secure serve HTTPS. Great!

                    I have A domain and B domain which is using SSL. I want to make B domain back to http (not using ssl).

                    The question is: How to revoke the SSL with spesific domain?

                    Thanks

                    Kamal Nasser
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    September 13, 2016

                    Hi!

                    You can remove domain B’s certificate by deleting the relevant .conf file in /etc/letsencrypt/renewal. Once you’ve done that, Let’s Encrypt will no longer attempt to renew the certificate. Then, edit your nginx config and remove any directives that deal with SSL. All you need to do is remove the server block that looks like this (which redirects http to https):

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

                    and the ssl configuration lines in the SSL-enabled server block:

                            ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
                            ssl_prefer_server_ciphers on;
                            ssl_dhparam /etc/ssl/certs/dhparam.pem;
                            ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
                            ssl_session_timeout 1d;
                            ssl_session_cache shared:SSL:50m;
                            ssl_stapling on;
                            ssl_stapling_verify on;
                            add_header Strict-Transport-Security max-age=15768000;
                    

                    Finally, replace listen 443 ssl; with listen 80; in the same server block. Restart nginx and you should be good to go:

                    sudo service nginx restart
                    

                    Very useful article but I’ve just configured a new cert and the overall rating on ssllabs site is a poor grade F due to this issue: “This server is vulnerable to the OpenSSL Padding Oracle vulnerability (CVE-2016-2107) and insecure. Grade set to F.”

                    Is there some improvement about this?

                    Kamal Nasser
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    September 26, 2016

                    What version of Ubuntu are you running? If it’s between 12.04 and 16.04, you will need to upgrade the OpenSSL package using the following command:

                    sudo apt-get install --only-upgrade libssl1.0.0
                    

                    Once you’ve done that, restart nginx so that the changes take effect:

                    sudo service nginx restart
                    

                    Hi @kamaln7, I’m running on Ubuntu 15.04. I’ve tried to update libssl and it seems that I’m already running on last version:

                    “sudo apt-get install --only-upgrade libssl1.0.0 … libssl1.0.0 is already the newest version”

                    This article is a great help. Thanks Mitchell for sharing this.

                    Ok, I followed every step of this tutorial and double checked all of my typing before saving and exiting or submitting the commands, but my site still does not recognize that there is a valid certificate from Let’s Encrypt. When I open the site in the browser (with or without the “www”), Firefox still says the site is unsecured. I am at a loss here. This is the first time I have tried to setup any kind of encryption. Help please.

                    Ubuntu 14.04 Nginx 1.4.6 Ghost 0.11.1

                    “This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.” Does it mean I cannot use any digitalocean material for my customers for commercial gain or business?

                    Kamal Nasser
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    October 19, 2016

                    That’s correct. According to the license terms:

                    NonCommercial — You may not use the material for commercial purposes.

                    then of what purpose are these materials, if i cannot use it on customers vps. most technical people don’t implement encryption for fun. possibly, a customer might have asked for ssl encryption. In my own case, i charge customers for my services to implement ssl. i use the knowledge here to do the work. i am not selling the material, to customer. i just use it to solve customer’s problem. does it mean i am violating the license? tell me if i have violated the license.

                    Kamal Nasser
                    DigitalOcean Employee
                    DigitalOcean Employee badge
                    October 20, 2016

                    Hi @kingsleyuk2003. Sorry—I misunderstood your question. You are definitely allowed to use the tutorials as guides in order to set up your customers’ servers, however, you may not repost the content on your website for commercial purposes.

                    Ok, I understand you. Thanks.

                    Nice article, thanx! There is one thing I can’t understand:

                    If I try to echo shell variable, it returns me nothing:

                    bash-3.2$ TEST='HELLO' echo $TEST
                    
                    bash-3.2$
                    

                    Lets create simple script ./test.sh

                    #!/bin/bash
                    
                    echo $TEST
                    

                    Run it and it will work exactly as expected

                    bash-3.2$ TEST='HELLO' ./test.sh
                    HELLO
                    bash-3.2$
                    

                    Why it’s happening?

                    you can try the

                    TEST='HELLO' && echo $TEST
                    

                    First of all, thanks for this article, I now get the best grades in ssl-labs :)

                    Just one suggestion: I think, instead of waiting 5 minutes after renewal it would be better to use the provided --post-hook don’t you think? /usr/local/sbin/certbot-auto renew --post-hook “service nginx restart”

                    Thank you Michell for this great guide

                    Yet when ssl test my website I get this following error: OCSP ERROR: Request failed with OCSP status: 6 [http://ocsp.int-x3.letsencrypt.org/]

                    WHat does it mean? ANyone please help

                    nice - thank you.

                    couple of comments;

                    1. eff suggest running the auto-renewal twice a day
                    2. they also suggest trying a dry-run on auto-renew with certbot-auto renew --dry-run this checks that everything actually works, rather than just skipping the non-expired certs

                    https://certbot.eff.org/all-instructions/#ubuntu-14-04-trusty-nginx

                    I think you should mention that “certbot” used to be the “letsencrypt-auto” tool. I came to this article a few months after initially using this article to setup a certificate, and I was very confused that this article talks about a “certbot” tool that I somehow don’t have, I kept thinking “how did I mess up the instructions this bad but it still worked?!”

                    And I saw that my crontab has a tool named “letsencrypt-auto” so then after a google search I understand that it got renamed. But when you edited the article to reflect the name change you should mention this to not confuse anyone who previously used it.

                    Whats my document root path if i dont have one, i use my nguni as a proxy

                    One of the best article i have ever read! You helped me a lot to setup ssl to my droplet :)

                    Hi sir,

                    Thanks for sharing. It was really useful but I got one problem. May you help me?

                    https://s4.postimg.org/3re3dmqjx/Screenshot_from_2016_12_06_10_52_01.png

                    Thank you so much!!!

                    Great tutorial. The only thing that tripped me up was that I needed to set up my server block to listen for the default server like this:

                    listen 443 default_server ssl;
                    

                    Excluding default_server on that line—per the article instructions—resulted in the following nginx error:

                    no “ssl_certificate” is defined in server listening on SSL port while SSL handshaking

                    followed this instruction perfectly. got everything to work… except im now stuck on the “welcome to nginx” I cannot access my site anymore. what am I missing?

                    Thanks for the article. I followed the youtube video as well (https://www.youtube.com/watch?v=m9aa7xqX67c&list=WL&index=20) and for renewal they provide a le-renew-webroot script from http://do.co/le-nginx-renew. I keep getting an error saying “line 23: bc: command not found”. The line in question is:

                    days_exp=$(echo \( $exp - $datenow \) / 86400 |bc)
                    ...
                    

                    Subsequently a integer is not passed to line 27 triggering error “line 27: [: : integer expression expected”. Line 27 opens an if statement:

                    if [ "$days_exp" -gt "$exp_limit" ] ; then
                    ...
                    

                    Would really appreciate a guiding hand with this error as I’m new to shell scripting.

                    Hi, thanks for sharing. But when I try to run

                    certbot-auto renew
                    

                    I got this error: Can’t Renew SSL

                    So, I followed this tutorial back in September or October last year. I even set up a cron job to renew the cert for me. Well, it never ran or, if it did, did not notify me of errors. Anyway, I get the following error message:

                    
                    Attempting to renew cert from /etc/letsencrypt/renewal/mydomain.com.conf produced an unexpected error: Failed authorization procedure. mydomain.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://mydomain.com/.well-known/acme-challenge/AWDtbzlAoV_3RcsUfEOTg9ZtvAzX88GNe1Fv1zTlPBk: "<!doctype html>
                    <!--[if (IE 8)&!(IEMobile)]><html class="no-js lt-ie9" lang="en"><![endif]-->
                    <!--[if (gte IE 9)| IEMobile |!(IE", www.mydomain.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://www.mydomain.com/.well-known/acme-challenge/gwSbpkZCrA27E1XWzFryWvkpHiFNZ2ewtFJtyxAevMo: "<!doctype html>
                    <!--[if (IE 8)&!(IEMobile)]><html class="no-js lt-ie9" lang="en"><![endif]-->
                    <!--[if (gte IE 9)| IEMobile |!(IE". Skipping.
                    
                    All renewal attempts failed. The following certs could not be renewed:
                      /etc/letsencrypt/live/mydomain.com/fullchain.pem (failure)
                    1 renew failure(s), 0 parse failure(s)
                    
                    IMPORTANT NOTES:
                     - The following errors were reported by the server:
                    
                       Domain: mydomain.com
                       Type:   unauthorized
                       Detail: Invalid response from
                       http://mydomain.com/.well-known/acme-challenge/AWDtbzlAoV_3RcsUfEOTg9ZtvAzX88GNe1Fv1zTlPBk:
                       "<!doctype html>
                       <!--[if (IE 8)&!(IEMobile)]><html class="no-js lt-ie9"
                       lang="en"><![endif]-->
                       <!--[if (gte IE 9)| IEMobile |!(IE"
                    
                       Domain: www.mydomain.com
                       Type:   unauthorized
                       Detail: Invalid response from
                       http://www.mydomain.com/.well-known/acme-challenge/gwSbpkZCrA27E1XWzFryWvkpHiFNZ2ewtFJtyxAevMo:
                       "<!doctype html>
                       <!--[if (IE 8)&!(IEMobile)]><html class="no-js lt-ie9"
                       lang="en"><![endif]-->
                       <!--[if (gte IE 9)| IEMobile |!(IE"
                    
                       To fix these errors, please make sure that your domain name was
                       entered correctly and the DNS A record(s) for that domain
                       contain(s) the right IP address.
                    

                    How can I fix this? I have ensured that the DNS A records have the correct IP addresses. I am at a loss. Thanks for any help!

                    This comment has been deleted

                      This comment has been deleted

                        This comment has been deleted

                          This comment has been deleted

                            the command “certbot-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d example.com” , didn’t work with me and i used the below command to veryfay throw nginx

                            certbot-auto certonly –nginx -d example.com

                            Thank You for this helpful information

                            I had this working (I think) for a few sites, but ran into a problem today when I tried to add another. Whether I try to add a new certificate:

                            certbot-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d example.com -d www.example.com

                            or do a dry run renew:

                            /usr/local/sbin/certbot-auto renew --dry-run,

                            i get the following error:

                            Couldn't download https://raw.githubusercontent.com/certbot/certbot/v0.10.0/letsencrypt-auto-source/letsencrypt-auto. HTTP Error 503: first byte timeout

                            Any idea why this might be? thanks

                            EDIT: apparently it was just a glitch in the matrix - gave it some time, tried again, and it worked.

                            I’m stuck on step 3. Anyone have any idea why I’m getting the error below? I’ve tried serval of the comments fixes and nothing seems to work. It tells me to check DNS A record, but my site has been live for almost a year, just adding an SSL option. Thanks in advance.

                            Failed authorization procedure. mydomain.com (http-01): urn:acme:error:unauthorized :: The client lacks sufficient authorization :: Invalid response from http://mydomain.com/.well-known/acme-challenge/RM0v1xZ74aLNkD5PfbIkjGBsrwnqEQtmJNT5_ILeyHY: "<!doctype html>

                            I figured out my solution, I had to edit the /etc/nginx/sites-available/ghost file. I just kept getting errors when I would try to edit the /etc/nginx/sites-available/default file.

                            I currently only have one domain on this web server so it’s not a big deal, but I thought this solution was supposed to be for the whole server, not just a specific site it hosts. Any ideas where I went wrong? Or did I just misunderstand something?

                            Great. But after doing this my Redmine One-Click web page show this and I can’t login back into Redmine.

                            Almost there!

                            Redmine is being installed in the background. Once that’s done, this page will be replaced with your Redmine installation. Hang on!

                            To get your credentials, ssh into your droplet.

                            I tried and followed the tutorial but my main domain page is opening the nginx page instead my website, any clue how to solve that? there is no error on nginx config file and the content from the file is the same from the tutorial,

                            thanks.

                            When in the file under

                            /etc/nginx/sites-available/default

                            He is blank.

                            What do I do?

                            Very good article ! Clear, precise and very useful !

                            This write-up is amazing!! Absolutely perfect. Got setup and running with SSL in about an hour. Thanks so much for the solid work. It’s greatly appreciated.

                            Only worked for me when comma seperate the domains. You said: -d example.com -d www.example.com

                            But it only worked for me with a comma seperation: -d example.com,www.example.com

                            Edit: certbot version: 0.13.0

                            When i execute below command in the terminal it was updating(just like sudo apt-get update)

                            certbot-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d example.com -d www.example.com

                            first line Bootstrapping dependencies for Debian-based OSes… (you can skip this with --no-bootstrap)

                            Hi!

                            Thanks for the helpful article! Unfortunately, I ran into a problem. I have created a website with Flask that is serving a Bokeh app on a Digital Ocean VPN. When opening the website the Bokeh app doesn’t load, instead I get the following error message in my Chrome console:

                            Mixed Content: The page at 'https://geomorphix.net/company_abc/' was loaded over HTTPS, but requested an insecure script 'http://###.###.###.##:5006/company_abc/autoload.js?bokeh-autoload-element=f…aab19c633c95&bokeh-session-id=AvWhaYqOzsX0GZPOjTS5LX2M7Z6arzsBFBxCjb0Up2xP'. This request has been blocked; the content must be served over HTTPS.
                            

                            I’m serving the Bokeh app using the “–use-xheaders” option. Here’s the config file ‘/etc/supervisor/conf.d/bokeh_serve.conf’:

                            [program:bokeh_serve]
                            command=/opt/envs/virtual/bin/bokeh serve company_abc.py company_xyz.py --allow-websocket-origin=www.geomorphix.net --allow-websocket-origin=geomorphix.net --host=###.###.###.##:5006 --use-xheaders
                            directory=/opt/webapps/flask_telemetry
                            autostart=false
                            autorestart=true
                            startretries=3
                            user=nobody
                            

                            I think the problem might lies in the Nginx config file ‘/etc/nginx/sites-available/default’, but I can’t figure out what’s wrong:

                            upstream flask_siti {
                                    server 127.0.0.1:8118 fail_timeout=0;
                            }
                            server {
                                    listen 443 ssl;
                            
                                    server_name geomorphix.net www.geomorphix.net;
                            
                                    ssl_certificate /etc/letsencrypt/live/geomorphix.net/fullchain.pem;
                                    ssl_certificate_key /etc/letsencrypt/live/geomorphix.net/privkey.pem;
                                    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
                                    ssl_prefer_server_ciphers on;
                                    ssl_dhparam /etc/ssl/certs/dhparam.pem;
                                    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
                                    ssl_session_timeout 1d;
                                    ssl_session_cache shared:SSL:50m;
                                    ssl_stapling on;
                                    ssl_stapling_verify on;
                                    add_header Strict-Transport-Security max-age=15768000;
                            
                                    charset utf-8;
                                    client_max_body_size 75M;
                            
                                    access_log /var/log/nginx/flask/access.log;
                                    error_log /var/log/nginx/flask/error.log;
                            
                                    keepalive_timeout 5;
                            
                                    location / {
                                            # checks for static file, if not found proxy to the app
                                            try_files $uri @proxy_to_app;
                                    }
                            
                                    location @proxy_to_app {
                                            proxy_redirect off;
                                            proxy_set_header Host $http_host;
                                            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                                            proxy_pass http://flask_siti;
                                    }
                            }
                            
                            server {
                                listen 80;
                                server_name geomorphix.net www.geomorphix.net;
                                return 301 https://$host$request_uri;
                            }
                            

                            Do you happen to know what is going on? Here is my Flask config file ‘/etc/supervisor/conf.d/flask.conf’:

                            [program:flask]
                            command=/opt/envs/virtual/bin/gunicorn -b :8118 website_app:app
                            directory=/opt/webapps/flask_telemetry
                            user=nobody
                            autostart=true
                            autorestart=true
                            redirect_stderr=true
                            

                            And here is my Flask app:

                            from flask import Flask
                            from flask_sqlalchemy import SQLAlchemy
                            from flask import render_template, request, redirect, url_for
                            from flask_security import Security, SQLAlchemyUserDatastore, UserMixin, RoleMixin, login_required, roles_accepted, current_user
                            from flask_security.decorators import anonymous_user_required
                            from flask_security.forms import LoginForm
                            from bokeh.embed import autoload_server
                            from bokeh.client import pull_session
                            from wtforms import StringField
                            from wtforms.validators import InputRequired
                            from werkzeug.contrib.fixers import ProxyFix
                            
                            app = Flask(__name__)
                            app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://###:###@localhost/telemetry'
                            app.config['SECRET_KEY'] = '###'
                            app.config['SECURITY_REGISTERABLE'] = True
                            app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
                            app.config['SECURITY_USER_IDENTITY_ATTRIBUTES'] = 'username'
                            app.config['SECURITY_POST_LOGIN_VIEW'] = '/re_direct'
                            app.debug = True
                            db = SQLAlchemy(app)
                            
                            # Define models
                            roles_users = db.Table('roles_users',
                                    db.Column('user_id', db.Integer(), db.ForeignKey('user.id')),
                                    db.Column('role_id', db.Integer(), db.ForeignKey('role.id')))
                            
                            class Role(db.Model, RoleMixin):
                                id = db.Column(db.Integer(), primary_key=True)
                                name = db.Column(db.String(80), unique=True)
                                description = db.Column(db.String(255))
                            
                            class User(db.Model, UserMixin):
                                id = db.Column(db.Integer, primary_key=True)
                                username = db.Column(db.String(255), unique=True)
                                password = db.Column(db.String(255))
                                active = db.Column(db.Boolean())
                                confirmed_at = db.Column(db.DateTime())
                                roles = db.relationship('Role', secondary=roles_users,
                                                        backref=db.backref('users', lazy='dynamic'))
                            
                            class ExtendedLoginForm(LoginForm):
                                email = StringField('Username', [InputRequired()])
                            
                            # Setup Flask-Security
                            user_datastore = SQLAlchemyUserDatastore(db, User, Role)
                            security = Security(app, user_datastore, login_form=ExtendedLoginForm)
                            
                            # Views
                            @app.route('/')
                            @anonymous_user_required
                            def index():
                                return render_template('index.html')
                            
                            @app.route('/re_direct/')
                            @login_required
                            def re_direct():
                                identifier = current_user.username
                                print(identifier)
                                return redirect(url_for(identifier))
                            
                            @app.route('/index/')
                            @login_required
                            @roles_accepted('admin')
                            def admin():
                                return render_template('admin.html')
                            
                            @app.route("/company_abc/")
                            @login_required
                            @roles_accepted('company_abc', 'admin')
                            def company_abc():
                                url='http://###.###.###.##:5006'
                                session=pull_session(url=url,app_path="/company_abc")
                                bokeh_script=autoload_server(None,app_path="/company_abc",session_id=session.id,url=url)
                                return render_template("company_abc.html", bokeh_script=bokeh_script)
                            
                            @app.route("/company_xyz/")
                            @login_required
                            @roles_accepted('company_xyz', 'admin')
                            def company_xyz():
                                url='http://###.###.###.##:5006'
                                session=pull_session(url=url,app_path="/company_xyz")
                                bokeh_script=autoload_server(None,app_path="/company_xyz",session_id=session.id,url=url)
                                return render_template("company_xyz.html", bokeh_script=bokeh_script)
                            
                            app.wsgi_app = ProxyFix(app.wsgi_app)
                            
                            if __name__ == '__main__':
                                app.run()
                            

                            should the web root for a server with a default install be ‘/var/www/html/’ ?

                            If you created a wordpress-hosting droplet this is the location of the web root , just in case you were wondering.

                            We are at 2017 and certbot evolved… using stadanrds of certbot.eff.org/#ubuntuxenial-nginx, that say to use certbot certonly command…

                            • there are no SIMPLE PLUG-AND-PLAY procedure?? Why need to edit NGINX configs?

                            • I need to use certbot-auto or certbot?

                            • … some news using new version of certbot here?

                            We are at 2017 and certbot evolved… using stadanrds of certbot.eff.org/#ubuntuxenial-nginx, that say to use certbot certonly command…

                            • there are no SIMPLE PLUG-AND-PLAY procedure?? Why need to edit NGINX configs?

                            • I need to use certbot-auto or certbot?

                            • … some news using new version of certbot here?

                            Thanks, Mitchell! This article was great. I had set up my server with Flask (https://www.digitalocean.com/community/tutorials/how-to-serve-flask-applications-with-gunicorn-and-nginx-on-ubuntu-16-04), so I had to change Steps 2 and 3 a little.

                            Step 2: Instead of changing /etc/nginx/sites-available/default, I added the following function to my main Python file at ~/ProjectName/ProjectName.py:

                            @app.route('/.well-known/acme-challenge/<token_value>')
                            def letsencrpyt(tmp):
                                with open('.well-known/acme-challenge/{}'.format(token_value)) as f:
                                    answer = f.readline().strip()
                                return answer
                            

                            Source: https://hjlog.me/post/177

                            Step 3: Instead of changing /etc/nginx/sites-available/default, I added the SSL information and new server block to my project’s configuration file: /etc/nginx/sites-available/ProjectName.

                            @kelseytheriault Hello, I also have a flask app where the .websocket file is located in directory /home/me/website/

                            I’ve followed the guide in the link, but am still getting 404 not found errors for my app when I run certbot. Do you mind telling me what is the webroot path for flask apps? It’s running on a Nginx server.

                            @axhindol Hey, I don’t know if I can be of much help. The command I ran that eventually worked was:

                            sudo certbot certonly --webroot --webroot-path=/usr/share/nginx/html -d www.mydomain.com

                            So I just used the default webroot path, even though I have a .sock file in a different place. (I assume that’s the same as a .websocket file?) And I didn’t do it for the domain without www.

                            I would:

                            1. Make sure your site is running Flask correctly in the first place – i.e. displays something from your Flask file and not the default Nginx message.
                            2. Try it with only the www domain.

                            You could also try this modified letsencrypt function I found on GitHub that also worked for me: https://github.com/ciil/gcloud-letsencrypt-flask/blob/master/letsencrypt.py

                            @kelseytheriault Thanks for the response!

                            My site works. Just a few clarifications:

                            1. You didn’t make any changes to your nginx/sites-available/ file until after you used the certbot command?

                            2. The flask route you added, did you add it in your main “app.py” file ?

                            Thanks again!

                            @axhindol

                            1. Nope! I would have had to have fullchain.pem and privkey.pem to change that file.

                            2. Yes, this Flask route was in my main app.py file.

                            This comment has been deleted

                              Good article! We are linking to this great post on our site. Keep up the great writing.

                              http://wwwmaxbet.net/ Daftar ID Maxbet http://www.maxparlay.net/ Login Maxbet http://mobilemaxparlay.com/ Login Mobile Maxbet

                              Wow, super informative and I faced with no errors at all. Thanks!

                              Is this method work in Ubuntu 16.04.3 x64? And I have the EasyEngine framework installed on it and also do we need to create another user (I have only root user).

                              Please help me with that I am a beginner in this field.

                              Hello, I am following this guide and i get this error on step 3:

                              Generating key (1024 bits): /var/lib/letsencrypt/snakeoil/0000_key.pem /usr/lib/python2.7/dist-packages/OpenSSL/rand.py:58: UserWarning: implicit cast from ‘char *’ to a different pointer type: will be forbidden in the future (check that the types are as you expect; use an explicit ffi.cast() if they are correct) result_code = _lib.RAND_bytes(result_buffer, num_bytes) Cleaning up challenges nginx restart failed:

                              nginx: [emerg] could not build the server_names_hash, you should increase server_names_hash_bucket_size: 64

                              I am a bit newbie so, does anybody know how can i solve this?

                              Thank you.

                              Excellent article, Mitchell. This works pretty well on Ubuntu 16.04 as well.

                              I registered to say THANK YOU!

                              This comment has been deleted

                                thank you!

                                This worked like a charm! Thanks.

                                Now Firefox needs to get its act together. My page loads in Chrome and it used to load in Firefox but no more. Firefox gives a security error message pointing to “var socket = new WebSocket(‘ws://localhost:3055/’);” My server is more secure than ever but now Firefox rejects it on grounds that it is supposedly not secure. How absurd is that?

                                Maybe you can help. Here is a link to my bug report:

                                https://bugzilla.mozilla.org/show_bug.cgi?id=1468944

                                alt text
                                Thumbs Up

                                Thanks, this was helpful. I installed this after doing the One-click Ghost droplet and it created a new .conf file that was using my IP. I changed that to my domain and it was smooth from there on out.

                                Thanks for the help!

                                not working

                                root@cdn-lt4:~# sudo apt-get install python-certbot-nginx Reading package lists… Done Building dependency tree
                                Reading state information… Done E: Unable to locate package python-certbot-nginx root@cdn-lt4:~#

                                Try DigitalOcean for free

                                Click below to sign up and get $200 of credit to try our products over 60 days!

                                Sign up

                                Join the Tech Talk
                                Success! Thank you! Please check your email for further details.

                                Please complete your information!

                                Become a contributor for community

                                Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

                                DigitalOcean Documentation

                                Full documentation for every DigitalOcean product.

                                Resources for startups and SMBs

                                The Wave has everything you need to know about building a business, from raising funding to marketing your product.

                                Get our newsletter

                                Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

                                New accounts only. By submitting your email you agree to our Privacy Policy

                                The developer cloud

                                Scale up as you grow — whether you're running one virtual machine or ten thousand.

                                Get started for free

                                Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

                                *This promotional offer applies to new accounts only.