This repository has been archived on 2026-04-27. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
website/public/error-nginx.html
T
2026-04-26 22:51:33 +09:00

431 lines
18 KiB
HTML

<!DOCTYPE html>
<html lang="ja">
<head>
<!--
$ curl https://nercone.dev/error-nginx.html
■ ■ ■■■■■ ■■■■ ■■■■ ■■■ ■ ■ ■■■■■
■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■ ■
■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■ ■
■ ■ ■ ■■■■ ■■■■ ■ ■ ■ ■ ■ ■ ■■■■
■ ■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■
■ ■■ ■ ■ ■ ■ ■ ■ ■ ■■ ■
■ ■ ■■■■■ ■ ■ ■■■■ ■■■ ■ ■ ■■■■■
このページは、リバースプロキシとして使用しているNginxが、バックエンドのWebサーバーへの接続に失敗した際に表示されます。
バックエンドのWebサーバーはUvicorn + FastAPIで動作しており、サブドメインのルーティングとTLSへの対応のためにNginxを間に噛ませています。
Webサーバーの再起動には約4秒かかります。システムごと再起動する場合、最大5分程度かかります。
もし5分以上アクセスできない場合、何かしらの問題が発生しているか、サーバーの環境を作り直している可能性があります。
どちらにせよ、すぐにはアクセスできないので、コーヒーか何かを飲んで待つか、Nerconeを叩き起こして修正させる必要があります。
さて、このメッセージを見ているということは、少なくともDevToolを知っている人...おそらく技術者でしょう?良ければネッ友になりませんか?
P.S. 暇つぶしにソースコードでもいかがですか? https://gitea.nercone.dev/nercone-dev/website/
-->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>nercone.dev への接続に失敗しました</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=BIZ+UDGothic&family=Noto+Sans+JP:wght@100..900&family=Noto+Sans+SC:wght@100..900&family=Noto+Sans+TC:wght@100..900&family=Noto+Sans+KR:wght@100..900&display=swap">
<style>
:root {
--color-black: #000000;
--color-white: #FFFFFF;
--color-dark-grey: #1A1A1A;
--color-dark-grey-alt: #272727;
--color-light-grey: #E0E0E0;
--color-light-grey-alt: #939393;
--color-red: #A03333;
--color-green: #00A050;
}
html {
color: var(--color-light-grey);
background-color: var(--color-dark-grey);
margin: 0;
padding: 0;
font-family: "Inter", "BIZ UDGothic", "Noto Sans JP", "Noto Sans TC", "Noto Sans SC", sans-serif;
font-size: 12pt;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 24px;
box-sizing: border-box;
}
.error-container {
display: flex;
flex-direction: column;
align-items: center;
gap: 80px;
width: 100%;
max-width: 1032px;
}
.diagram {
display: flex;
align-items: flex-start;
width: 100%;
justify-content: center;
}
.node {
width: 192px;
flex-shrink: 0;
display: flex;
flex-direction: column;
}
.node-body {
width: 192px;
height: 192px;
background-color: var(--color-dark-grey-alt);
border-radius: 32px 32px 0 0;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 48px;
box-sizing: border-box;
}
.node-icon {
width: 96px;
height: 96px;
color: var(--color-light-grey);
}
.node-icon svg {
display: block;
width: 100%;
height: 100%;
overflow: visible;
}
.node-label {
margin-top: 27px;
font-size: 12px;
line-height: 1;
color: var(--color-light-grey);
}
.node-status {
width: 192px;
height: 32px;
border-radius: 0 0 32px 32px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
color: var(--color-white);
background-color: var(--color-light-grey-alt);
transition: background-color 0.3s ease;
}
.node-status.status-normal { background-color: var(--color-green); }
.node-status.status-error { background-color: var(--color-red); }
.node-status.status-unknown { background-color: var(--color-light-grey-alt); }
.connector {
flex: 1;
height: 4px;
margin-top: 94px;
min-width: 60px;
background-repeat: repeat-x;
background-position: center;
background-size: 32px 4px;
transition: background 0.3s ease;
}
.connector.line-solid-green {
background-color: var(--color-green);
background-image: none;
}
.connector.line-solid-red {
background-color: var(--color-red);
background-image: none;
}
.connector.line-solid-grey {
background-color: var(--color-light-grey-alt);
background-image: none;
}
.connector.line-dashed-green {
background-color: transparent;
background-image: repeating-linear-gradient(to right,
var(--color-green) 0, var(--color-green) 16px,
transparent 16px, transparent 32px);
}
.connector.line-dashed-red {
background-color: transparent;
background-image: repeating-linear-gradient(to right,
var(--color-red) 0, var(--color-red) 16px,
transparent 16px, transparent 32px);
}
.connector.line-dashed-grey {
background-color: transparent;
background-image: repeating-linear-gradient(to right,
var(--color-light-grey-alt) 0, var(--color-light-grey-alt) 16px,
transparent 16px, transparent 32px);
}
.error-text {
text-align: center;
}
.error-title {
font-size: 32px;
color: var(--color-light-grey);
margin: 0 0 16px 0;
font-weight: 400;
line-height: 1.3;
}
.error-message {
font-size: 20px;
color: var(--color-light-grey-alt);
margin: 0;
font-weight: 400;
line-height: 1.4;
}
@media (max-width: 880px) {
.error-container { gap: 56px; }
.error-title { font-size: 26px; line-height: 1.35; }
.error-message { font-size: 16px; }
.node, .node-body, .node-status { width: 144px; }
.node-body {
height: 144px;
padding-top: 36px;
border-radius: 24px 24px 0 0;
}
.node-icon { width: 72px; height: 72px; }
.node-status {
height: 28px;
font-size: 14px;
border-radius: 0 0 24px 24px;
}
.node-label { margin-top: 20px; font-size: 11px; }
.connector {
margin-top: 70px;
min-width: 24px;
background-size: 24px 4px;
}
.connector.line-dashed-red,
.connector.line-dashed-green,
.connector.line-dashed-grey {
background-image: repeating-linear-gradient(to right,
currentColor 0, currentColor 12px,
transparent 12px, transparent 24px);
}
.connector.line-dashed-red { color: var(--color-red); }
.connector.line-dashed-green { color: var(--color-green); }
.connector.line-dashed-grey { color: var(--color-light-grey-alt); }
}
@media (max-width: 540px) {
body { padding: 16px; }
.error-container { gap: 32px; }
.error-title { font-size: 20px; line-height: 1.4; }
.error-message { font-size: 13px; line-height: 1.5; }
.node, .node-body, .node-status { width: 96px; }
.node-body {
height: 96px;
padding-top: 24px;
border-radius: 16px 16px 0 0;
}
.node-icon { width: 48px; height: 48px; }
.node-status {
height: 24px;
font-size: 11px;
border-radius: 0 0 16px 16px;
}
.node-label {
margin-top: 8px;
font-size: 9px;
text-align: center;
padding: 0 2px;
}
.connector {
margin-top: 46px;
min-width: 12px;
background-size: 16px 4px;
}
.connector.line-dashed-red,
.connector.line-dashed-green,
.connector.line-dashed-grey {
background-image: repeating-linear-gradient(to right,
currentColor 0, currentColor 8px,
transparent 8px, transparent 16px);
}
}
@media (max-width: 360px) {
body { padding: 8px; }
.node, .node-body, .node-status { width: 80px; }
.node-body {
height: 80px;
padding-top: 18px;
}
.node-icon { width: 44px; height: 44px; }
.node-status { height: 22px; font-size: 10px; }
.node-label { margin-top: 6px; font-size: 8px; }
.connector { margin-top: 38px; min-width: 8px; }
}
</style>
</head>
<body>
<div class="error-container">
<div class="diagram">
<!-- User Agent -->
<div class="node">
<div class="node-body">
<div class="node-icon" aria-hidden="true">
<svg viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2">
<rect x="1" y="1" width="94" height="94" rx="8" ry="8"/>
<line x1="1" y1="32" x2="95" y2="32"/>
</svg>
</div>
<div class="node-label">ユーザーエージェント</div>
</div>
<div class="node-status" id="status-1">正常</div>
</div>
<div class="connector" id="connector-1"></div>
<div class="node">
<div class="node-body">
<div class="node-icon" aria-hidden="true">
<svg viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2">
<path d="M41.21,16.39 l-24.83,24.83"/>
<path d="M79.61,54.79 l-24.83,24.83"/>
<path d="M19.2,48 h57.6"/>
<circle cx="48" cy="9.6" r="9.6"/>
<circle cx="86.4" cy="48" r="9.6"/>
<circle cx="48" cy="86.4" r="9.6"/>
<circle cx="9.6" cy="48" r="9.6"/>
</svg>
</div>
<div class="node-label">リバースプロキシ</div>
</div>
<div class="node-status" id="status-2">正常</div>
</div>
<div class="connector" id="connector-2"></div>
<div class="node">
<div class="node-body">
<div class="node-icon" aria-hidden="true">
<svg viewBox="0 0 96 96" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square">
<path d="M24,0 h62.4 c5.3,0 9.6,4.3 9.6,9.6 v19.2 c0,5.3 -4.3,9.6 -9.6,9.6 h-24"/>
<path d="M38.4,38.4 l-36,-36 c-2.4,-2.4 -2.4,0 -2.4,12 v14.4 c0,5.3 4.3,9.6 9.6,9.6 h28.8 Z"/>
<path d="M96,72 v-4.8 c0,-5.3 -4.3,-9.6 -9.6,-9.6 h-4.8"/>
<path d="M9.6,57.6 c-5.3,0 -9.6,4.3 -9.6,9.6 v19.2 c0,5.3 4.3,9.6 9.6,9.6 h79.2 l4.8,-2.4 2.4,2.4 -38.4,-38.4 h-48 Z"/>
<path d="M19.2,76.8 h.05"/>
<path d="M0,0 l96,96"/>
</svg>
</div>
<div class="node-label" id="domain-label">nercone.dev</div>
</div>
<div class="node-status" id="status-3">応答なし</div>
</div>
</div>
<div class="error-text">
<h1 class="error-title"><span id="domain-text">nercone.dev</span> への接続に失敗しました</h1>
<p class="error-message">もう少し待ってから接続を再試行してください。HTTPレスポンスステータスコードは<span id="status-code">502</span>です。</p>
</div>
</div>
<script>
(function () {
'use strict';
var STAGES = {
'user-agent': { statusElId: 'status-1', incomingConnectorId: null },
'reverse-proxy': { statusElId: 'status-2', incomingConnectorId: 'connector-1' },
'origin': { statusElId: 'status-3', incomingConnectorId: 'connector-2' }
};
var STATUS_DEFS = {
normal: { className: 'status-normal', label: '正常', line: 'line-solid-green' },
error: { className: 'status-error', label: '応答なし', line: 'line-dashed-red' },
unknown: { className: 'status-unknown', label: '不明', line: 'line-dashed-grey' }
};
function setStageStatus(stage, status, label) {
var stageConfig = STAGES[stage];
var statusDef = STATUS_DEFS[status];
if (!stageConfig || !statusDef) return;
var statusEl = document.getElementById(stageConfig.statusElId);
if (statusEl) {
statusEl.className = 'node-status ' + statusDef.className;
statusEl.textContent = label != null ? label : statusDef.label;
}
if (stageConfig.incomingConnectorId) {
var connectorEl = document.getElementById(stageConfig.incomingConnectorId);
if (connectorEl) {
connectorEl.className = 'connector ' + statusDef.line;
}
}
}
function setStatusCode(code) {
var el = document.getElementById('status-code');
if (el) el.textContent = String(code);
}
function setDomain(domain) {
var labelEl = document.getElementById('domain-label');
var textEl = document.getElementById('domain-text');
if (labelEl) labelEl.textContent = domain;
if (textEl) textEl.textContent = domain;
}
window.ConnectionError = {
setStageStatus: setStageStatus,
setStatusCode: setStatusCode,
setDomain: setDomain
};
setStageStatus('user-agent', 'normal');
setStageStatus('reverse-proxy', 'normal');
setStageStatus('origin', 'error');
try {
var host = window.location.hostname;
if (host) setDomain(host);
} catch (e) {}
try {
var url = window.location.href;
var controller = (typeof AbortController !== 'undefined') ? new AbortController() : null;
if (controller) {
setTimeout(function () { try { controller.abort(); } catch (e) {} }, 5000);
}
fetch(url, {
method: 'HEAD',
cache: 'no-store',
credentials: 'same-origin',
redirect: 'manual',
signal: controller ? controller.signal : undefined
}).then(function (response) {
if (response && typeof response.status === 'number' && response.status > 0) {
setStatusCode(response.status);
}
}).catch(function () {});
} catch (e) {}
})();
</script>
</body>
</html>