Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,12 @@ If you are using multiple hostnames for a single container (e.g. `VIRTUAL_HOST=e
If you want most of your virtual hosts to use a default single `location` block configuration and then override on a few specific ones, add those settings to the `/etc/nginx/vhost.d/default_location` file. This file
will be used on any virtual host which does not have a `/etc/nginx/vhost.d/{VIRTUAL_HOST}_location` file associated with it.

#### Multiple ports per virtual host

A container can expose multiple ports that you want to proxy to different host names. In order to achieve this, you need
to activate pass `VIRTUAL_HOST_SPECIFIC_PORT` with the value `true` to *nginx-proxy*. Then you can start start any container
and pass multiple host names and ports `VIRTUAL_HOST=subdomain1.youdomain.com,subdomain2.youdomain.com:5000`

### Contributing

Before submitting pull requests or issues, please check github to make sure an existing issue or pull request is not already open.
Expand Down
52 changes: 30 additions & 22 deletions nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ server {

{{ $host := trim $host }}
{{ $is_regexp := hasPrefix "~" $host }}
{{ $upstream_name := when $is_regexp (sha1 $host) $host }}
{{ $is_specific_port := eq (or ($.Env.VIRTUAL_HOST_SPECIFIC_PORT) "") "true" }}
{{ $upstream_name := when (or $is_regexp $is_specific_port) (sha1 $host) $host }}
{{ $server_name := when $is_specific_port (index (split $host ":") 0) $host }}

# {{ $host }}
upstream {{ $upstream_name }} {
Expand All @@ -132,8 +134,14 @@ upstream {{ $upstream_name }} {
{{ if (and (ne $containerNetwork.Name "ingress") (or (eq $knownNetwork.Name $containerNetwork.Name) (eq $knownNetwork.Name "host"))) }}
## Can be connected with "{{ $containerNetwork.Name }}" network

{{ $split_host_port := split $host ":" }}
{{ if (and $is_specific_port (eq (len $split_host_port) 2)) }}
{{/* If ports per virtual host specified, use that */}}
{{ $port := index $split_host_port 1 }}
{{ $address := where $container.Addresses "Port" $port | first }}
{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
{{/* If only 1 port exposed, use that */}}
{{ if eq $addrLen 1 }}
{{ else if eq $addrLen 1 }}
{{ $address := index $container.Addresses 0 }}
{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }}
{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}}
Expand All @@ -152,7 +160,7 @@ upstream {{ $upstream_name }} {
}

{{ $default_host := or ($.Env.DEFAULT_HOST) "" }}
{{ $default_server := index (dict $host "" $default_host "default_server") $host }}
{{ $default_server := index (dict $server_name "" $default_host "default_server") $server_name }}

{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}}
{{ $proto := trim (or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http") }}
Expand All @@ -177,7 +185,7 @@ upstream {{ $upstream_name }} {
{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }}

{{/* Get the best matching cert by name for the vhost. */}}
{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}}
{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $server_name))}}

{{/* vhostCert is actually a filename so remove any suffixes since they are added later */}}
{{ $vhostCert := trimSuffix ".crt" $vhostCert }}
Expand All @@ -192,7 +200,7 @@ upstream {{ $upstream_name }} {

{{ if eq $https_method "redirect" }}
server {
server_name {{ $host }};
server_name {{ $server_name }};
listen 80 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:80 {{ $default_server }};
Expand All @@ -203,7 +211,7 @@ server {
{{ end }}

server {
server_name {{ $host }};
server_name {{ $server_name }};
listen 443 ssl http2 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:443 ssl http2 {{ $default_server }};
Expand Down Expand Up @@ -266,8 +274,8 @@ server {
add_header Strict-Transport-Security "{{ trim $hsts }}" always;
{{ end }}

{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $host }};
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $server_name)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $server_name }};
{{ else if (exists "/etc/nginx/vhost.d/default") }}
include /etc/nginx/vhost.d/default;
{{ end }}
Expand All @@ -284,12 +292,12 @@ server {
proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
{{ end }}

{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
auth_basic "Restricted {{ $host }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $server_name)) }}
auth_basic "Restricted {{ $server_name }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $server_name) }};
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $server_name)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $server_name}};
{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
include /etc/nginx/vhost.d/default_location;
{{ end }}
Expand All @@ -301,7 +309,7 @@ server {
{{ if or (not $is_https) (eq $https_method "noredirect") }}

server {
server_name {{ $host }};
server_name {{ $server_name }};
listen 80 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:80 {{ $default_server }};
Expand All @@ -313,8 +321,8 @@ server {
include /etc/nginx/network_internal.conf;
{{ end }}

{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $host }};
{{ if (exists (printf "/etc/nginx/vhost.d/%s" $server_name)) }}
include {{ printf "/etc/nginx/vhost.d/%s" $server_name }};
{{ else if (exists "/etc/nginx/vhost.d/default") }}
include /etc/nginx/vhost.d/default;
{{ end }}
Expand All @@ -330,12 +338,12 @@ server {
{{ else }}
proxy_pass {{ trim $proto }}://{{ trim $upstream_name }};
{{ end }}
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }}
auth_basic "Restricted {{ $host }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }};
{{ if (exists (printf "/etc/nginx/htpasswd/%s" $server_name)) }}
auth_basic "Restricted {{ $server_name }}";
auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $server_name) }};
{{ end }}
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $host}};
{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $server_name)) }}
include {{ printf "/etc/nginx/vhost.d/%s_location" $server_name }};
{{ else if (exists "/etc/nginx/vhost.d/default_location") }}
include /etc/nginx/vhost.d/default_location;
{{ end }}
Expand All @@ -344,7 +352,7 @@ server {

{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }}
server {
server_name {{ $host }};
server_name {{ $server_name }};
listen 443 ssl http2 {{ $default_server }};
{{ if $enable_ipv6 }}
listen [::]:443 ssl http2 {{ $default_server }};
Expand Down
16 changes: 16 additions & 0 deletions test/test_specific-ports/test_specified_all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest


def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy.tld/port")
assert r.status_code == 503

def test_webA_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webA.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 5000\n"

def test_webB_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webB.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 5001\n"
17 changes: 17 additions & 0 deletions test/test_specific-ports/test_specified_all.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
web:
image: web
expose:
- "5000"
- "5001"
environment:
WEB_PORTS: "5000 5001"
VIRTUAL_HOST: webA.nginx-proxy.tld:5000,webB.nginx-proxy.tld:5001


sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
environment:
VIRTUAL_HOST_SPECIFIC_PORT: "true"
16 changes: 16 additions & 0 deletions test/test_specific-ports/test_specified_mixed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest


def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy.tld/port")
assert r.status_code == 503

def test_webA_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webA.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 80\n"

def test_webB_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webB.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 5001\n"
17 changes: 17 additions & 0 deletions test/test_specific-ports/test_specified_mixed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
web:
image: web
expose:
- "80"
- "5001"
environment:
WEB_PORTS: "80 5001"
VIRTUAL_HOST: webA.nginx-proxy.tld,webB.nginx-proxy.tld:5001


sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
environment:
VIRTUAL_HOST_SPECIFIC_PORT: "true"
16 changes: 16 additions & 0 deletions test/test_specific-ports/test_specified_none.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest


def test_unknown_virtual_host_is_503(docker_compose, nginxproxy):
r = nginxproxy.get("http://unknown.nginx-proxy.tld/port")
assert r.status_code == 503

def test_webA_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webA.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 80\n"

def test_webB_is_forwarded(docker_compose, nginxproxy):
r = nginxproxy.get("http://webB.nginx-proxy.tld/port")
assert r.status_code == 200
assert r.text == "answer from port 80\n"
16 changes: 16 additions & 0 deletions test/test_specific-ports/test_specified_none.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
web:
image: web
expose:
- "80"
environment:
WEB_PORTS: 80
VIRTUAL_HOST: webA.nginx-proxy.tld,webB.nginx-proxy.tld


sut:
image: jwilder/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
- ../lib/ssl/dhparam.pem:/etc/nginx/dhparam/dhparam.pem:ro
environment:
VIRTUAL_HOST_SPECIFIC_PORT: "true"