Skip to content

Commit

Permalink
feat: Add support for HTTP keep-alive between the proxy and upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
rhansen committed Mar 28, 2022
1 parent 89bf84f commit fe0c391
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ docker run -d -p 80:80 -p 443:443 \

You'll need apache2-utils on the machine where you plan to create the htpasswd file. Follow these [instructions](http://httpd.apache.org/docs/2.2/programs/htpasswd.html)

### Upstream (Backend) Server HTTP Keep-Alive Support

To enable HTTP keep-alive between `nginx-proxy` and a backend server, set the `com.github.nginx-proxy.nginx-proxy.keepalive` label on the server's container to the desired maximum number of idle connections. See the [NGINX keepalive documentation](https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive) and the [Docker label documentation](https://docs.docker.com/config/labels-custom-metadata/) for details.

### Custom Nginx Configuration

If you need to configure Nginx beyond what is possible using environment variables, you can provide custom configuration files on either a proxy-wide or per-`VIRTUAL_HOST` basis.
Expand Down
25 changes: 21 additions & 4 deletions nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
{{ end }}

{{ define "location" }}
{{- $keepalive := first (keys (groupByLabel .Containers "com.github.nginx-proxy.nginx-proxy.keepalive")) }}
location {{ .Path }} {
{{ if eq .NetworkTag "internal" }}
# Only allow traffic from internal clients
Expand All @@ -64,10 +65,16 @@ location {{ .Path }} {
root {{ trim .VhostRoot }};
include fastcgi_params;
fastcgi_pass {{ trim .Upstream }};
{{- if $keepalive }}
fastcgi_keep_conn on;
{{- end }}
{{ else if eq .Proto "grpc" }}
grpc_pass {{ trim .Proto }}://{{ trim .Upstream }};
{{ else }}
proxy_pass {{ trim .Proto }}://{{ trim .Upstream }}{{ trim .Dest }};
{{- if $keepalive }}
proxy_set_header Connection $proxy_connection_noclose;
{{- end }}
{{ end }}

{{ if (exists (printf "/etc/nginx/htpasswd/%s" .Host)) }}
Expand Down Expand Up @@ -140,6 +147,10 @@ location {{ .Path }} {
# Fallback entry
server 127.0.0.1 down;
{{ end }}
{{- $keepalive := first (keys (groupByLabel .Containers "com.github.nginx-proxy.nginx-proxy.keepalive")) }}
{{- if $keepalive }}
keepalive {{ $keepalive }};
{{- end }}
}
{{ end }}

Expand Down Expand Up @@ -167,6 +178,12 @@ map $http_upgrade $proxy_connection {
default upgrade;
'' close;
}
map $http_upgrade $proxy_connection_noclose {
default upgrade;
# By default, NGINX sets "Connection: close". Cancel that to allow keepalive
# connections between NGINX and the upstream server.
'' '';
}

# Apply fix for very long server names
server_names_hash_bucket_size 128;
Expand Down Expand Up @@ -388,7 +405,7 @@ server {

{{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
{{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
{{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }}
{{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag "Containers" $containers) }}
{{ else }}
{{ range $path, $containers := $paths }}
{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}}
Expand All @@ -399,7 +416,7 @@ server {
{{ $sum := sha1 $path }}
{{ $upstream := printf "%s-%s" $upstream_name $sum }}
{{ $dest := (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }}
{{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }}
{{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag "Containers" $containers) }}
{{ end }}
{{ if (not (contains $paths "/")) }}
location / {
Expand Down Expand Up @@ -436,7 +453,7 @@ server {

{{/* Get the NETWORK_ACCESS defined by containers w/ the same vhost, falling back to "external" */}}
{{ $network_tag := or (first (groupByKeys $containers "Env.NETWORK_ACCESS")) "external" }}
{{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag) }}
{{ template "location" (dict "Path" "/" "Proto" $proto "Upstream" $upstream_name "Host" $host "VhostRoot" $vhost_root "Dest" "" "NetworkTag" $network_tag "Containers" $containers) }}
{{ else }}
{{ range $path, $containers := $paths }}
{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost-vpath, falling back to "http" */}}
Expand All @@ -447,7 +464,7 @@ server {
{{ $sum := sha1 $path }}
{{ $upstream := printf "%s-%s" $upstream_name $sum }}
{{ $dest := (or (first (groupByKeys $containers "Env.VIRTUAL_DEST")) "") }}
{{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag) }}
{{ template "location" (dict "Path" $path "Proto" $proto "Upstream" $upstream "Host" $host "VhostRoot" $vhost_root "Dest" $dest "NetworkTag" $network_tag "Containers" $containers) }}
{{ end }}
{{ if (not (contains $paths "/")) }}
location / {
Expand Down
12 changes: 12 additions & 0 deletions test/test_keepalive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import re


def test_keepalive_disabled(docker_compose, nginxproxy):
r = nginxproxy.get("http://web.nginx-proxy.example/headers")
assert r.status_code == 200
assert re.search(fr'(?m)^(?i:Connection): close$', r.text)

def test_keepalive_enabled(docker_compose, nginxproxy):
r = nginxproxy.get("http://web-keepalive.nginx-proxy.example/headers")
assert r.status_code == 200
assert not re.search(fr'(?m)^(?i:Connection):', r.text)
23 changes: 23 additions & 0 deletions test/test_keepalive.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
web:
image: web
expose:
- "80"
environment:
WEB_PORTS: 80
VIRTUAL_HOST: web.nginx-proxy.example

web-keepalive:
image: web
expose:
- "80"
environment:
WEB_PORTS: 80
VIRTUAL_HOST: web-keepalive.nginx-proxy.example
labels:
com.github.nginx-proxy.nginx-proxy.keepalive: "64"


sut:
image: nginxproxy/nginx-proxy:test
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro

0 comments on commit fe0c391

Please sign in to comment.