a host that predates HTTP/2 can be repaired

`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) <noreply@anthropic.com>
This commit is contained in:
2026-08-29 22:19:31 +01:00
co-authored by Claude Opus 5
parent 9b64933797
commit a331b85dc3
+44 -1
View File
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react'; 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 { useClient } from 'hooks/useClient';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
@@ -46,6 +46,7 @@ type Host = {
enabled: boolean; enabled: boolean;
ssl_forced: boolean; ssl_forced: boolean;
certificate_id: number; certificate_id: number;
http2_support: boolean;
meta?: { nginx_online?: 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 secure = async (host: Host) => {
const names = host.domain_names; const names = host.domain_names;
setBusy(`cert:${host.id}`); setBusy(`cert:${host.id}`);
@@ -320,6 +348,21 @@ export const ProxiesBlock = ({ servers }: { servers: Server[] }) => {
)} )}
</p> </p>
{/*
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 && (
<Button variant="outline" size="sm" onClick={() => void upgrade(host)} disabled={busy !== null}>
{busy === `http2:${host.id}` ? (
<Loader2 className="size-3.5 animate-spin" />
) : (
<Gauge className="size-3.5" />
)}
{busy === `http2:${host.id}` ? 'Enabling…' : 'Enable HTTP/2'}
</Button>
)}
{!host.certificate_id && ( {!host.certificate_id && (
<Button variant="outline" size="sm" onClick={() => void secure(host)} disabled={busy !== null}> <Button variant="outline" size="sm" onClick={() => void secure(host)} disabled={busy !== null}>
{busy === `cert:${host.id}` ? ( {busy === `cert:${host.id}` ? (