├── .gitignore ├── README.md ├── docker-compose.yml ├── samples ├── api │ ├── Dockerfile │ ├── app.js │ └── package.json └── website │ ├── Dockerfile │ ├── index.html │ └── index.js └── volumes ├── config └── sample-website │ └── config.js ├── nginx-sample-website └── conf.d │ └── sample-website.conf └── proxy └── templates └── nginx.tmpl /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | certs/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docker + Nginx + Let's Encrypt 2 | 3 | This simple example shows how to set up multiple websites running behind a dockerized Nginx reverse proxy and served via HTTPS using free [Let's Encrypt](https://letsencrypt.org) certificates. New sites can be added on the fly by just modifying `docker-compose.yml` and then running `docker-compose up` as the main Nginx config is automatically updated and certificates (if needed) are automatically acquired. 4 | 5 | Some of the configuration is derived from with some simplifications and updates to work with current `nginx.tmpl` from [nginx-proxy](https://github.com/jwilder/nginx-proxy) and docker-compose v2 files. 6 | 7 | ## Running the example 8 | ### Prerequisites 9 | * [docker](https://docs.docker.com/engine/installation/) (>= 1.10) 10 | * [docker-compose](https://github.com/docker/compose/releases) (>= 1.8.1) 11 | * access to (sub)domain(s) pointing to a publicly accessible server (required for TLS) 12 | 13 | ### Preparation 14 | * Clone the [repository](https://github.com/gilyes/docker-nginx-letsencrypt-sample) on the server pointed to by your domain. 15 | * In `docker-compose.yml`: 16 | * Change the **VIRTUAL_HOST** and **LETSENCRYPT_HOST** entries from *sampleapi.example.com* and *samplewebsite.example.com* to your domains. 17 | * Change **LETSENCRYPT_EMAIL** entries to the email address you want to be associated with the certificates. 18 | * In `volumes/config/sample-website/config.js` change **apiUrl** to your API endpoint as set up in the previous point in `docker-compose.yml`. 19 | 20 | ### Running 21 | In the main directory run: 22 | ```bash 23 | docker-compose up 24 | ``` 25 | 26 | This will perform the following steps: 27 | 28 | * Download the required images from Docker Hub ([nginx](https://hub.docker.com/_/nginx/), [docker-gen](https://hub.docker.com/r/jwilder/docker-gen/), [docker-letsencrypt-nginx-proxy-companion](https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion/)). 29 | * Create containers from them. 30 | * Build and create containers for the two sites located in `sample-websites`. 31 | * Start up the containers. 32 | * *docker-letsencrypt-nginx-proxy-companion* inspects containers' metadata and tries to acquire certificates as needed (if successful then saving them in a volume shared with the host and the Nginx container). 33 | * *docker-gen* also inspects containers' metadata and generates the configuration file for the main Nginx reverse proxy 34 | 35 | If everything went well then you should now be able to access your website at the provided address. 36 | 37 | ### Troubleshooting 38 | * To view logs run `docker-compose logs`. 39 | * To view the generated Nginx configuration run `docker exec -ti nginx cat /etc/nginx/conf.d/default.conf` 40 | 41 | ## How does it work 42 | 43 | The system consists of 4 main parts: 44 | 45 | * Main Nginx reverse proxy container. 46 | * Container that generates the main Nginx config based on container metadata. 47 | * Container that automatically handles the acquisition and renewal of Let's Encrypt TLS certificates. 48 | * The actual websites living in their own containers. In this example, a very simple website, talking to a very simple API. 49 | 50 | ### The main Nginx reverse proxy container 51 | This is the only publicly exposed container, routes traffic to the backend servers and provides TLS termination. 52 | 53 | Uses the official [nginx](https://hub.docker.com/_/nginx/) Docker image. 54 | 55 | It is defined in `docker-compose.yml` under the **nginx** service block: 56 | 57 | ``` 58 | services: 59 | nginx: 60 | restart: always 61 | image: nginx 62 | container_name: nginx 63 | ports: 64 | - "80:80" 65 | - "443:443" 66 | volumes: 67 | - "/etc/nginx/conf.d" 68 | - "/etc/nginx/vhost.d" 69 | - "/usr/share/nginx/html" 70 | - "./volumes/proxy/certs:/etc/nginx/certs:ro" 71 | ``` 72 | 73 | As you can see it shares a few volumes: 74 | * Configuration folder: used by the container that generates the configuration file. 75 | * Default Nginx root folder: used by the Let's Encrypt container for challenges from the CA. 76 | * Certificates folder: written to by the Let's Encrypt container, this is where the TLS certificates are maintained. 77 | 78 | ### The configuration generator container 79 | This container inspects the other running containers and based on their metadata (like **VIRTUAL_HOST** environment variable) and a template file it generates the Nginx configuration file for the main Nginx container. When a new container is spinning up this container detects that, generates the appropriate configuration entries and restarts Nginx. 80 | 81 | Uses the [jwilder/docker-gen](https://hub.docker.com/r/jwilder/docker-gen/) Docker image. 82 | 83 | It is defined in `docker-compose.yml` under the **nginx-gen** service block: 84 | 85 | ``` 86 | services: 87 | ... 88 | 89 | nginx-gen: 90 | restart: always 91 | image: jwilder/docker-gen 92 | container_name: nginx-gen 93 | volumes: 94 | - "/var/run/docker.sock:/tmp/docker.sock:ro" 95 | - "./volumes/proxy/templates/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro" 96 | volumes_from: 97 | - nginx 98 | entrypoint: /usr/local/bin/docker-gen -notify-sighup nginx -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf 99 | ``` 100 | 101 | The container reads the `nginx.tmpl` template file (source: [jwilder/nginx-proxy](https://github.com/jwilder/nginx-proxy)) via a volume shared with the host. 102 | 103 | It also mounts the Docker socket into the container in order to be able to inspect the other containers (the `"/var/run/docker.sock:/tmp/docker.sock:ro"` line). 104 | **Security warning**: mounting the Docker socket is usually discouraged because the container getting (even read-only) access to it can get root access to the host. In our case, this container is not exposed to the world so if you trust the code running inside it the risks are probably fairly low. But definitely something to take into account. See e.g. [The Dangers of Docker.sock](https://raesene.github.io/blog/2016/03/06/The-Dangers-Of-Docker.sock/) for further details. 105 | 106 | NOTE: it would be preferrable to have docker-gen only handle containers with exposed ports (via `-only-exposed` flag in the `entrypoint` script above) but currently that does not work, see e.g. . 107 | 108 | ### The Let's Encrypt container 109 | This container also inspects the other containers and acquires Let's Encrypt TLS certificates based on the **LETSENCRYPT_HOST** and **LETSENCRYPT_EMAIL** environment variables. At regular intervals it checks and renews certificates as needed. 110 | 111 | Uses the [jrcs/letsencrypt-nginx-proxy-companion](https://hub.docker.com/r/jrcs/letsencrypt-nginx-proxy-companion/) Docker image. 112 | 113 | It is defined in `docker-compose.yml` under the **letsencrypt-nginx-proxy-companion** service block: 114 | 115 | ``` 116 | services: 117 | ... 118 | 119 | letsencrypt-nginx-proxy-companion: 120 | restart: always 121 | image: jrcs/letsencrypt-nginx-proxy-companion 122 | container_name: letsencrypt-nginx-proxy-companion 123 | volumes_from: 124 | - nginx 125 | volumes: 126 | - "/var/run/docker.sock:/var/run/docker.sock:ro" 127 | - "./volumes/proxy/certs:/etc/nginx/certs:rw" 128 | environment: 129 | - NGINX_DOCKER_GEN_CONTAINER=nginx-gen 130 | ``` 131 | 132 | The container uses a volume shared with the host and the Nginx container to maintain the certificates. 133 | 134 | It also mounts the Docker socket in order to inspect the other containers. See the security warning above in the docker-gen section about the risks of that. 135 | 136 | ### The sample website and the sample API 137 | These two very simple samples are running in their own respective containers. They are defined in `docker-compose.yml` under the **sample-api** and **sample-website** service blocks: 138 | 139 | ``` 140 | services: 141 | ... 142 | 143 | sample-api: 144 | restart: always 145 | image: sample-api 146 | build: ./samples/api 147 | container_name: sample-api 148 | environment: 149 | - VIRTUAL_HOST=sampleapi.example.com 150 | - VIRTUAL_NETWORK=nginx-proxy 151 | - VIRTUAL_PORT=3000 152 | - LETSENCRYPT_HOST=sampleapi.example.com 153 | - LETSENCRYPT_EMAIL=email@example.com 154 | 155 | sample-website: 156 | restart: always 157 | image: sample-website 158 | build: ./samples/website 159 | container_name: sample-website 160 | volumes: 161 | - "./volumes/nginx-sample-website/conf.d/:/etc/nginx/conf.d" 162 | - "./volumes/config/sample-website/config.js:/usr/share/nginx/html/config.js" 163 | environment: 164 | - VIRTUAL_HOST=samplewebsite.example.com 165 | - VIRTUAL_NETWORK=nginx-proxy 166 | - VIRTUAL_PORT=80 167 | - LETSENCRYPT_HOST=sample.example.com 168 | - LETSENCRYPT_EMAIL=email@example.com 169 | ``` 170 | The important part here are the environment variables. These are used by the config generator and certificate maintainer containers to set up the system. 171 | 172 | The source code for these two images is in the `samples` subfolder, the images are built from there. In a real-world scenario these images would likely come from a Docker registry. 173 | 174 | ## Conclusion 175 | This can be a fairly simple way to have easy, reproducible deploys for websites with free, auto-renewing TLS certificates. 176 | 177 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | nginx: 5 | restart: always 6 | image: nginx 7 | container_name: nginx 8 | ports: 9 | - "80:80" 10 | - "443:443" 11 | volumes: 12 | - "/etc/nginx/conf.d" 13 | - "/etc/nginx/vhost.d" 14 | - "/usr/share/nginx/html" 15 | - "./volumes/proxy/certs:/etc/nginx/certs:ro" 16 | 17 | nginx-gen: 18 | restart: always 19 | image: jwilder/docker-gen 20 | container_name: nginx-gen 21 | volumes: 22 | - "/var/run/docker.sock:/tmp/docker.sock:ro" 23 | - "./volumes/proxy/templates/nginx.tmpl:/etc/docker-gen/templates/nginx.tmpl:ro" 24 | volumes_from: 25 | - nginx 26 | entrypoint: /usr/local/bin/docker-gen -notify-sighup nginx -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf 27 | 28 | letsencrypt-nginx-proxy-companion: 29 | restart: always 30 | image: jrcs/letsencrypt-nginx-proxy-companion 31 | container_name: letsencrypt-nginx-proxy-companion 32 | volumes_from: 33 | - nginx 34 | volumes: 35 | - "/var/run/docker.sock:/var/run/docker.sock:ro" 36 | - "./volumes/proxy/certs:/etc/nginx/certs:rw" 37 | environment: 38 | - NGINX_DOCKER_GEN_CONTAINER=nginx-gen 39 | 40 | sample-api: 41 | restart: always 42 | image: sample-api 43 | build: ./samples/api 44 | container_name: sample-api 45 | environment: 46 | - VIRTUAL_HOST=sampleapi.example.com 47 | - VIRTUAL_NETWORK=nginx-proxy 48 | - VIRTUAL_PORT=3000 49 | - LETSENCRYPT_HOST=sampleapi.example.com 50 | - LETSENCRYPT_EMAIL=email@example.com 51 | 52 | sample-website: 53 | restart: always 54 | image: sample-website 55 | build: ./samples/website 56 | container_name: sample-website 57 | volumes: 58 | - "./volumes/nginx-sample-website/conf.d/:/etc/nginx/conf.d" 59 | - "./volumes/config/sample-website/config.js:/usr/share/nginx/html/config.js" 60 | environment: 61 | - VIRTUAL_HOST=samplewebsite.example.com 62 | - VIRTUAL_NETWORK=nginx-proxy 63 | - VIRTUAL_PORT=80 64 | - LETSENCRYPT_HOST=sample.example.com 65 | - LETSENCRYPT_EMAIL=email@example.com 66 | -------------------------------------------------------------------------------- /samples/api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node 2 | 3 | EXPOSE 3000 4 | 5 | RUN adduser -S app 6 | 7 | ENV HOME=/home/app 8 | 9 | COPY package.json $HOME/src/ 10 | RUN chown -R app $HOME/* 11 | 12 | USER app 13 | WORKDIR $HOME/src 14 | 15 | RUN npm install && \ 16 | npm cache clean --force 17 | 18 | USER root 19 | COPY . $HOME/src 20 | RUN chown -R app $HOME/* 21 | 22 | USER app 23 | 24 | CMD ["node", "app.js"] 25 | -------------------------------------------------------------------------------- /samples/api/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var cors = require('cors'); 3 | 4 | var app = express(); 5 | app.use(cors()); 6 | 7 | app.get('/hello', function (req, res) { 8 | res.send('Hello World!'); 9 | }); 10 | 11 | app.listen(3000, function () { 12 | console.log('Listening on port 3000.'); 13 | }); 14 | -------------------------------------------------------------------------------- /samples/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sample-api", 3 | "version": "1.0.0", 4 | "description": "Express Hello World sample", 5 | "main": "app.js", 6 | "author": "George Ilyes", 7 | "license": "MIT", 8 | "dependencies": { 9 | "express": "^4.14.0", 10 | "cors": "^2.8.1" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /samples/website/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | 3 | COPY ./ /usr/share/nginx/html/ 4 | -------------------------------------------------------------------------------- /samples/website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample webpage 5 | 6 | 7 |
8 | 9 |
10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /samples/website/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | (function() { 4 | 5 | var button = document.getElementById('button'); 6 | var result = document.getElementById('result'); 7 | 8 | button.addEventListener('click', function() { 9 | fetch(window.config.apiUrl + '/hello', { method: 'GET' }) 10 | .then(function (response) { 11 | return response.text(); 12 | }) 13 | .then(function (body) { 14 | result.innerHTML += body; 15 | }); 16 | }); 17 | 18 | })(); 19 | 20 | -------------------------------------------------------------------------------- /volumes/config/sample-website/config.js: -------------------------------------------------------------------------------- 1 | window.config = { 2 | apiUrl: 'https://sampleapi.example.com' 3 | }; 4 | -------------------------------------------------------------------------------- /volumes/nginx-sample-website/conf.d/sample-website.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name sample-website; 4 | root /usr/share/nginx/html; 5 | } 6 | -------------------------------------------------------------------------------- /volumes/proxy/templates/nginx.tmpl: -------------------------------------------------------------------------------- 1 | {{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} 2 | 3 | {{ define "upstream" }} 4 | {{ if .Address }} 5 | {{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} 6 | {{ if and .Container.Node.ID .Address.HostPort }} 7 | # {{ .Container.Node.Name }}/{{ .Container.Name }} 8 | server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }}; 9 | {{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} 10 | {{ else if .Network }} 11 | # {{ .Container.Name }} 12 | server {{ .Network.IP }}:{{ .Address.Port }}; 13 | {{ end }} 14 | {{ else if .Network }} 15 | # {{ .Container.Name }} 16 | server {{ .Network.IP }} down; 17 | {{ end }} 18 | {{ end }} 19 | 20 | # If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the 21 | # scheme used to connect to this server 22 | map $http_x_forwarded_proto $proxy_x_forwarded_proto { 23 | default $http_x_forwarded_proto; 24 | '' $scheme; 25 | } 26 | 27 | # If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any 28 | # Connection header that may have been passed to this server 29 | map $http_upgrade $proxy_connection { 30 | default upgrade; 31 | '' close; 32 | } 33 | 34 | gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; 35 | 36 | log_format vhost '$host $remote_addr - $remote_user [$time_local] ' 37 | '"$request" $status $body_bytes_sent ' 38 | '"$http_referer" "$http_user_agent"'; 39 | 40 | access_log off; 41 | 42 | {{ if (exists "/etc/nginx/proxy.conf") }} 43 | include /etc/nginx/proxy.conf; 44 | {{ else }} 45 | # HTTP 1.1 support 46 | proxy_http_version 1.1; 47 | proxy_buffering off; 48 | proxy_set_header Host $http_host; 49 | proxy_set_header Upgrade $http_upgrade; 50 | proxy_set_header Connection $proxy_connection; 51 | proxy_set_header X-Real-IP $remote_addr; 52 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 53 | proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; 54 | 55 | # Mitigate httpoxy attack (see README for details) 56 | proxy_set_header Proxy ""; 57 | {{ end }} 58 | 59 | server { 60 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 61 | listen 80; 62 | access_log /var/log/nginx/access.log vhost; 63 | return 503; 64 | } 65 | 66 | {{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 67 | server { 68 | server_name _; # This is just an invalid value which will never trigger on a real hostname. 69 | listen 443 ssl http2; 70 | access_log /var/log/nginx/access.log vhost; 71 | return 503; 72 | 73 | ssl_session_tickets off; 74 | ssl_certificate /etc/nginx/certs/default.crt; 75 | ssl_certificate_key /etc/nginx/certs/default.key; 76 | } 77 | {{ end }} 78 | 79 | {{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} 80 | 81 | upstream {{ $host }} { 82 | {{ range $container := $containers }} 83 | {{ $addrLen := len $container.Addresses }} 84 | 85 | {{ range $knownNetwork := $CurrentContainer.Networks }} 86 | {{ range $containerNetwork := $container.Networks }} 87 | {{ if eq $knownNetwork.Name $containerNetwork.Name }} 88 | ## Can be connect with "{{ $containerNetwork.Name }}" network 89 | 90 | {{/* If only 1 port exposed, use that */}} 91 | {{ if eq $addrLen 1 }} 92 | {{ $address := index $container.Addresses 0 }} 93 | {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} 94 | {{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}} 95 | {{ else }} 96 | {{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }} 97 | {{ $address := where $container.Addresses "Port" $port | first }} 98 | {{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} 99 | {{ end }} 100 | {{ end }} 101 | {{ end }} 102 | {{ end }} 103 | {{ end }} 104 | } 105 | 106 | {{ $default_host := or ($.Env.DEFAULT_HOST) "" }} 107 | {{ $default_server := index (dict $host "" $default_host "default_server") $host }} 108 | 109 | {{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} 110 | {{ $proto := or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http" }} 111 | 112 | {{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} 113 | {{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }} 114 | 115 | {{/* Get the first cert name defined by containers w/ the same vhost */}} 116 | {{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} 117 | 118 | {{/* Get the best matching cert by name for the vhost. */}} 119 | {{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} 120 | 121 | {{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} 122 | {{ $vhostCert := trimSuffix ".crt" $vhostCert }} 123 | {{ $vhostCert := trimSuffix ".key" $vhostCert }} 124 | 125 | {{/* Use the cert specified on the container or fallback to the best vhost match */}} 126 | {{ $cert := (coalesce $certName $vhostCert) }} 127 | 128 | {{ $is_https := (and (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} 129 | 130 | {{ if $is_https }} 131 | 132 | {{ if eq $https_method "redirect" }} 133 | server { 134 | server_name {{ $host }}; 135 | listen 80 {{ $default_server }}; 136 | access_log /var/log/nginx/access.log vhost; 137 | return 301 https://$host$request_uri; 138 | } 139 | {{ end }} 140 | 141 | server { 142 | server_name {{ $host }}; 143 | listen 443 ssl http2 {{ $default_server }}; 144 | access_log /var/log/nginx/access.log vhost; 145 | 146 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 147 | ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; 148 | 149 | ssl_prefer_server_ciphers on; 150 | ssl_session_timeout 5m; 151 | ssl_session_cache shared:SSL:50m; 152 | ssl_session_tickets off; 153 | 154 | ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; 155 | ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; 156 | 157 | {{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} 158 | ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; 159 | {{ end }} 160 | 161 | {{ if (ne $https_method "noredirect") }} 162 | add_header Strict-Transport-Security "max-age=31536000"; 163 | {{ end }} 164 | 165 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 166 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 167 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 168 | include /etc/nginx/vhost.d/default; 169 | {{ end }} 170 | 171 | location / { 172 | {{ if eq $proto "uwsgi" }} 173 | include uwsgi_params; 174 | uwsgi_pass {{ trim $proto }}://{{ trim $host }}; 175 | {{ else }} 176 | proxy_pass {{ trim $proto }}://{{ trim $host }}; 177 | {{ end }} 178 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 179 | auth_basic "Restricted {{ $host }}"; 180 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 181 | {{ end }} 182 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 183 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 184 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 185 | include /etc/nginx/vhost.d/default_location; 186 | {{ end }} 187 | } 188 | } 189 | 190 | {{ end }} 191 | 192 | {{ if or (not $is_https) (eq $https_method "noredirect") }} 193 | 194 | server { 195 | server_name {{ $host }}; 196 | listen 80 {{ $default_server }}; 197 | access_log /var/log/nginx/access.log vhost; 198 | 199 | {{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} 200 | include {{ printf "/etc/nginx/vhost.d/%s" $host }}; 201 | {{ else if (exists "/etc/nginx/vhost.d/default") }} 202 | include /etc/nginx/vhost.d/default; 203 | {{ end }} 204 | 205 | location / { 206 | {{ if eq $proto "uwsgi" }} 207 | include uwsgi_params; 208 | uwsgi_pass {{ trim $proto }}://{{ trim $host }}; 209 | {{ else }} 210 | proxy_pass {{ trim $proto }}://{{ trim $host }}; 211 | {{ end }} 212 | {{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} 213 | auth_basic "Restricted {{ $host }}"; 214 | auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; 215 | {{ end }} 216 | {{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} 217 | include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; 218 | {{ else if (exists "/etc/nginx/vhost.d/default_location") }} 219 | include /etc/nginx/vhost.d/default_location; 220 | {{ end }} 221 | } 222 | } 223 | 224 | {{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} 225 | server { 226 | server_name {{ $host }}; 227 | listen 443 ssl http2 {{ $default_server }}; 228 | access_log /var/log/nginx/access.log vhost; 229 | return 500; 230 | 231 | ssl_certificate /etc/nginx/certs/default.crt; 232 | ssl_certificate_key /etc/nginx/certs/default.key; 233 | } 234 | {{ end }} 235 | 236 | {{ end }} 237 | {{ end }} 238 | --------------------------------------------------------------------------------