Nginx with Let's Encrypt configuration

In this article I show how easy and fast install and configure nginx. Also we will obtain free SSL public certificate for our domain by Let'sEncrypt service.

In the end we will make advanced nginx configuration to support for proxying request to backend (flask in my case) and providing balancing.


All configuration we will make on CentOS 6.7 with kernel 2.6.32-573.8.1.el6.x86_64. As a test domain in this article we will use and for configure balancing. However you should change this domain names to yous.

We need configure nginx twice: for obtain SSL certificate and for backend config, because of my backend app on flask does not provide required http content for web site root.


Nginx Installation

I recomend to use latest nginx version from official repo because EPEL provide too old package version (without new shiny features;]). However all configuration in this article produced with nginx version 1.8.1-1.el6.

cat << EOF > /etc/yum.repo.d/nginx.repo  
name=nginx repo  
yum --disablerepo=* --enablerepo=nginx install nginx-1.8.1-1.el6.ngx.x86_64  

LetsEncrypt(LE) Installation

LetsEncrypt is free public CA service. It distributed via github so we need to install git first.

yum install epel-release  
yum install git  
cd /opt  
git clone /opt/letsencrypt  


Base LE and nginx configuration. Obtaining certificates.

In the first place we should edit nginx.conf.

cat << EOF > /etc/nginx/nginx.conf  
user nginx;  
worker_processes 1; # ~ number of CPU  
error_log /var/log/nginx/error.log warn;  
pid /var/run/;  
worker_rlimit_nofile 2048;

events {  
    worker_connections 512;

http {  
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    limit_req_zone $binary_remote_addr zone=example:10m rate=1r/s;

    ssl_dhparam /etc/nginx/cert/dhparam.pem;
    ssl_ciphers "AES128+EECDH:AES128+EDH";
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;

    add_header X-Content-Type-Options nosniff;

    log_format main '[$time_local] $remote_addr - $remote_user $request" $status $body_bytes_sent $http_referer" "$http_user_agent" "$http_x_forwarded_for" $uid_got $uid_set';
    log_format up_status '[$time_local] $remote_addr "$request" status $bytes_sent $upstream_addr $upstream_status $upstream_cache_status $upstream_response_time $request_time';

    access_log /var/log/nginx/access.log main;
    charset utf8;
    client_max_body_size 5m;
    gzip on;
    gzip_buffers 16 8k;
    gzip_comp_level 6;
    gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    gzip_http_version 1.1;
    gzip_proxied any;
    gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript text/x-js;
    gzip_vary on;
    keepalive_timeout 65;
    large_client_header_buffers 8 32k;
    open_file_cache max=1000 inactive=20s;
    open_file_cache_min_uses 2;
    open_file_cache_valid 30s;
    proxy_set_header Request-Range "";
    real_ip_header X-Forwarded-For;
    send_timeout 300;
    sendfile on;
    server_names_hash_max_size 2048;
    server_tokens off;
    tcp_nodelay on;
    tcp_nopush on;
    proxy_buffer_size 16k;
    proxy_buffers 16 512k;
    proxy_busy_buffers_size 512k;
    proxy_busy_buffers_size 120;
    proxy_read_timeout 120;
    fastcgi_buffer_size 16k;
    fastcgi_buffers 16 512k;
    fastcgi_busy_buffers_size 512k;
    fastcgi_connect_timeout 300;
    fastcgi_read_timeout 300;
    fastcgi_temp_file_write_size 512k;
    include /etc/nginx/conf.d/*.conf;

Generate dhparam file.

mkdir /etc/nginx/cert  
openssl dhparam -out /etc/nginx/cert/dhparam.pem 4096  

Also we need to make le-well-known.conf file to provide LE support.

cat << EOF > /etc/nginx/default.d/le-well-known.conf  
location ~ /.well-known {  
allow all;  

Then configure default host for obtain SSL cert for it.

cp /etc/nginx/conf.d/default.conf.dist /etc/nginx/conf.d/default.conf  
nginx -t  
service nginx start  

Now you can test the nginx is working by going to with your browser. If you can see nginx default start page then everything is good and we cant obtain certs.

Note: we must to use -a webroot option because LE client doesn't support nginx for now.

cd /opt/letsencrypt  
./letsencrypt-auto certonly -a webroot --webroot-path=/usr/share/nginx/html -d -d

Note: you can find cert into /etc/letsencrypt/live/ directory.

Nginx advanced configuration. Enable https support.

The next step - is deleting the default domain config and creating new one for our host.
Also for our new virtual domain we will enable redirecting from http to https to provide more security.

To enforce redundancy we will configure balancing.

rm -f /etc/nginx/conf.d/default.conf  
nginx -t  
service nginx restart  
cat << EOF > /etc/nginx/conf.d/  
upstream "balancer" {  
    server weight=6; #backup;
    server weight=5 max_fails=1 fail_timeout=5s;
server {  
    listen 80;
    return 302 https://$server_name$request_uri;
server {  
    listen 443 ssl;
    limit_req zone=example burst=5;

    ssl_certificate /letsenxrypt/live/;
    ssl_certificate_key /letsencrypt/live/;
    access_log /var/log/nginx/$host.access.log main;
    access_log /var/log/nginx/$host.upstream.log up_status;
    error_log /var/log/nginx/$host.error.log warn;
    charset utf-8;
    client_max_body_size 10m;

    location / {
        proxy_read_timeout 240;
        add_header Access-Control-Allow-Origin *;

    rewrite_log on;
    charset utf-8;
    client_max_body_size 10m;

    # static content example
    location ~* \.(jpg|jpeg|gif|png|js|css|ico|avi|pdf|swf|txt)$ {
        access_log off;
        expires max;

    expires max;

    location / {
        if ($upstream_addr ~* '') {
            set $upstream_hostname $host;
        if ($upstream_addr !~ '') {
            set $upstream_hostname;
        proxy_pass http://todo-web-app;
        proxy_set_header Host $upstream_hostname;
        proxy_set_header X-Forwarded-For $remote_addr;
root@example ~# nginx -t  
root@example ~# service nginx start  

Renew certificates

You can create easy cron task to auto-renew yours certificate every month.

Create LE config for renewing.

cat << EOF > /etc/letsencrypt/cli.ini  
authenticator = webroot  
webroot-path = /var/www/  
server =  
email =  

And add cron job.

crontab -e

@monthly /opt/letsencrypt/letsencrypt-auto --config /etc/letsencrypt/cli.ini -d -d certonly && service nginx reload


Now we finished installation and configurtion for nginx and LE. We created virtual test domain configuration, obtain SSL cert from LE CA and enable https support for our test domain.
You can test how it works by typing into your browser - and you will redirected to Note if you got error 502 Bad Gateway it's mean that yours backend is not working.

See more