From a331b85dc3a3ea8ae50d4124ed01e19404fdcecf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andr=C3=A9=20Padez?=
Date: Sat, 29 Aug 2026 22:19:31 +0100
Subject: [PATCH] a host that predates HTTP/2 can be repaired
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
`http2_support: true` is the default now and is set when a host is secured, but both hosts here were
created and secured before either change, so nothing re-runs and they stay on HTTP/1.1 forever.
Lighthouse costs that 670 ms.
An explicit action rather than a fix applied on load: a panel that silently rewrites the proxy while
somebody is reading it is not a thing to build, and the change reloads nginx for every site behind it.
Only offered where it can do something — a host with a certificate that lacks it. HTTP/2 is negotiated
during the TLS handshake, so on a plain host the field is stored and never used, and the button would
do nothing visible. A host created since the default never shows it: this is a repair, not a setting.
Co-Authored-By: Claude Opus 5 (1M context)
---
web/ProxiesBlock.tsx | 45 +++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)
diff --git a/web/ProxiesBlock.tsx b/web/ProxiesBlock.tsx
index 7677813..af32f9d 100644
--- a/web/ProxiesBlock.tsx
+++ b/web/ProxiesBlock.tsx
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react';
-import { CircleDot, Globe, Loader2, Lock, Plus, ShieldCheck } from 'lucide-react';
+import { CircleDot, Gauge, Globe, Loader2, Lock, Plus, ShieldCheck } from 'lucide-react';
import { useClient } from 'hooks/useClient';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -46,6 +46,7 @@ type Host = {
enabled: boolean;
ssl_forced: boolean;
certificate_id: number;
+ http2_support: boolean;
meta?: { nginx_online?: boolean };
};
@@ -191,6 +192,33 @@ export const ProxiesBlock = ({ servers }: { servers: Server[] }) => {
});
};
+ /**
+ * Turn HTTP/2 on for a host that predates it being the default.
+ *
+ * Offered rather than done on load, even though it is one field and always the right answer: a
+ * panel that silently rewrites the proxy while you are reading it is not something to build, and
+ * the change momentarily reloads nginx for every site behind it.
+ *
+ * Only shown for a host that HAS a certificate — HTTP/2 is negotiated during the TLS handshake, so
+ * on a plain host the setting is stored and never used, and offering it there would be offering a
+ * button that does nothing visible.
+ */
+ const upgrade = async (host: Host) => {
+ setBusy(`http2:${host.id}`);
+ setFailed(false);
+ setOutput(null);
+ try {
+ await clientRef.current.put(`/proxy-manager/npm/nginx/proxy-hosts/${host.id}`, { http2_support: true });
+ setOutput(`${host.domain_names.join(', ')} → HTTP/2`);
+ await load();
+ } catch (failure) {
+ setOutput(readableError(failure));
+ setFailed(true);
+ } finally {
+ setBusy(null);
+ }
+ };
+
const secure = async (host: Host) => {
const names = host.domain_names;
setBusy(`cert:${host.id}`);
@@ -320,6 +348,21 @@ export const ProxiesBlock = ({ servers }: { servers: Server[] }) => {
)}
+ {/*
+ Only when it is both possible and missing. A host created since http2_support became the
+ default never shows this, which is the point — it is a repair, not a setting.
+ */}
+ {host.certificate_id > 0 && !host.http2_support && (
+
+ )}
+
{!host.certificate_id && (