From f960dc58560027e8781e4c16b6ced56679f459d1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 25 May 2026 05:32:11 -0400 Subject: [PATCH] feat: warn on deprecated listen http2 in custom nginx templates nginx 1.25.1 deprecated the `listen ... http2` parameter in favor of a standalone `http2 on;` directive. Apps that ship a custom `nginx.conf.sigil` forked from an old default still hardcode `listen ... ssl http2;` and trigger `nginx: [warn]` lines from `nginx -t`. Surface a deprecation warning during `nginx_build_config` whenever the active template is app- or plugin-supplied and still contains the deprecated parameter, matching the existing warnings for `DOKKU_APP_LISTENERS`, `NGINX_SSL_PORT`, and `NGINX_PORT`, and point users at the `HTTP2_DIRECTIVE_SUPPORTED` variable already used by the default template. --- .../file-formats/nginx-conf-sigil.md | 19 ++++++ plugins/nginx-vhosts/functions | 4 ++ tests/unit/nginx-vhosts_7.bats | 61 +++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/docs/appendices/file-formats/nginx-conf-sigil.md b/docs/appendices/file-formats/nginx-conf-sigil.md index 3cb67eecf..26b7269e6 100644 --- a/docs/appendices/file-formats/nginx-conf-sigil.md +++ b/docs/appendices/file-formats/nginx-conf-sigil.md @@ -7,3 +7,22 @@ The `nginx.conf.sigil` file is used to configure the nginx server for an applica A custom `nginx.conf.sigil` is pre-validated at the start of every deploy, immediately after it is extracted from the source tree and before the build phase runs. Pre-validation renders the template via sigil with the same parameters used at deploy time, wraps the rendered config in a minimal `events`/`http` scaffold, and runs `nginx -t` against the result. The deploy is aborted if either the sigil render or the `nginx -t` check fails, so build work is not wasted on a syntactically invalid template. When no app listeners exist yet (typical for first deploys), pre-validation injects a placeholder `127.0.0.1:5000` listener for `DOKKU_APP_WEB_LISTENERS` so that the rendered upstream block has a static server entry and `nginx -t` does not bail out on "host not found in upstream". Pre-validation is skipped when the proxy type is not `nginx` or when `disable-custom-config` is set to `true` for the app. + +## HTTP/2 + +nginx 1.25.1 deprecated the `http2` parameter on the `listen` directive in favor of a standalone `http2 on;` directive. Custom `nginx.conf.sigil` templates that hardcode `listen ... ssl http2;` will produce `nginx: [warn] the "listen ... http2" directive is deprecated` warnings when run against nginx 1.25.1 or newer. + +Dokku exposes the `HTTP2_DIRECTIVE_SUPPORTED` template variable, set to `"true"` when the host nginx is 1.25.1 or newer, so a single template can render the correct syntax against either version. The default template uses this pattern: + +``` +{{ if eq $.HTTP2_DIRECTIVE_SUPPORTED "true" }} +listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl; +listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} ssl; +http2 on; +{{ else }} +listen [{{ $.NGINX_BIND_ADDRESS_IP6 }}]:{{ $listen_port }} ssl http2; +listen {{ if $.NGINX_BIND_ADDRESS_IP4 }}{{ $.NGINX_BIND_ADDRESS_IP4 }}:{{end}}{{ $listen_port }} ssl http2; +{{ end }} +``` + +Dokku logs a deprecation warning during deploys when a custom template still uses the `listen ... http2` form, so the offending template can be located via the deploy output. diff --git a/plugins/nginx-vhosts/functions b/plugins/nginx-vhosts/functions index f15556cbf..bdc1048af 100755 --- a/plugins/nginx-vhosts/functions +++ b/plugins/nginx-vhosts/functions @@ -540,6 +540,10 @@ nginx_build_config() { if grep NGINX_PORT "$NGINX_TEMPLATE"; then dokku_log_warn "Deprecated: Usage of NGINX_PORT within nginx.conf.sigil templates is deprecated in favor of PROXY_PORT" fi + + if [[ "$NGINX_TEMPLATE_SOURCE" != "built-in" ]] && grep -E 'listen[[:space:]]+.*[[:space:]]http2[;[:space:]]' "$NGINX_TEMPLATE"; then + dokku_log_warn "Deprecated: Usage of 'listen ... http2' within nginx.conf.sigil templates is deprecated since nginx 1.25.1 in favor of the standalone 'http2 on;' directive. Use the {{ if eq \$.HTTP2_DIRECTIVE_SUPPORTED \"true\" }} conditional from the default template to support both old and new nginx versions." + fi fi xargs -i echo "-----> Configuring {}...(using $NGINX_TEMPLATE_SOURCE template)" <<<"$(echo "${SSL_VHOSTS}" "${NONSSL_VHOSTS}" | tr ' ' '\n' | sort -u)" diff --git a/tests/unit/nginx-vhosts_7.bats b/tests/unit/nginx-vhosts_7.bats index b72896822..591266eb1 100644 --- a/tests/unit/nginx-vhosts_7.bats +++ b/tests/unit/nginx-vhosts_7.bats @@ -99,3 +99,64 @@ teardown() { assert_output_contains "Pre-validating custom nginx.conf.sigil" assert_output_contains "Custom nginx.conf.sigil failed nginx -t validation" } + +@test "(nginx-vhosts) proxy:build-config (custom nginx template with deprecated listen http2)" { + run /bin/bash -c "dokku domains:add $TEST_APP www.test.app.${DOKKU_DOMAIN}" + echo "output: $output" + echo "status: $status" + assert_success + + run deploy_app python dokku@$DOKKU_DOMAIN:$TEST_APP custom_nginx_template_with_http2_listen + echo "output: $output" + echo "status: $status" + assert_success + + assert_output_contains "Deprecated: Usage of 'listen ... http2' within nginx.conf.sigil templates is deprecated" -1 +} + +custom_nginx_template_with_http2_listen() { + local APP="$1" + local APP_REPO_DIR="$2" + [[ -z "$APP" ]] && local APP="$TEST_APP" + mkdir -p "$APP_REPO_DIR" + + echo "injecting custom_nginx_template_with_http2_listen -> $APP_REPO_DIR/nginx.conf.sigil" + cat <"$APP_REPO_DIR/nginx.conf.sigil" +{{ range \$port_map := .PROXY_PORT_MAP | split " " }} +{{ \$port_map_list := \$port_map | split ":" }} +{{ \$scheme := index \$port_map_list 0 }} +{{ \$listen_port := index \$port_map_list 1 }} +{{ \$upstream_port := index \$port_map_list 2 }} + +server { + listen [::]:{{ \$listen_port }} http2; + listen {{ \$listen_port }} http2; + server_name {{ $.NOSSL_SERVER_NAME }} customtemplate.${DOKKU_DOMAIN}; + + location / { + proxy_pass http://{{ $.APP }}-{{ \$upstream_port }}; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host \$http_host; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_set_header X-Forwarded-For \$remote_addr; + proxy_set_header X-Forwarded-Port \$server_port; + proxy_set_header X-Request-Start \$msec; + } + include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; +} +{{ end }} + +{{ if $.DOKKU_APP_WEB_LISTENERS }} +{{ range \$upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }} +upstream {{ $.APP }}-{{ \$upstream_port }} { +{{ range \$listeners := $.DOKKU_APP_WEB_LISTENERS | split " " }} +{{ \$listener_list := \$listeners | split ":" }} +{{ \$listener_ip := index \$listener_list 0 }} + server {{ \$listener_ip }}:{{ \$upstream_port }};{{ end }} +} +{{ end }}{{ end }} +EOF + cat "$APP_REPO_DIR/nginx.conf.sigil" +}