mirror of
https://github.com/kemalai/FreeCrawl-SEO-Tool.git
synced 2026-10-18 00:39:38 +02:00
fix: bot protection, live-crawl false alarms, table scroll gaps, i18n
This commit is contained in:
1 parent
3d99a340fc
commit
f155c4554c
41 files changed
+4587
-526
No files matched your search
@@ -333,6 +333,7 @@ async function main(): Promise<void> {
|
||||
const overview = db.getOverviewCounts();
|
||||
const issues = overview.issues;
|
||||
const soft404Probe = overview.site?.soft404Probe ?? null;
|
||||
const botBlocked = overview.site?.botBlocked ?? null;
|
||||
// A crawl that fetched nothing (unreachable seed, everything filtered) is a
|
||||
// failure for CI purposes — a successful spider always fetches ≥1 page.
|
||||
const hasErrors =
|
||||
@@ -353,7 +354,7 @@ async function main(): Promise<void> {
|
||||
dbPath,
|
||||
summary,
|
||||
issues,
|
||||
site: { soft404Probe },
|
||||
site: { soft404Probe, botBlocked },
|
||||
exportedTo,
|
||||
rowsExported: exportedTo ? rowsWritten : null,
|
||||
},
|
||||
@@ -368,6 +369,13 @@ async function main(): Promise<void> {
|
||||
if (exportedTo) {
|
||||
console.log(`Wrote ${rowsWritten} rows -> ${exportedTo}`);
|
||||
}
|
||||
if (botBlocked) {
|
||||
console.log(
|
||||
`Warning: bot protection blocked ${botBlocked.count} URL(s)` +
|
||||
(botBlocked.sample ? ` (${botBlocked.sample})` : '') +
|
||||
' — not crawled; lower --rps or allowlist the crawler and re-run',
|
||||
);
|
||||
}
|
||||
if (soft404Probe) {
|
||||
console.log(
|
||||
`Warning: missing pages return ${soft404Probe.status} (soft 404) — ${soft404Probe.url}` +
|
||||
|
||||
@@ -8088,6 +8088,7 @@ function registerIpc(): void {
|
||||
mobileUsability,
|
||||
a11y: res.a11y ?? null,
|
||||
spaRoutes: res.spaRoutes,
|
||||
navigatedTo: res.navigatedTo ?? null,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1830,8 +1830,8 @@ function HttpHeadersView({
|
||||
// 5xx-under-load) into a plain-English explanation enriched with
|
||||
// signals read off the response headers (Retry-After, cf-ray, …).
|
||||
const diag = useMemo(
|
||||
() => diagnoseStatus(row.statusCode, row.statusText, headers, row.indexability),
|
||||
[row.statusCode, row.statusText, headers, row.indexability],
|
||||
() => diagnoseStatus(row.statusCode, row.statusText, headers, row.indexability, t),
|
||||
[row.statusCode, row.statusText, headers, row.indexability, t],
|
||||
);
|
||||
const rows = side === 'response' ? headers : requestHeaders;
|
||||
// Only surface the diagnosis banner when there's something worth
|
||||
|
||||
@@ -85,6 +85,7 @@ const TAB_CHILDREN: Partial<Record<TabKey, { subdir: string; children: SubCatego
|
||||
children: [
|
||||
{ label: 'All', category: 'all', filename: 'all' },
|
||||
{ label: 'Blocked by Robots', category: 'status:blocked-robots', filename: 'blocked-robots' },
|
||||
{ label: 'Blocked by Bot Protection', category: 'status:bot-blocked', filename: 'blocked-bot-protection' },
|
||||
{ label: 'No Response', category: 'status:no-response', filename: 'no-response' },
|
||||
{ label: 'Success (2xx)', category: 'status:2xx', filename: '2xx-success' },
|
||||
{ label: 'Redirection (3xx)', category: 'status:3xx', filename: '3xx-redirect' },
|
||||
|
||||
@@ -170,6 +170,7 @@ export function OverviewSidebar() {
|
||||
const tree = useMemo(() => buildTree(overview, disabledSet), [overview, disabledSet]);
|
||||
const totalForPercent = overview?.summary.totalInternalUrls ?? 0;
|
||||
const soft404Probe = overview?.site?.soft404Probe ?? null;
|
||||
const botBlocked = overview?.site?.botBlocked ?? null;
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col bg-surface-900">
|
||||
@@ -182,6 +183,30 @@ export function OverviewSidebar() {
|
||||
<span>{t('sidebar.percentOfTotal', { defaultValue: '% of Total' })}</span>
|
||||
</div>
|
||||
</div>
|
||||
{botBlocked && (
|
||||
// The crawl itself is incomplete — said up front, because every
|
||||
// count below silently lacks these pages otherwise.
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-start gap-1.5 border-b border-amber-700/40 bg-amber-900/20 px-2 py-1.5 text-left text-[11px] text-amber-200 hover:bg-amber-900/30"
|
||||
title={t('sidebar.siteBotBlockedHint', {
|
||||
defaultValue:
|
||||
"The site's bot protection answered with a challenge instead of these pages. They were not crawled and are left out of the broken-link and sitemap checks. Lower the crawl speed or allowlist the crawler's user agent, then re-crawl them.",
|
||||
})}
|
||||
onClick={() => navigateToCategory('status:bot-blocked')}
|
||||
>
|
||||
<AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="font-medium">
|
||||
{t('sidebar.siteBotBlocked', { defaultValue: 'Bot protection blocked part of the crawl' })}
|
||||
</div>
|
||||
{botBlocked.sample && (
|
||||
<div className="break-all font-mono text-[10px] text-amber-300/80">{botBlocked.sample}</div>
|
||||
)}
|
||||
</div>
|
||||
<span className="shrink-0 font-mono tabular-nums">{botBlocked.count.toLocaleString()}</span>
|
||||
</button>
|
||||
)}
|
||||
{soft404Probe && (
|
||||
// A finding about the whole site, not a URL list — so a notice
|
||||
// rather than a tree node with a count and a filter behind it.
|
||||
@@ -385,6 +410,13 @@ function buildTree(o: OverviewCounts | null, disabled: ReadonlySet<string>): Nod
|
||||
category: 'status:blocked-robots',
|
||||
percentBase: allUrls,
|
||||
},
|
||||
{
|
||||
key: 'rc-bot-blocked',
|
||||
label: 'Blocked by Bot Protection',
|
||||
count: o.responseCodes.botBlocked,
|
||||
category: 'status:bot-blocked',
|
||||
percentBase: allUrls,
|
||||
},
|
||||
{
|
||||
key: 'rc-none',
|
||||
label: 'No Response',
|
||||
|
||||
@@ -248,8 +248,11 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
if (!cancelled)
|
||||
setRows(
|
||||
r.map((x: StatusCodeHistogramRow) => ({
|
||||
key: x.status === null ? 'No response' : String(x.status),
|
||||
badge: statusBadge(x.status),
|
||||
key:
|
||||
x.status === null
|
||||
? t('reports.noResponse', { defaultValue: 'No response' })
|
||||
: String(x.status),
|
||||
badge: statusBadge(x.status, t),
|
||||
count: x.count,
|
||||
})),
|
||||
);
|
||||
@@ -258,8 +261,11 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
if (!cancelled)
|
||||
setRows(
|
||||
r.map((x: IndexabilityDistributionRow) => ({
|
||||
key: indexabilityLabel(x.indexability),
|
||||
badge: x.indexability === 'indexable' ? 'OK' : 'NON',
|
||||
key: indexabilityLabel(x.indexability, t),
|
||||
badge:
|
||||
x.indexability === 'indexable'
|
||||
? t('reports.badge.ok', { defaultValue: 'OK' })
|
||||
: t('reports.badge.non', { defaultValue: 'NON' }),
|
||||
count: x.count,
|
||||
})),
|
||||
);
|
||||
@@ -268,7 +274,7 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
if (!cancelled)
|
||||
setRows(
|
||||
r.map((x: ContentKindDistributionRow) => ({
|
||||
key: contentKindLabel(x.contentKind),
|
||||
key: contentKindLabel(x.contentKind, t),
|
||||
count: x.count,
|
||||
})),
|
||||
);
|
||||
@@ -281,8 +287,8 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
if (!cancelled)
|
||||
setRows(
|
||||
r.map((x: ResponseTimeHistogramRow) => ({
|
||||
key: x.label,
|
||||
badge: rtBadge(x.label),
|
||||
key: bucketLabel(x.label, t),
|
||||
badge: rtBadge(x.label, t),
|
||||
count: x.count,
|
||||
})),
|
||||
);
|
||||
@@ -294,16 +300,23 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
key: x.domain,
|
||||
badge:
|
||||
x.errorRatePercent === 0
|
||||
? 'OK'
|
||||
? t('reports.badge.ok', { defaultValue: 'OK' })
|
||||
: x.errorRatePercent < 10
|
||||
? 'WARN'
|
||||
: 'BAD',
|
||||
? t('reports.badge.warn', { defaultValue: 'WARN' })
|
||||
: t('reports.badge.bad', { defaultValue: 'BAD' }),
|
||||
// Bar = error count so the worst domains spike visually;
|
||||
// value label shows the breakdown.
|
||||
count: x.errorCount,
|
||||
valueLabel: `${x.successCount}/${x.totalUrls} OK · ${x.errorRatePercent}% err${
|
||||
x.avgResponseTimeMs !== null ? ` · ${x.avgResponseTimeMs}ms avg` : ''
|
||||
}`,
|
||||
valueLabel:
|
||||
t('reports.domainHealth', {
|
||||
defaultValue: '{{ok}}/{{total}} OK · {{rate}}% errors',
|
||||
ok: x.successCount.toLocaleString(),
|
||||
total: x.totalUrls.toLocaleString(),
|
||||
rate: x.errorRatePercent,
|
||||
}) +
|
||||
(x.avgResponseTimeMs !== null
|
||||
? ` · ${t('reports.avgMs', { defaultValue: '{{ms}} ms avg', ms: x.avgResponseTimeMs.toLocaleString() })}`
|
||||
: ''),
|
||||
})),
|
||||
);
|
||||
} else if (kind === 'analytics-coverage') {
|
||||
@@ -313,9 +326,16 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
r.map((x: AnalyticsCoverageRow) => ({
|
||||
key: x.name,
|
||||
count: x.pageCount,
|
||||
valueLabel: `${x.pageCount.toLocaleString()} pages · ${x.distinctIds} distinct ID${
|
||||
x.distinctIds === 1 ? '' : 's'
|
||||
}${x.sampleIds.length > 0 ? ` · ${x.sampleIds.join(', ')}` : ''}`,
|
||||
valueLabel: [
|
||||
pageCountLabel(x.pageCount, t),
|
||||
t('reports.distinctIds', {
|
||||
defaultValue_one: '{{count}} distinct ID',
|
||||
defaultValue_other: '{{count}} distinct IDs',
|
||||
defaultValue: '{{count}} distinct IDs',
|
||||
count: x.distinctIds,
|
||||
}),
|
||||
...(x.sampleIds.length > 0 ? [x.sampleIds.join(', ')] : []),
|
||||
].join(' · '),
|
||||
})),
|
||||
);
|
||||
} else if (kind === 'link-positions') {
|
||||
@@ -323,7 +343,7 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
if (!cancelled)
|
||||
setRows(
|
||||
r.map((x: LinkPositionRow) => ({
|
||||
key: x.position,
|
||||
key: linkPositionLabel(x.position, t),
|
||||
count: x.count,
|
||||
})),
|
||||
);
|
||||
@@ -334,15 +354,19 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
r.map((x: ImageWeightRow) => ({
|
||||
key: x.url,
|
||||
count: x.imageBytes,
|
||||
valueLabel: `${VALUE_FORMAT['image-weight'](x.imageBytes)} · ${
|
||||
x.imageCount
|
||||
} image${x.imageCount === 1 ? '' : 's'}`,
|
||||
valueLabel: `${VALUE_FORMAT['image-weight'](x.imageBytes)} · ${t('reports.imageCount', {
|
||||
defaultValue_one: '{{formatted}} image',
|
||||
defaultValue_other: '{{formatted}} images',
|
||||
defaultValue: '{{formatted}} images',
|
||||
count: x.imageCount,
|
||||
formatted: x.imageCount.toLocaleString(),
|
||||
})}`,
|
||||
})),
|
||||
);
|
||||
} else if (kind === 'inlinks-histogram') {
|
||||
const r = await window.freecrawl.reportsInlinksHistogram();
|
||||
if (!cancelled)
|
||||
setRows(r.map((x: BucketHistogramRow) => ({ key: x.label, count: x.count })));
|
||||
setRows(r.map((x: BucketHistogramRow) => ({ key: bucketLabel(x.label, t), count: x.count })));
|
||||
} else if (kind === 'word-count-histogram') {
|
||||
const r = await window.freecrawl.reportsWordCountHistogram();
|
||||
if (!cancelled)
|
||||
@@ -364,9 +388,10 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
// directories visually spike; secondary value shows page
|
||||
// count so the user can spot single-page outliers.
|
||||
count: x.avgWordCount,
|
||||
valueLabel: `${x.avgWordCount.toLocaleString()} avg · ${x.pageCount.toLocaleString()} page${
|
||||
x.pageCount === 1 ? '' : 's'
|
||||
}`,
|
||||
valueLabel: `${t('reports.avgWords', {
|
||||
defaultValue: '{{formatted}} avg',
|
||||
formatted: x.avgWordCount.toLocaleString(),
|
||||
})} · ${pageCountLabel(x.pageCount, t)}`,
|
||||
})),
|
||||
);
|
||||
} else if (kind === 'sitemap-orphans') {
|
||||
@@ -381,8 +406,10 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
// sitemap go to the secondary value column.
|
||||
count: 1,
|
||||
valueLabel: [
|
||||
x.lastmod ? `lastmod ${x.lastmod}` : null,
|
||||
x.sourceSitemap ? `from ${x.sourceSitemap}` : null,
|
||||
x.lastmod ? t('reports.lastmod', { defaultValue: 'lastmod {{date}}', date: x.lastmod }) : null,
|
||||
x.sourceSitemap
|
||||
? t('reports.fromSitemap', { defaultValue: 'from {{sitemap}}', sitemap: x.sourceSitemap })
|
||||
: null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' · ') || '—',
|
||||
@@ -467,9 +494,13 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
// so the user can spot "high count, narrow coverage" =
|
||||
// a single page repeating a word.
|
||||
count: x.count,
|
||||
valueLabel: `${x.count.toLocaleString()} hits · ${x.pages.toLocaleString()} page${
|
||||
x.pages === 1 ? '' : 's'
|
||||
}`,
|
||||
valueLabel: `${t('reports.hits', {
|
||||
defaultValue_one: '{{formatted}} hit',
|
||||
defaultValue_other: '{{formatted}} hits',
|
||||
defaultValue: '{{formatted}} hits',
|
||||
count: x.count,
|
||||
formatted: x.count.toLocaleString(),
|
||||
})} · ${pageCountLabel(x.pages, t)}`,
|
||||
})),
|
||||
);
|
||||
} else if (kind === 'mobile-parity') {
|
||||
@@ -699,8 +730,10 @@ export function ReportsDialog({ open, onClose }: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
function statusBadge(status: number | null): string {
|
||||
if (status === null) return 'NET';
|
||||
type Translate = ReturnType<typeof useTranslation>['t'];
|
||||
|
||||
function statusBadge(status: number | null, t: Translate): string {
|
||||
if (status === null) return t('reports.badge.net', { defaultValue: 'NET' });
|
||||
if (status >= 200 && status < 300) return '2xx';
|
||||
if (status >= 300 && status < 400) return '3xx';
|
||||
if (status >= 400 && status < 500) return '4xx';
|
||||
@@ -713,11 +746,48 @@ function statusBadge(status: number | null): string {
|
||||
* the histogram for "where am I losing performance?" without re-reading
|
||||
* the bucket boundaries.
|
||||
*/
|
||||
function rtBadge(label: string): string {
|
||||
if (label === 'No response') return 'ERR';
|
||||
if (label === '< 100ms' || label === '100–500ms') return 'OK';
|
||||
if (label === '500ms–1s') return 'WARN';
|
||||
return 'SLOW';
|
||||
function rtBadge(label: string, t: Translate): string {
|
||||
if (label === 'No response') return t('reports.badge.err', { defaultValue: 'ERR' });
|
||||
if (label === '< 100ms' || label === '100–500ms') return t('reports.badge.ok', { defaultValue: 'OK' });
|
||||
if (label === '500ms–1s') return t('reports.badge.warn', { defaultValue: 'WARN' });
|
||||
return t('reports.badge.slow', { defaultValue: 'SLOW' });
|
||||
}
|
||||
|
||||
/** Histogram bucket labels come from the database; only two carry words. */
|
||||
function bucketLabel(label: string, t: Translate): string {
|
||||
if (label === 'No response') return t('reports.noResponse', { defaultValue: 'No response' });
|
||||
if (label === '0 (orphan)') return t('reports.bucketOrphan', { defaultValue: '0 (orphan)' });
|
||||
return label;
|
||||
}
|
||||
|
||||
function pageCountLabel(n: number, t: Translate): string {
|
||||
return t('reports.pageCount', {
|
||||
defaultValue_one: '{{formatted}} page',
|
||||
defaultValue_other: '{{formatted}} pages',
|
||||
defaultValue: '{{formatted}} pages',
|
||||
count: n,
|
||||
formatted: n.toLocaleString(),
|
||||
});
|
||||
}
|
||||
|
||||
/** The page region a link sits in (`links.link_position`). */
|
||||
function linkPositionLabel(value: string, t: Translate): string {
|
||||
switch (value) {
|
||||
case 'navigation':
|
||||
return t('reports.position.navigation', { defaultValue: 'Navigation' });
|
||||
case 'header':
|
||||
return t('reports.position.header', { defaultValue: 'Header' });
|
||||
case 'content':
|
||||
return t('reports.position.content', { defaultValue: 'Content' });
|
||||
case 'sidebar':
|
||||
return t('reports.position.sidebar', { defaultValue: 'Sidebar' });
|
||||
case 'footer':
|
||||
return t('reports.position.footer', { defaultValue: 'Footer' });
|
||||
case 'aside':
|
||||
return t('reports.position.aside', { defaultValue: 'Aside' });
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -725,22 +795,24 @@ function rtBadge(label: string): string {
|
||||
* machine-friendly token (`non-indexable:robots-blocked`) out of the UI so
|
||||
* the dropdown reads naturally — Title Case + human-readable suffix.
|
||||
*/
|
||||
function indexabilityLabel(value: string): string {
|
||||
function indexabilityLabel(value: string, t: Translate): string {
|
||||
switch (value) {
|
||||
case 'indexable':
|
||||
return 'Indexable';
|
||||
return t('reports.indexability.indexable', { defaultValue: 'Indexable' });
|
||||
case 'non-indexable:noindex':
|
||||
return 'Non-indexable — noindex';
|
||||
return t('reports.indexability.noindex', { defaultValue: 'Non-indexable — noindex' });
|
||||
case 'non-indexable:canonical':
|
||||
return 'Non-indexable — canonicalised';
|
||||
return t('reports.indexability.canonical', { defaultValue: 'Non-indexable — canonicalised' });
|
||||
case 'non-indexable:robots-blocked':
|
||||
return 'Non-indexable — robots-blocked';
|
||||
return t('reports.indexability.robotsBlocked', { defaultValue: 'Non-indexable — blocked by robots.txt' });
|
||||
case 'non-indexable:redirect':
|
||||
return 'Non-indexable — redirect';
|
||||
return t('reports.indexability.redirect', { defaultValue: 'Non-indexable — redirect' });
|
||||
case 'non-indexable:client-error':
|
||||
return 'Non-indexable — 4xx';
|
||||
return t('reports.indexability.clientError', { defaultValue: 'Non-indexable — 4xx' });
|
||||
case 'non-indexable:server-error':
|
||||
return 'Non-indexable — 5xx';
|
||||
return t('reports.indexability.serverError', { defaultValue: 'Non-indexable — 5xx' });
|
||||
case 'unknown:bot-blocked':
|
||||
return t('reports.indexability.botBlocked', { defaultValue: 'Unknown — blocked by bot protection' });
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
@@ -751,7 +823,7 @@ function indexabilityLabel(value: string): string {
|
||||
* font / pdf / other). Upper-cases acronyms (HTML/CSS/JS/PDF) so the chart
|
||||
* matches the way users talk about file types.
|
||||
*/
|
||||
function contentKindLabel(value: string): string {
|
||||
function contentKindLabel(value: string, t: Translate): string {
|
||||
switch (value) {
|
||||
case 'html':
|
||||
return 'HTML';
|
||||
@@ -760,13 +832,13 @@ function contentKindLabel(value: string): string {
|
||||
case 'js':
|
||||
return 'JavaScript';
|
||||
case 'image':
|
||||
return 'Image';
|
||||
return t('reports.kind.image', { defaultValue: 'Image' });
|
||||
case 'font':
|
||||
return 'Font';
|
||||
return t('reports.kind.font', { defaultValue: 'Font' });
|
||||
case 'pdf':
|
||||
return 'PDF';
|
||||
case 'other':
|
||||
return 'Other';
|
||||
return t('reports.kind.other', { defaultValue: 'Other' });
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -124,6 +124,9 @@ export const CHANGED_MARKER_TITLE = 'Changed since the previous crawl';
|
||||
/** Tooltip for the Robots status badge. */
|
||||
export const ROBOTS_STATUS_TITLE = 'Not requested — disallowed by robots.txt';
|
||||
|
||||
/** Tooltip for the Blocked status badge (followed by what answered). */
|
||||
export const BOT_BLOCKED_STATUS_TITLE = 'Not crawled — bot protection answered instead of the page';
|
||||
|
||||
/** How one cell is painted. */
|
||||
export type CellPaint =
|
||||
/** An absent value — the muted "—". */
|
||||
@@ -172,6 +175,17 @@ export function cellPaint(row: CrawlUrlRow, spec: ColumnSpec, lang: string): Cel
|
||||
title: translateLabel(ROBOTS_STATUS_TITLE, lang),
|
||||
};
|
||||
}
|
||||
// Bot protection (Cloudflare challenge, WAF) or a rate limit answered
|
||||
// instead of the page — not the page's status, and not a failure of
|
||||
// the connection either. The hover names what answered.
|
||||
if ((code === null || code === undefined) && row.indexability === 'unknown:bot-blocked') {
|
||||
return {
|
||||
kind: 'badge',
|
||||
text: translateLabel('Blocked', lang),
|
||||
...STATUS_BADGES.robots,
|
||||
title: `${translateLabel(BOT_BLOCKED_STATUS_TITLE, lang)}: ${row.statusText ?? ''}`,
|
||||
};
|
||||
}
|
||||
// No HTTP status (DNS/TLS/connect/timeout failure): a bare "—" hides
|
||||
// the reason the crawler already captured. Show a compact token (DNS /
|
||||
// TLS / Timeout / …) with the full error text on hover.
|
||||
@@ -190,8 +204,9 @@ export function cellPaint(row: CrawlUrlRow, spec: ColumnSpec, lang: string): Cel
|
||||
const v = row.indexability;
|
||||
return {
|
||||
kind: 'text',
|
||||
text: translateLabel(v === 'indexable' ? 'Indexable' : 'Non-Indexable', lang),
|
||||
color: v === 'indexable' ? GRID_COLORS.good : GRID_COLORS.warn,
|
||||
text: translateLabel(indexabilityHeadline(v), lang),
|
||||
color:
|
||||
v === 'indexable' ? GRID_COLORS.good : v === 'unknown:bot-blocked' ? GRID_COLORS.rowNum : GRID_COLORS.warn,
|
||||
mono: false,
|
||||
title: v,
|
||||
marker: false,
|
||||
@@ -269,12 +284,13 @@ export function cellText(row: CrawlUrlRow, spec: ColumnSpec, lang: string): stri
|
||||
if (row.indexability === 'non-indexable:robots-blocked') {
|
||||
return translateLabel('Robots', lang);
|
||||
}
|
||||
if (row.indexability === 'unknown:bot-blocked') return translateLabel('Blocked', lang);
|
||||
const label = shortFailureLabel(row.statusText);
|
||||
return label === '—' ? '' : label;
|
||||
}
|
||||
|
||||
if (spec.kind === 'indexability') {
|
||||
return translateLabel(row.indexability === 'indexable' ? 'Indexable' : 'Non-Indexable', lang);
|
||||
return translateLabel(indexabilityHeadline(row.indexability), lang);
|
||||
}
|
||||
|
||||
if (spec.kind === 'indexability-status') {
|
||||
@@ -364,6 +380,14 @@ export function formatExtractionCell(raw: string): string {
|
||||
return entries.map(([name, v]) => `${name}: ${fmt(v)}`).join(' · ');
|
||||
}
|
||||
|
||||
/** The Indexability column's one-word verdict. */
|
||||
function indexabilityHeadline(v: CrawlUrlRow['indexability']): string {
|
||||
if (v === 'indexable') return 'Indexable';
|
||||
// Not crawled, so not known — search engines are usually let through.
|
||||
if (v === 'unknown:bot-blocked') return 'Unknown';
|
||||
return 'Non-Indexable';
|
||||
}
|
||||
|
||||
export function indexabilityStatusLabel(v: CrawlUrlRow['indexability']): string {
|
||||
switch (v) {
|
||||
case 'indexable':
|
||||
@@ -380,6 +404,8 @@ export function indexabilityStatusLabel(v: CrawlUrlRow['indexability']): string
|
||||
return 'Client Error';
|
||||
case 'non-indexable:server-error':
|
||||
return 'Server Error';
|
||||
case 'unknown:bot-blocked':
|
||||
return 'Blocked by bot protection';
|
||||
default:
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,13 @@ const TICK_PAD_ROWS = 40;
|
||||
// copy per tick onto the reader pool until nothing else could get
|
||||
// through. The user's explicit Retry bypasses the hold.
|
||||
const FAILURE_BACKOFF_MS = 5000;
|
||||
// A cached chunk can be short: fetched while the crawl was still filling
|
||||
// it (the look-ahead chunk is requested the moment the table opens, when
|
||||
// it holds nothing yet). Rows on screen that such a chunk lacks are
|
||||
// re-fetched at once; a short look-ahead chunk is topped up at most this
|
||||
// often, so a live crawl doesn't turn it into a constant re-query.
|
||||
const VISIBLE_GAP_REFETCH_MS = 250;
|
||||
const LOOKAHEAD_REFETCH_MS = 1000;
|
||||
|
||||
export interface LazyRowsOpts {
|
||||
category: UrlCategory;
|
||||
@@ -97,7 +104,14 @@ function describeError(err: unknown): string {
|
||||
* count grows. The user sees a continuously-flowing, sorted table.
|
||||
*/
|
||||
export function useLazyUrlRows(opts: LazyRowsOpts): LazyRowsState {
|
||||
const [total, setTotal] = useState(0);
|
||||
const [total, setTotalState] = useState(0);
|
||||
// `ensureRange` runs outside React's render cycle and needs the latest
|
||||
// total to tell a short chunk from the end of the table.
|
||||
const totalRef = useRef(0);
|
||||
const setTotal = useCallback((t: number): void => {
|
||||
totalRef.current = t;
|
||||
setTotalState(t);
|
||||
}, []);
|
||||
const [version, setVersion] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -111,6 +125,8 @@ export function useLazyUrlRows(opts: LazyRowsOpts): LazyRowsState {
|
||||
const chunks = useRef(new Map<number, CrawlUrlRow[]>());
|
||||
const chunkOrder = useRef<number[]>([]);
|
||||
const fetching = useRef(new Set<number>());
|
||||
/** When each chunk was last requested — paces re-fetches of short chunks. */
|
||||
const fetchedAt = useRef(new Map<number, number>());
|
||||
const activeRange = useRef<{ first: number; last: number }>({ first: 0, last: 0 });
|
||||
/** Visible row-index span, as the virtualizer last reported it. */
|
||||
const activeRows = useRef<{ start: number; end: number }>({ start: 0, end: 0 });
|
||||
@@ -195,6 +211,7 @@ export function useLazyUrlRows(opts: LazyRowsOpts): LazyRowsState {
|
||||
chunks.current.clear();
|
||||
chunkOrder.current = [];
|
||||
fetching.current.clear();
|
||||
fetchedAt.current.clear();
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
failedAt.current = 0;
|
||||
@@ -335,12 +352,16 @@ export function useLazyUrlRows(opts: LazyRowsOpts): LazyRowsState {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [keyStr]);
|
||||
|
||||
/** Fetch a chunk that isn't cached — or, with `minGapMs`, re-fetch a
|
||||
* cached one that is short, at most once per `minGapMs`. */
|
||||
const fetchChunk = useCallback(
|
||||
async (chunkIdx: number) => {
|
||||
if (chunks.current.has(chunkIdx)) return;
|
||||
async (chunkIdx: number, minGapMs?: number) => {
|
||||
if (minGapMs === undefined && chunks.current.has(chunkIdx)) return;
|
||||
if (minGapMs !== undefined && Date.now() - (fetchedAt.current.get(chunkIdx) ?? 0) < minGapMs) return;
|
||||
if (fetching.current.has(chunkIdx)) return;
|
||||
if (inBackoff()) return;
|
||||
fetching.current.add(chunkIdx);
|
||||
fetchedAt.current.set(chunkIdx, Date.now());
|
||||
const token = resetToken.current;
|
||||
try {
|
||||
const { rows, total: t } = await queryChunk(chunkIdx);
|
||||
@@ -358,7 +379,7 @@ export function useLazyUrlRows(opts: LazyRowsOpts): LazyRowsState {
|
||||
fetching.current.delete(chunkIdx);
|
||||
}
|
||||
},
|
||||
[queryChunk, storeChunk],
|
||||
[queryChunk, storeChunk, setTotal],
|
||||
);
|
||||
|
||||
const ensureRange = useCallback(
|
||||
@@ -373,8 +394,28 @@ export function useLazyUrlRows(opts: LazyRowsOpts): LazyRowsState {
|
||||
// range, its rows are usually already in the cache.
|
||||
const fetchFrom = Math.max(0, first - PREFETCH_CHUNKS);
|
||||
const fetchTo = last + PREFETCH_CHUNKS;
|
||||
const rowsNow = totalRef.current;
|
||||
for (let i = fetchFrom; i <= fetchTo; i++) {
|
||||
if (!chunks.current.has(i)) void fetchChunk(i);
|
||||
const chunk = chunks.current.get(i);
|
||||
if (!chunk) {
|
||||
void fetchChunk(i);
|
||||
continue;
|
||||
}
|
||||
// A cached chunk is only as complete as the table was when it
|
||||
// was fetched. Treating it as final left every row the crawl
|
||||
// added after that as "…" — black rows on scroll — until the
|
||||
// 5 s safety tick happened to cover them (the live tick stands
|
||||
// down while the user scrolls).
|
||||
const base = i * CHUNK_SIZE;
|
||||
const visible = i >= first && i <= last;
|
||||
const from = visible ? Math.max(base, start) : base;
|
||||
const to = Math.min(visible ? end : base + CHUNK_SIZE - 1, base + CHUNK_SIZE - 1, rowsNow - 1);
|
||||
for (let r = from; r <= to; r++) {
|
||||
if (chunk[r - base] === undefined) {
|
||||
void fetchChunk(i, visible ? VISIBLE_GAP_REFETCH_MS : LOOKAHEAD_REFETCH_MS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
[fetchChunk],
|
||||
|
||||
@@ -61,6 +61,11 @@ export const AZ_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: 'Hamısı',
|
||||
'Blocked by Robots': 'Robots tərəfindən bloklanıb',
|
||||
'Blocked by Bot Protection': 'Bot qorunması tərəfindən bloklanıb',
|
||||
'Blocked by bot protection': 'bot qorunması tərəfindən bloklanıb',
|
||||
Blocked: 'Bloklanıb',
|
||||
Unknown: 'Naməlum',
|
||||
'Not crawled — bot protection answered instead of the page': 'Taranmadı — səhifənin əvəzinə bot qorunması cavab verdi',
|
||||
'No Response': 'Cavab yoxdur',
|
||||
'Success (2xx)': 'Uğurlu (2xx)',
|
||||
'Redirection (3xx)': 'Yönləndirmə (3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const ES_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: 'Todos',
|
||||
'Blocked by Robots': 'Bloqueadas por robots',
|
||||
'Blocked by Bot Protection': 'Bloqueadas por protección antibots',
|
||||
'Blocked by bot protection': 'bloqueada por protección antibots',
|
||||
Blocked: 'Bloqueada',
|
||||
Unknown: 'Desconocido',
|
||||
'Not crawled — bot protection answered instead of the page': 'No rastreada — respondió la protección antibots en lugar de la página',
|
||||
'No Response': 'Sin respuesta',
|
||||
'Success (2xx)': 'Correcto (2xx)',
|
||||
'Redirection (3xx)': 'Redirección (3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const FR_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: 'Tous',
|
||||
'Blocked by Robots': 'Bloquées par robots',
|
||||
'Blocked by Bot Protection': 'Bloquées par la protection anti-bots',
|
||||
'Blocked by bot protection': 'bloquée par la protection anti-bots',
|
||||
Blocked: 'Bloquée',
|
||||
Unknown: 'Inconnu',
|
||||
'Not crawled — bot protection answered instead of the page': 'Non explorée — la protection anti-bots a répondu à la place de la page',
|
||||
'No Response': 'Aucune réponse',
|
||||
'Success (2xx)': 'Succès (2xx)',
|
||||
'Redirection (3xx)': 'Redirection (3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const HI_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: 'सभी',
|
||||
'Blocked by Robots': 'robots से अवरुद्ध',
|
||||
'Blocked by Bot Protection': 'बॉट सुरक्षा से अवरुद्ध',
|
||||
'Blocked by bot protection': 'बॉट सुरक्षा से अवरुद्ध',
|
||||
Blocked: 'अवरुद्ध',
|
||||
Unknown: 'अज्ञात',
|
||||
'Not crawled — bot protection answered instead of the page': 'क्रॉल नहीं हुआ — पेज की जगह बॉट सुरक्षा ने उत्तर दिया',
|
||||
'No Response': 'कोई प्रतिक्रिया नहीं',
|
||||
'Success (2xx)': 'सफल (2xx)',
|
||||
'Redirection (3xx)': 'रीडायरेक्ट (3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const IT_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: 'Tutti',
|
||||
'Blocked by Robots': 'Bloccati da robots',
|
||||
'Blocked by Bot Protection': 'Bloccati dalla protezione anti-bot',
|
||||
'Blocked by bot protection': 'bloccata dalla protezione anti-bot',
|
||||
Blocked: 'Bloccata',
|
||||
Unknown: 'Sconosciuto',
|
||||
'Not crawled — bot protection answered instead of the page': 'Non scansionata — ha risposto la protezione anti-bot al posto della pagina',
|
||||
'No Response': 'Nessuna risposta',
|
||||
'Success (2xx)': 'Successo (2xx)',
|
||||
'Redirection (3xx)': 'Reindirizzamento (3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const KO_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: '전체',
|
||||
'Blocked by Robots': 'robots로 차단됨',
|
||||
'Blocked by Bot Protection': '봇 보호로 차단됨',
|
||||
'Blocked by bot protection': '봇 보호로 차단됨',
|
||||
Blocked: '차단됨',
|
||||
Unknown: '알 수 없음',
|
||||
'Not crawled — bot protection answered instead of the page': '크롤되지 않음 — 페이지 대신 봇 보호가 응답했습니다',
|
||||
'No Response': '응답 없음',
|
||||
'Success (2xx)': '성공(2xx)',
|
||||
'Redirection (3xx)': '리다이렉트(3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const PT_BR_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: 'Todos',
|
||||
'Blocked by Robots': 'Bloqueadas pelo robots',
|
||||
'Blocked by Bot Protection': 'Bloqueadas pela proteção contra bots',
|
||||
'Blocked by bot protection': 'bloqueada pela proteção contra bots',
|
||||
Blocked: 'Bloqueada',
|
||||
Unknown: 'Desconhecido',
|
||||
'Not crawled — bot protection answered instead of the page': 'Não rastreada — a proteção contra bots respondeu no lugar da página',
|
||||
'No Response': 'Sem resposta',
|
||||
'Success (2xx)': 'Sucesso (2xx)',
|
||||
'Redirection (3xx)': 'Redirecionamento (3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const RU_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: 'Все',
|
||||
'Blocked by Robots': 'Заблокировано robots',
|
||||
'Blocked by Bot Protection': 'Заблокировано защитой от ботов',
|
||||
'Blocked by bot protection': 'заблокировано защитой от ботов',
|
||||
Blocked: 'Заблок.',
|
||||
Unknown: 'Неизвестно',
|
||||
'Not crawled — bot protection answered instead of the page': 'Не обойдено — вместо страницы ответила защита от ботов',
|
||||
'No Response': 'Нет ответа',
|
||||
'Success (2xx)': 'Успех (2xx)',
|
||||
'Redirection (3xx)': 'Перенаправление (3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const TR_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: 'Tümü',
|
||||
'Blocked by Robots': 'Robots Tarafından Engellendi',
|
||||
'Blocked by Bot Protection': 'Bot Koruması Tarafından Engellendi',
|
||||
'Blocked by bot protection': 'bot koruması tarafından engellendi',
|
||||
Blocked: 'Engellendi',
|
||||
Unknown: 'Bilinmiyor',
|
||||
'Not crawled — bot protection answered instead of the page': 'Taranmadı — sayfa yerine bot koruması yanıt verdi',
|
||||
'No Response': 'Yanıt Yok',
|
||||
'Success (2xx)': 'Başarılı (2xx)',
|
||||
'Redirection (3xx)': 'Yönlendirme (3xx)',
|
||||
|
||||
@@ -53,6 +53,11 @@ export const ZH_CN_LABELS: Record<string, string> = {
|
||||
// Response codes
|
||||
All: '全部',
|
||||
'Blocked by Robots': '被 robots 拦截',
|
||||
'Blocked by Bot Protection': '被机器人防护拦截',
|
||||
'Blocked by bot protection': '被机器人防护拦截',
|
||||
Blocked: '已拦截',
|
||||
Unknown: '未知',
|
||||
'Not crawled — bot protection answered instead of the page': '未抓取——响应来自机器人防护,而不是页面',
|
||||
'No Response': '无响应',
|
||||
'Success (2xx)': '成功(2xx)',
|
||||
'Redirection (3xx)': '重定向(3xx)',
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL",
|
||||
"percentOfTotal": "Ümumidən %",
|
||||
"siteSoft404": "Mövcud olmayan səhifələr {{status}} qaytarır (soft 404)",
|
||||
"siteSoft404Hint": "Mövcud olmayan səhifəyə edilən sorğuya 404 əvəzinə {{status}} cavabı verildi, buna görə də bu saytdakı qırıq keçidlər axtarış sistemlərinə işləyən səhifələr kimi görünür."
|
||||
"siteSoft404Hint": "Mövcud olmayan səhifəyə edilən sorğuya 404 əvəzinə {{status}} cavabı verildi, buna görə də bu saytdakı qırıq keçidlər axtarış sistemlərinə işləyən səhifələr kimi görünür.",
|
||||
"siteBotBlocked": "Bot qorunması taramanın bir hissəsini blokladı",
|
||||
"siteBotBlockedHint": "Saytın bot qorunması bu səhifələrin əvəzinə yoxlama səhifəsi ilə cavab verdi. Onlar taranmadı və qırıq keçid və sitemap yoxlamalarından kənarda saxlanılır. Tarama sürətini azaldın və ya tarayıcının user agent-ini icazə siyahısına əlavə edin, sonra onları yenidən tarayın."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "Xarici xidmətləri öz giriş məlumatlarınızla qoşun. API açarları və sirlər əməliyyat sisteminizin təhlükəsiz anbarı ilə şifrələnərək saxlanılır və heç vaxt bu maşından kənara çıxmır.",
|
||||
@@ -394,7 +396,54 @@
|
||||
"orphanMetricaVisits_one": "{{formatted}} Metrica ziyarəti (giriş)",
|
||||
"orphanMetricaVisits_other": "{{formatted}} Metrica ziyarəti (giriş)",
|
||||
"orphanMetricaPageviews_one": "{{formatted}} Metrica səhifə baxışı (hit)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} Metrica səhifə baxışı (hit)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} Metrica səhifə baxışı (hit)",
|
||||
"noResponse": "Cavab yoxdur",
|
||||
"bucketOrphan": "0 (yetim)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "YOX",
|
||||
"warn": "DİQQƏT",
|
||||
"bad": "PİS",
|
||||
"err": "XƏTA",
|
||||
"slow": "YAVAŞ",
|
||||
"net": "ŞƏBƏKƏ"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} uğurlu · {{rate}}% xəta",
|
||||
"avgMs": "orta {{ms}} ms",
|
||||
"distinctIds_one": "{{count}} fərqli ID",
|
||||
"distinctIds_other": "{{count}} fərqli ID",
|
||||
"pageCount_one": "{{formatted}} səhifə",
|
||||
"pageCount_other": "{{formatted}} səhifə",
|
||||
"imageCount_one": "{{formatted}} şəkil",
|
||||
"imageCount_other": "{{formatted}} şəkil",
|
||||
"hits_one": "{{formatted}} dəfə",
|
||||
"hits_other": "{{formatted}} dəfə",
|
||||
"avgWords": "orta {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "mənbə: {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "Naviqasiya",
|
||||
"header": "Başlıq hissəsi",
|
||||
"content": "Məzmun",
|
||||
"sidebar": "Yan panel",
|
||||
"footer": "Alt hissə",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "İndekslənə bilən",
|
||||
"noindex": "İndekslənə bilməyən — noindex",
|
||||
"canonical": "İndekslənə bilməyən — kanonikləşdirilmiş",
|
||||
"robotsBlocked": "İndekslənə bilməyən — robots.txt ilə bloklanıb",
|
||||
"redirect": "İndekslənə bilməyən — yönləndirmə",
|
||||
"clientError": "İndekslənə bilməyən — 4xx",
|
||||
"serverError": "İndekslənə bilməyən — 5xx",
|
||||
"botBlocked": "Naməlum — bot qorunması tərəfindən bloklanıb"
|
||||
},
|
||||
"kind": {
|
||||
"image": "Şəkil",
|
||||
"font": "Şrift",
|
||||
"other": "Digər"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "Sitemap yoxlayıcısı",
|
||||
@@ -1049,7 +1098,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "Cavab başlıqlarına əsasən",
|
||||
"likelyCauses": "Ehtimal olunan səbəblər",
|
||||
"whatToDo": "Nə etməli"
|
||||
"whatToDo": "Nə etməli",
|
||||
"cloudflareStyle": "Cloudflare tipli edge kodu",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "Veb server naməlum xəta qaytardı",
|
||||
"explanation": "Cloudflare mənbə serverinə qoşuldu, lakin boş, pozulmuş və ya gözlənilməz cavab aldı (məsələn, bağlantı cavabın ortasında kəsildi, mənbə çox böyük başlıqlar qaytardı və ya mənbə prosesi çökdü). Bu, 'mənbədə nəsə səhv getdi' mənasını verən ümumi koddur.",
|
||||
"cause1": "Sorğunu emal edərkən mənbə server çökdü, yenidən başladı və ya işçiləri/yaddaşı tükəndi",
|
||||
"cause2": "Mənbə tam cavab göndərməmiş TCP bağlantısını bağladı (çox vaxt tətbiq səviyyəsində vaxt aşımı və ya yaddaş çatışmazlığı səbəbindən dayandırılma)",
|
||||
"cause3": "Taramanın sorğu sürəti mənbənin proses hovuzunu aşdı — yük altında toplu görünən 520-lər demək olar ki, həmişə bunu göstərir",
|
||||
"cause4": "Mənbə Cloudflare-in icazə verdiyindən böyük cavab başlıqları qaytardı (nadir hal; adətən səhv qurulmuş tətbiq)",
|
||||
"todo1": "520-lər bir çox URL-də toplu görünürsə, Maksimum paralellik / Maksimum URL/san dəyərini azaldın və İşçi başına gecikmə əlavə edin (Parametrlər → Sürət) — tarama mənbəni həddən artıq yükləyir",
|
||||
"todo2": "Tarama bitdikdən sonra təsirlənən URL-ləri yenidən tarayın (müvəqqəti 520-lər təkrar cəhddə çox vaxt aradan qalxır — Parametrlər → Sürət bölməsində Cəhd sayını artırın)",
|
||||
"todo3": "Yalnız müəyyən URL-lər daim 520 verirsə, həmin səhifələrdə server tərəfində xəta var — sayt sahibinə bildirin"
|
||||
},
|
||||
"521": {
|
||||
"name": "Veb server işləmir",
|
||||
"explanation": "Cloudflare mənbə serveri ilə TCP bağlantısı aça bilmədi — mənbə bağlantını açıq şəkildə rədd etdi və ya gözlənilən portu dinləmir.",
|
||||
"cause1": "Mənbə veb server (nginx/Apache/Node və s.) dayandırılıb və ya çöküb",
|
||||
"cause2": "Mənbənin firewall-u Cloudflare-in IP aralıqlarını bloklayır (səhv qurulma — mənbə Cloudflare-ə icazə verməlidir)",
|
||||
"cause3": "Mənbə server yeni bağlantıları qəbul etməyəcək qədər yüklənib",
|
||||
"todo1": "Bu, mənbənin əlçatanlıq problemidir, tarayıcının yan keçə biləcəyi bir şey deyil — sayt bu URL-lər üçün faktiki olaraq işləmir",
|
||||
"todo2": "Sonra yenidən tarayın; davam edərsə, sayt xarabdır"
|
||||
},
|
||||
"522": {
|
||||
"name": "Bağlantının vaxtı bitdi",
|
||||
"explanation": "Cloudflare mənbə ilə TCP əl sıxmasına başladı, amma o, vaxt həddi ərzində tamamlanmadı — mənbə SYN-i qəbul etdi, lakin heç cavab vermədi və ya aradakı şəbəkə qurğusu paketləri atdı.",
|
||||
"cause1": "Mənbə ciddi şəkildə yüklənib (TCP növbəsi dolub)",
|
||||
"cause2": "Mənbənin firewall-u Cloudflare-in paketlərini səssizcə atır (rədd etmir)",
|
||||
"cause3": "Cloudflare ilə mənbə arasında şəbəkə/marşrutlaşdırma problemi",
|
||||
"cause4": "Tarama yükü altında: taramanın paralel sorğuları mənbənin bağlantı cədvəlini tükətdi",
|
||||
"todo1": "Paralelliyi azaldın (Parametrlər → Sürət → Maksimum paralellik) və yenidən tarayın — taramanın ortasında görünürsə, adi səbəb bağlantı cədvəlinin tükənməsidir",
|
||||
"todo2": "Təsirlənən URL-ləri tarama bitdikdən sonra yenidən sınayın"
|
||||
},
|
||||
"523": {
|
||||
"name": "Mənbəyə çatmaq mümkün deyil",
|
||||
"explanation": "Cloudflare mənbə serverinə ümumiyyətlə yol tapa bilmədi — adətən Cloudflare-dəki mənbə qeydində DNS problemi var və ya mənbənin şəbəkəsi işləmir.",
|
||||
"cause1": "Saytın Cloudflare-dəki DNS qeydi səhv / işləməyən IP-yə yönəlir",
|
||||
"cause2": "Mənbənin hostinq provayderində şəbəkə nasazlığı var",
|
||||
"cause3": "Cloudflare ilə mənbə arasında BGP / marşrutlaşdırma nasazlığı",
|
||||
"todo1": "Tarayıcının edə biləcəyi bir şey yoxdur — bu, mənbənin DNS/şəbəkə qurğusundakı xətadır"
|
||||
},
|
||||
"524": {
|
||||
"name": "Vaxt aşımı baş verdi",
|
||||
"explanation": "Cloudflare mənbə ilə TCP bağlantısını uğurla qurdu, amma mənbə Cloudflare-in vaxt həddi (standart 100 san) ərzində tam HTTP cavabı göndərmədi. Mənbə işləyir, lakin bu sorğuda çox yavaşdır.",
|
||||
"cause1": "Səhifədə yavaş verilənlər bazası sorğusu, xarici API çağırışı və ya ağır hesablama",
|
||||
"cause2": "Mənbə yüklənib — sorğular dolmuş işçi hovuzunun arxasında növbədədir",
|
||||
"cause3": "Tarama yükü altında: tarama mənbənin sorğu növbəsini doldurdu və bu URL çox uzun gözlədi",
|
||||
"todo1": "Mənbəni doydurmamaq üçün Maksimum paralellik / Maksimum URL/san dəyərini azaldın və İşçi başına gecikmə əlavə edin (Parametrlər → Sürət)",
|
||||
"todo2": "Həqiqətən yavaş səhifələrə daha çox vaxt vermək istəyirsinizsə, Cavab gözləmə müddətini artırın (Parametrlər → Tarayıcı)",
|
||||
"todo3": "Təsirlənən URL-ləri tarama bitdikdən sonra yenidən sınayın"
|
||||
},
|
||||
"525": {
|
||||
"name": "SSL əl sıxması alınmadı",
|
||||
"explanation": "Cloudflare ilə mənbə server arasında TLS əl sıxması alınmadı. Bu, ünvan sətrində gördüyünüz brauzer↔Cloudflare hissəsi deyil, Cloudflare↔mənbə hissəsidir — buna görə sayt adi ziyarətçiyə qaydasında görünə və yenə də 525 verə bilər.",
|
||||
"cause1": "Mənbənin TLS sertifikatının müddəti bitib, özü tərəfindən imzalanıb və ya başqa host adı üçün verilib (Cloudflare 'Full (strict)' rejimində olanda)",
|
||||
"cause2": "Mənbə yalnız Cloudflare-in rədd etdiyi köhnə TLS versiyalarını / şifrə dəstlərini dəstəkləyir",
|
||||
"cause3": "Mənbə server yüklənib və TLS əl sıxmalarını atır",
|
||||
"todo1": "Mənbənin TLS qurğusunda xəta — tarayıcının dəyişə biləcəyi bir şey deyil. Hostun sertifikatını yoxlayın (HTTPS üçün URL təfərrüatlarındakı TLS Cert sətirləri)"
|
||||
},
|
||||
"526": {
|
||||
"name": "Etibarsız SSL sertifikatı",
|
||||
"explanation": "Cloudflare ('Full (strict)' SSL rejimində) mənbə serverin TLS sertifikatını təsdiqləyə bilmədi.",
|
||||
"cause1": "Mənbə sertifikatının müddəti bitib, özü tərəfindən imzalanıb və ya başqa host adı üçün verilib",
|
||||
"cause2": "Mənbədəki sertifikat zənciri natamamdır (aralıq sertifikatlar yoxdur)",
|
||||
"todo1": "Mənbə sertifikatı problemi — tarayıcının edə biləcəyi bir şey yoxdur"
|
||||
},
|
||||
"527": {
|
||||
"name": "Railgun xətası (Cloudflare, köhnəlib)",
|
||||
"explanation": "Cloudflare Railgun-da (artıq dayandırılmış mənbə sürətləndirmə məhsulu) bağlantı xətası. Faktiki olaraq köhnəlmiş koddur; onu görürsünüzsə, sayt çox köhnə Cloudflare quruluşundan istifadə edir.",
|
||||
"cause1": "Köhnə Cloudflare Railgun bağlantı nasazlığı",
|
||||
"todo1": "Tarayıcı tərəfindən edilə biləcək bir şey yoxdur"
|
||||
},
|
||||
"530": {
|
||||
"name": "Cloudflare xətası (adətən firewall / WAF bloku — xəta 1020)",
|
||||
"explanation": "530, Cloudflare-in \"xəta baş verdi, gövdədəki 1xxx xəta koduna baxın\" deməsinin üsuludur. Taramada bu, demək olar ki, həmişə 1020 \"Access Denied\"dir — Cloudflare Firewall / WAF / Bot-Fight qaydası sorğunu blokladı. Daha nadir hallarda 1016 (mənbə DNS xətası) və ya digər 1xxx kodları ilə birlikdə gəlir.",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode tarayıcını işarələdi — IP, User-Agent, sorğu sürəti və ya brauzer barmaq izlərinin olmaması səbəbindən",
|
||||
"cause2": "Saytda tarayıcının keçə bilmədiyi \"managed challenge\" var",
|
||||
"cause3": "(Nadir hallarda) blok deyil, mənbə DNS xətası (1016)",
|
||||
"todo1": "Brauzerə bənzər User-Agent təyin edin (Parametrlər → Sorğular) — standart tarayıcı UA-sı ən çox rast gəlinən səbəbdir",
|
||||
"todo2": "Maksimum paralellik / Maksimum URL/san dəyərini azaldın və İşçi başına gecikmə əlavə edin (Parametrlər → Sürət) — bir IP-dən gələn sürətli sorğular sürətə əsaslanan qaydaları işə salır",
|
||||
"todo3": "robots.txt-ə və orada göstərilmiş Crawl-delay dəyərinə riayət edin (Parametrlər → Tarayıcı)",
|
||||
"todo4": "Sayt sizindirsə, tarayıcını Cloudflare-də icazə siyahısına əlavə edin; əks halda sayt avtomatik girişi qəsdən bloklayır"
|
||||
},
|
||||
"401": {
|
||||
"name": "İcazəsiz",
|
||||
"explanation": "Resurs autentifikasiya tələb edir və heç bir (və ya etibarsız) giriş məlumatı göndərilmədi.",
|
||||
"cause1": "Səhifə HTTP Basic/Bearer autentifikasiyası arxasındadır",
|
||||
"cause2": "Sessiya kukisi tələb olunur, amma göndərilmədi",
|
||||
"todo1": "Giriş məlumatlarınız varsa, onları Parametrlər → Autentifikasiya bölməsində qurun",
|
||||
"todo2": "Sayt məzmunu sessiya arxasında saxlayırsa, Parametrlər → Kukilər → \"Hamısını qəbul et\" seçimindən istifadə edin"
|
||||
},
|
||||
"403": {
|
||||
"name": "Qadağandır",
|
||||
"explanation": "Server sorğunu başa düşdü, lakin onu yerinə yetirməkdən imtina edir. Taramada bu, əsl icazə problemi deyil, çox vaxt bot bloku və ya WAF qaydasıdır.",
|
||||
"cause1": "Tarayıcının User-Agent-i, IP-si və ya sorğu sürəti WAF / bot qorunması qaydasını işə saldı",
|
||||
"cause2": "Qovluq və ya fayl həqiqətən anonim istifadəçilər üçün əlçatan deyil",
|
||||
"cause3": "Hotlink / referer qorunması",
|
||||
"todo1": "Brauzerə bənzər User-Agent sınayın (Parametrlər → Sorğular)",
|
||||
"todo2": "Tarama sürətini azaldın (Parametrlər → Sürət)",
|
||||
"todo3": "Bütün saytda baş verirsə, sayt avtomatik girişi aktiv şəkildə bloklayır"
|
||||
},
|
||||
"404": {
|
||||
"name": "Tapılmadı",
|
||||
"explanation": "Serverdə bu URL-də heç bir resurs yoxdur.",
|
||||
"cause1": "Qırıq daxili/xarici keçid",
|
||||
"cause2": "Səhifə yönləndirmə olmadan silinib",
|
||||
"cause3": "Keçidin href dəyərində yazı səhvi",
|
||||
"todo1": "Keçid verən səhifələri düzəldin və ya keçidləri silin — buraya kimin keçid verdiyini Daxil olan keçidlər tabında görə bilərsiniz"
|
||||
},
|
||||
"408": {
|
||||
"name": "Sorğunun vaxtı bitdi",
|
||||
"explanation": "Server sorğunun tamamlanmasını gözləyərkən vaxt həddi bitdi.",
|
||||
"cause1": "Tarayıcı ilə server arasında yavaş şəbəkə",
|
||||
"cause2": "Server tərəfində bağlantı vaxt həddi çox aşağı qurulub",
|
||||
"todo1": "Adətən müvəqqətidir — Cəhd sayını artırın (Parametrlər → Sürət)"
|
||||
},
|
||||
"410": {
|
||||
"name": "Silinib",
|
||||
"explanation": "Resurs qəsdən və birdəfəlik silinib — bu, 404-dən daha güclü siqnaldır.",
|
||||
"cause1": "Səhifə qəsdən götürülüb; server tarayıcılara onu çıxarmağı bildirir",
|
||||
"todo1": "Bu URL-ə keçidləri silin — Google onu 404-dən daha tez indeksdən çıxaracaq"
|
||||
},
|
||||
"429": {
|
||||
"name": "Həddən artıq sorğu (sürət limiti)",
|
||||
"explanation": "Server tarayıcının sürətini məhdudlaşdırır — bu IP/User-Agent-dən çox qısa müddətdə həddən artıq sorğu gəlib.",
|
||||
"cause1": "Tarama sürəti serverin hər müştəri üçün limitini keçir",
|
||||
"cause2": "Sərt məhdudlaşdırma tətbiq edən paylaşılan hostinq",
|
||||
"todo1": "Maksimum paralellik / Maksimum URL/san dəyərini azaldın və İşçi başına gecikmə əlavə edin (Parametrlər → Sürət)",
|
||||
"todo2": "Retry-After başlığına riayət edin — server göndəribsə, aşağıdakı \"Cavab başlıqlarına əsasən\" bölməsinə baxın",
|
||||
"todo3": "\"robots.txt-in Crawl-delay dəyərinə riayət et\" seçimini aktiv edin (Parametrlər → Tarayıcı)"
|
||||
},
|
||||
"451": {
|
||||
"name": "Hüquqi səbəblərə görə əlçatan deyil",
|
||||
"explanation": "Resurs hüquqi səbəblərə görə bloklanıb (məhkəmə qərarı, GDPR coğrafi bloku, DMCA və s.).",
|
||||
"cause1": "Coğrafi məhdudiyyətli məzmun",
|
||||
"cause2": "Qanunla tələb olunan silinmə",
|
||||
"todo1": "Heç nə — blok qəsdəndir"
|
||||
},
|
||||
"500": {
|
||||
"name": "Daxili server xətası",
|
||||
"explanation": "Səhifə yaradılarkən server tərəfindəki tətbiqdə emal olunmamış xəta baş verdi.",
|
||||
"cause1": "Səhifə şablonunda / backend kodunda xəta",
|
||||
"cause2": "Verilənlər bazası əlçatan deyil",
|
||||
"cause3": "Serverdə səhv quraşdırma",
|
||||
"todo1": "Tarama bitdikdən sonra yenidən sınayın (müvəqqəti 500-lər aradan qalxır; davamlı olanlar əsl xətadır)",
|
||||
"todo2": "500-lər yük altında görünürsə, tarama verilənlər bazasını həddən artıq yükləyə bilər — taramanı yavaşladın (Parametrlər → Sürət)"
|
||||
},
|
||||
"502": {
|
||||
"name": "Yanlış şlüz",
|
||||
"explanation": "Tətbiqin qarşısındakı əks proksi / yük balanslaşdırıcısı yuxarı axındakı tətbiq serverindən etibarsız cavab aldı.",
|
||||
"cause1": "Yuxarı axındakı tətbiq serveri çökdü və ya mənasız məlumat qaytardı",
|
||||
"cause2": "Yuxarı axın yüklənib — tarama yükü altında tez-tez olur",
|
||||
"cause3": "Proksi↔yuxarı axın vaxt aşımı",
|
||||
"todo1": "502-lər toplu görünürsə, tarama sürətini azaldın (Parametrlər → Sürət)",
|
||||
"todo2": "Təsirlənən URL-ləri tarama bitdikdən sonra yenidən sınayın"
|
||||
},
|
||||
"503": {
|
||||
"name": "Xidmət əlçatan deyil",
|
||||
"explanation": "Server sorğunu müvəqqəti olaraq emal edə bilmir — adətən həddən artıq yük və ya texniki xidmət səbəbindən. Çox vaxt Retry-After başlığı ilə gəlir.",
|
||||
"cause1": "Server yüklənib — buna taramanın özü də səbəb ola bilər",
|
||||
"cause2": "Planlaşdırılmış texniki xidmət vaxtı",
|
||||
"cause3": "Avtomatik miqyaslamada gecikmə (yeni nüsxələr işə düşür)",
|
||||
"todo1": "Maksimum paralellik / Maksimum URL/san dəyərini azaldın və İşçi başına gecikmə əlavə edin (Parametrlər → Sürət)",
|
||||
"todo2": "Retry-After dəyərinə riayət edin (aşağıdakı \"Cavab başlıqlarına əsasən\" bölməsinə baxın)",
|
||||
"todo3": "Təsirlənən URL-ləri tarama bitdikdən sonra yenidən sınayın"
|
||||
},
|
||||
"504": {
|
||||
"name": "Şlüz vaxt aşımı",
|
||||
"explanation": "Əks proksi / yük balanslaşdırıcısı yuxarı axındakı tətbiq serverindən vaxtında cavab almadı.",
|
||||
"cause1": "Səhifədə yavaş verilənlər bazası sorğusu / xarici API çağırışı",
|
||||
"cause2": "Yuxarı axın yüklənib — sorğular növbədədir",
|
||||
"cause3": "Tarama yükü altında: tarama yuxarı axının işçi hovuzunu doydurdu",
|
||||
"todo1": "Tarama sürətini azaldın (Parametrlər → Sürət)",
|
||||
"todo2": "Həqiqətən yavaş səhifələr üçün Cavab gözləmə müddətini artırın (Parametrlər → Tarayıcı)",
|
||||
"todo3": "Təsirlənən URL-ləri tarama bitdikdən sonra yenidən sınayın"
|
||||
},
|
||||
"508": {
|
||||
"name": "Dövr aşkarlandı",
|
||||
"explanation": "Server sorğunu emal edərkən sonsuz dövr aşkarladı (çox vaxt WebDAV və ya server tərəfində yönləndirmə dövrü).",
|
||||
"cause1": "Server tərəfində yönləndirmə dövrü",
|
||||
"cause2": "Rekursiv include / simvolik keçid dövrü",
|
||||
"todo1": "Server xətası — tarayıcının edə biləcəyi bir şey yoxdur"
|
||||
},
|
||||
"509": {
|
||||
"name": "Trafik limiti aşıldı",
|
||||
"explanation": "Hostinq hesabının aylıq trafik limitinə çatdığını bildirən qeyri-standart kod (cPanel/Apache).",
|
||||
"cause1": "Paylaşılan hostinqin trafik kvotası tükənib",
|
||||
"todo1": "Tarayıcının edə biləcəyi bir şey yoxdur — provayder bütün hesabı məhdudlaşdırır"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Cloudflare arxasında — cf-ray {{ray}}. Status kodu brauzerin gördüyünü deyil, Cloudflare↔mənbə əlaqəsini təsvir edir.",
|
||||
"fronted": "{{front}} vasitəsilə təqdim olunur.",
|
||||
"origin": "Mənbə server: {{server}}.",
|
||||
"retryAfterSeconds": "Server Retry-After: {{seconds}} san göndərdi — müştərilərdən bu müddət ərzində açıq şəkildə gözləmələrini istəyir. Diqqətli tarama bu URL-i bundan əvvəl yenidən istəməməlidir.",
|
||||
"retryAfter": "Server Retry-After: {{value}} göndərdi.",
|
||||
"cfCache": "Cloudflare keş statusu: {{status}}.",
|
||||
"xRobots": "X-Robots-Tag: {{value}} — statusdan asılı olmayaraq indekslənə bilməyə təsir edir."
|
||||
},
|
||||
"robots": {
|
||||
"title": "Sorğu göndərilməyib — robots.txt icazə vermir",
|
||||
"explanation": "Tarayıcı bu URL üçün heç vaxt sorğu göndərmədi: qurulmuş user-agent üçün robots.txt qaydası buna icazə vermir. Heç vaxt cavab istənilmədiyi üçün status kodu yoxdur — bu, bağlantı xətası deyil.",
|
||||
"cause1": "Saytın robots.txt faylındakı Disallow qaydası tarama user-agent-i üçün bu yola uyğun gəlir",
|
||||
"cause2": "URL-ə taranmış səhifələrdən keçid verilir, ona görə də qeydə alınır və sayılır, amma heç vaxt yüklənmir",
|
||||
"todo1": "İstisna qəsdəndirsə, heç nə etmək lazım deyil — Google da onu taramayacaq",
|
||||
"todo2": "Səhifənin indekslənə bilən olmalı olub-olmadığını yoxlayın: bloklanmış URL keçid dəyərini ötürə bilmir və məzmunu heç vaxt görünmür",
|
||||
"todo3": "Hansı qaydanın uyğun gəldiyini görmək üçün Kömək → robots.txt test edicisindən istifadə edin və ya yenə də yoxlamaq üçün Parametrlər → Tarayıcı bölməsində \"robots.txt-ə riayət et\" seçimini söndürün"
|
||||
},
|
||||
"network": {
|
||||
"title": "HTTP cavabı yoxdur — şəbəkə / bağlantı xətası",
|
||||
"explanationDetail": "Sorğu heç bir HTTP cavabı almadı. Tarayıcının qeydə aldığı: \"{{detail}}\".",
|
||||
"explanation": "Sorğu heç bir HTTP cavabı almadı — hər hansı status sətri gəlməmişdən əvvəl DNS axtarışı, TCP bağlantısı, TLS əl sıxması və ya cavabın oxunması alınmadı.",
|
||||
"cause1": "DNS host adını həll edə bilmədi (yazı səhvi, işləməyən domen və ya yalnız şəxsi şəbəkə daxilində həll olunan ad)",
|
||||
"cause2": "TCP bağlantısı rədd edildi və ya vaxtı bitdi (host işləmir, firewall, səhv port)",
|
||||
"cause3": "TLS əl sıxması alınmadı (müddəti bitmiş/etibarsız sertifikat, protokol uyğunsuzluğu)",
|
||||
"cause4": "Bağlantı sıfırlandı və ya cavabın ortasında oxuma vaxtı bitdi",
|
||||
"cause5": "Sorğu Cavab gözləmə müddətini (Parametrlər → Tarayıcı) və ya Maksimum cavab vaxtını (Parametrlər → Geniş) aşdığı üçün tarayıcı tərəfindən dayandırıldı",
|
||||
"todo1": "Host adının həll olunduğunu və saytın bu kompüterdəki adi brauzerdən açıldığını yoxlayın",
|
||||
"todo2": "Əlçatandırsa, amma yavaşdırsa, Cavab gözləmə müddətini artırın (Parametrlər → Tarayıcı)",
|
||||
"todo3": "Yalnız yük altında alınmırsa, Maksimum paralellik / Maksimum URL/san dəyərini azaldın (Parametrlər → Sürət) — mənbədə bağlantı cədvəlinin tükənməsi müştəriyə şəbəkə xətası kimi görünür",
|
||||
"todo4": "Təsirlənən URL-ləri tarama bitdikdən sonra yenidən sınayın (müvəqqəti sıfırlamalar təkrar cəhddə aradan qalxır)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — Məlumat",
|
||||
"fallback": "Məlumat",
|
||||
"explanation": "1xx cavabı ({{text}}). Bunlar aralıq cavablardır; tarayıcı onları müvəqqəti sayır və son statusu gözləyir."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "Uğurlu",
|
||||
"explanation": "Sorğu uğurlu oldu. Diaqnoza ehtiyac yoxdur — bu bölmə yalnız xəta və yönləndirmə kodları üçün əlavə təfərrüat verir."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "Yönləndirmə",
|
||||
"explanation301": "Daimi yönləndirmə. Axtarış sistemləri sıralama siqnallarını hədəfə ötürür və indekslərini yeni URL ilə yeniləyir.",
|
||||
"explanation302": "Müvəqqəti yönləndirmə. Axtarış sistemləri orijinal URL-i indeksdə saxlayır. 302-nin uzun müddət istifadəsi geniş yayılmış SEO səhvidir — adətən 301 olmalıdır.",
|
||||
"explanation308": "Daimi yönləndirmə (301 kimi, lakin sorğu metodu saxlanılır).",
|
||||
"explanation304": "Dəyişməyib — keşdəki nüsxə hələ aktualdır. Tarayıcı adətən şərti başlıqlar olmadan sorğu göndərir, ona görə bu, qeyri-adi haldır.",
|
||||
"explanationOther": "{{code}} yönləndirməsi ({{text}}).",
|
||||
"location": "Location başlığı buraya yönəlir: {{location}}",
|
||||
"noLocation": "Location başlığı tutulmadı — yönləndirmə üçün qeyri-adi haldır.",
|
||||
"todoPermanent": "Bu yönləndirmə daimidirsə, sıralama siqnallarının birləşməsi üçün onu 301/308-ə dəyişin",
|
||||
"todoChain": "Tam yönləndirmə zəncirini və son hədəfi görmək üçün Cavab kodları tabına baxın"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "Müştəri xətası",
|
||||
"explanation": "4xx kodu serverin sorğunu müştərinin günahı kimi rədd etdiyini bildirir (səhv sorğu, icazə yoxdur, tapılmadı və s.). Taramada keçid verilən səhifələrdəki davamlı 4xx adətən qırıq keçid deməkdir; ara-sıra görünən 4xx bot qorunması ola bilər.",
|
||||
"cause1": "Qırıq və ya səhv formatlı keçid",
|
||||
"cause2": "Bot qorunması / WAF qaydası",
|
||||
"cause3": "Serverin qəbul etmədiyi metod və ya məzmun",
|
||||
"todo1": "Bu URL-ə nəyin keçid verdiyini görmək üçün Daxil olan keçidlər tabına baxın",
|
||||
"todo2": "Blok kimi görünürsə, brauzerə bənzər User-Agent və daha aşağı sürət sınayın (Parametrlər → Sorğular / Sürət)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "Server xətası",
|
||||
"explanation": "5xx kodu serverin etibarlı kimi qəbul etdiyi sorğunu yerinə yetirə bilmədiyini bildirir. Tarama yükü altında bu, çox vaxt əsl səhifə xətası deyil, taramanın mənbəni həddən artıq yükləməsidir.",
|
||||
"cause1": "Server tərəfində xəta və ya çökmə",
|
||||
"cause2": "Mənbə yüklənib — bəlkə də taramanın özü səbəbindən",
|
||||
"cause3": "Yuxarı axın/proksi nasazlığı",
|
||||
"todo1": "5xx-lər toplu görünürsə, Maksimum paralellik / Maksimum URL/san dəyərini azaldın və İşçi başına gecikmə əlavə edin (Parametrlər → Sürət)",
|
||||
"todo2": "Cəhd sayını artırın (Parametrlər → Sürət) və təsirlənən URL-ləri yenidən tarayın"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "Bu səhifədə başlıq tapılmadı (heç bir",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URLs",
|
||||
"percentOfTotal": "% of Total",
|
||||
"siteSoft404": "Missing pages return {{status}} (soft 404)",
|
||||
"siteSoft404Hint": "A request for a page that does not exist was answered {{status}} instead of 404, so broken links on this site look like working pages to search engines."
|
||||
"siteSoft404Hint": "A request for a page that does not exist was answered {{status}} instead of 404, so broken links on this site look like working pages to search engines.",
|
||||
"siteBotBlocked": "Bot protection blocked part of the crawl",
|
||||
"siteBotBlockedHint": "The site's bot protection answered with a challenge instead of these pages. They were not crawled and are left out of the broken-link and sitemap checks. Lower the crawl speed or allowlist the crawler's user agent, then re-crawl them."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "Connect external services with your own credentials. API keys and secrets are encrypted at rest using your operating system's secure storage and never leave this machine.",
|
||||
@@ -394,7 +396,54 @@
|
||||
"orphanMetricaVisits_one": "{{formatted}} Metrica visit (landing)",
|
||||
"orphanMetricaVisits_other": "{{formatted}} Metrica visits (landing)",
|
||||
"orphanMetricaPageviews_one": "{{formatted}} Metrica pageview (hits)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} Metrica pageviews (hits)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} Metrica pageviews (hits)",
|
||||
"noResponse": "No response",
|
||||
"bucketOrphan": "0 (orphan)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "NON",
|
||||
"warn": "WARN",
|
||||
"bad": "BAD",
|
||||
"err": "ERR",
|
||||
"slow": "SLOW",
|
||||
"net": "NET"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} OK · {{rate}}% errors",
|
||||
"avgMs": "{{ms}} ms avg",
|
||||
"distinctIds_one": "{{count}} distinct ID",
|
||||
"distinctIds_other": "{{count}} distinct IDs",
|
||||
"pageCount_one": "{{formatted}} page",
|
||||
"pageCount_other": "{{formatted}} pages",
|
||||
"imageCount_one": "{{formatted}} image",
|
||||
"imageCount_other": "{{formatted}} images",
|
||||
"hits_one": "{{formatted}} hit",
|
||||
"hits_other": "{{formatted}} hits",
|
||||
"avgWords": "{{formatted}} avg",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "from {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "Navigation",
|
||||
"header": "Header",
|
||||
"content": "Content",
|
||||
"sidebar": "Sidebar",
|
||||
"footer": "Footer",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "Indexable",
|
||||
"noindex": "Non-indexable — noindex",
|
||||
"canonical": "Non-indexable — canonicalised",
|
||||
"robotsBlocked": "Non-indexable — blocked by robots.txt",
|
||||
"redirect": "Non-indexable — redirect",
|
||||
"clientError": "Non-indexable — 4xx",
|
||||
"serverError": "Non-indexable — 5xx",
|
||||
"botBlocked": "Unknown — blocked by bot protection"
|
||||
},
|
||||
"kind": {
|
||||
"image": "Image",
|
||||
"font": "Font",
|
||||
"other": "Other"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "Sitemap Validator",
|
||||
@@ -1049,7 +1098,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "From the response headers",
|
||||
"likelyCauses": "Likely causes",
|
||||
"whatToDo": "What to do"
|
||||
"whatToDo": "What to do",
|
||||
"cloudflareStyle": "Cloudflare-style edge code",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "Web Server Returned an Unknown Error",
|
||||
"explanation": "Cloudflare connected to the origin server but got back an empty, malformed, or unexpected response (e.g. the connection was reset mid-response, the origin returned headers that are too large, or the origin process crashed). It is a catch-all 'something went wrong at the origin' code.",
|
||||
"cause1": "Origin server crashed, restarted, or ran out of workers/memory while serving the request",
|
||||
"cause2": "Origin closed the TCP connection before sending a complete response (often an app-level timeout or OOM kill)",
|
||||
"cause3": "The crawl's request rate overwhelmed the origin's process pool — 520s that appear in bursts under load almost always mean this",
|
||||
"cause4": "Origin returned response headers larger than Cloudflare allows (rare; usually a misconfigured app)",
|
||||
"todo1": "If 520s appear in bursts across many URLs, lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed) — the origin is being overloaded by the crawl",
|
||||
"todo2": "Re-crawl the affected URLs after the crawl finishes (transient 520s often clear on retry — raise Retry Attempts in Settings → Speed)",
|
||||
"todo3": "If only specific URLs 520 consistently, those pages have a server-side bug — flag them to the site owner"
|
||||
},
|
||||
"521": {
|
||||
"name": "Web Server Is Down",
|
||||
"explanation": "Cloudflare could not open a TCP connection to the origin server — the origin actively refused the connection or is not listening on the expected port.",
|
||||
"cause1": "Origin web server (nginx/Apache/Node/etc.) is stopped or has crashed",
|
||||
"cause2": "Origin's firewall is blocking Cloudflare's IP ranges (a misconfiguration — the origin should allow Cloudflare)",
|
||||
"cause3": "Origin server is overloaded to the point of not accepting new connections",
|
||||
"todo1": "This is an origin-availability problem, not something the crawler can work around — the site is effectively down for these URLs",
|
||||
"todo2": "Re-crawl later; if it persists, the site is broken"
|
||||
},
|
||||
"522": {
|
||||
"name": "Connection Timed Out",
|
||||
"explanation": "Cloudflare started a TCP handshake with the origin but it never completed within the timeout — the origin accepted the SYN but never replied, or a network device between them dropped the packets.",
|
||||
"cause1": "Origin is severely overloaded (TCP backlog full)",
|
||||
"cause2": "Origin's firewall is silently dropping (not rejecting) Cloudflare's packets",
|
||||
"cause3": "Network/routing issue between Cloudflare and the origin",
|
||||
"cause4": "Under crawl load: the origin's connection table is exhausted by the crawl's parallel requests",
|
||||
"todo1": "Lower Max Concurrency (Settings → Speed) and re-crawl — connection-table exhaustion is the usual cause when this shows up mid-crawl",
|
||||
"todo2": "Retry the affected URLs after the crawl"
|
||||
},
|
||||
"523": {
|
||||
"name": "Origin Is Unreachable",
|
||||
"explanation": "Cloudflare could not route to the origin server at all — typically a DNS problem in Cloudflare's origin record, or the origin's network is down.",
|
||||
"cause1": "The site's DNS record in Cloudflare points to a wrong / dead IP",
|
||||
"cause2": "Origin's hosting provider has a network outage",
|
||||
"cause3": "BGP / routing failure between Cloudflare and the origin",
|
||||
"todo1": "Nothing the crawler can do — this is an origin DNS/network misconfiguration"
|
||||
},
|
||||
"524": {
|
||||
"name": "A Timeout Occurred",
|
||||
"explanation": "Cloudflare made a TCP connection to the origin successfully, but the origin did not send a complete HTTP response within Cloudflare's timeout (100 s by default). The origin is alive but too slow on this request.",
|
||||
"cause1": "A slow database query, external API call, or heavy computation on the page",
|
||||
"cause2": "Origin is overloaded — requests are queuing behind a saturated worker pool",
|
||||
"cause3": "Under crawl load: the crawl filled the origin's request queue and this URL waited too long behind it",
|
||||
"todo1": "Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed) so the origin isn't saturated",
|
||||
"todo2": "Increase Response Timeout (Settings → Crawler) if you want to give genuinely slow pages more time",
|
||||
"todo3": "Retry affected URLs after the crawl"
|
||||
},
|
||||
"525": {
|
||||
"name": "SSL Handshake Failed",
|
||||
"explanation": "The TLS handshake between Cloudflare and the origin server failed. This is the Cloudflare↔origin leg, not the browser↔Cloudflare leg you see in the address bar — so the site can look fine to a normal visitor and still 525.",
|
||||
"cause1": "Origin's TLS certificate is expired, self-signed, or for the wrong hostname (with Cloudflare in 'Full (strict)' mode)",
|
||||
"cause2": "Origin only supports old TLS versions / cipher suites that Cloudflare rejects",
|
||||
"cause3": "Origin server is overloaded and dropping TLS handshakes",
|
||||
"todo1": "Origin TLS misconfiguration — not something the crawler can change. Check the host's certificate (the TLS Cert rows in URL Details, for HTTPS)"
|
||||
},
|
||||
"526": {
|
||||
"name": "Invalid SSL Certificate",
|
||||
"explanation": "Cloudflare (in 'Full (strict)' SSL mode) could not validate the origin server's TLS certificate.",
|
||||
"cause1": "Origin certificate is expired, self-signed, or issued for a different hostname",
|
||||
"cause2": "Certificate chain on the origin is incomplete (missing intermediates)",
|
||||
"todo1": "Origin certificate problem — nothing the crawler can do"
|
||||
},
|
||||
"527": {
|
||||
"name": "Railgun Error (Cloudflare, deprecated)",
|
||||
"explanation": "A connection error in Cloudflare Railgun (a now-retired origin-acceleration product). Effectively obsolete; if you see it the site is on a very old Cloudflare setup.",
|
||||
"cause1": "Legacy Cloudflare Railgun connection failure",
|
||||
"todo1": "Nothing actionable from the crawler side"
|
||||
},
|
||||
"530": {
|
||||
"name": "Cloudflare Error (usually a firewall / WAF block — error 1020)",
|
||||
"explanation": "A 530 is Cloudflare's way of saying \"an error happened, see the error 1xxx code in the body\". In a crawl context it is almost always 1020 \"Access Denied\" — a Cloudflare Firewall / WAF / Bot-Fight rule blocked the request. Less commonly it pairs with 1016 (origin DNS error) or other 1xxx codes.",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode flagged the crawler — by IP, by User-Agent, by request rate, or by missing browser fingerprints",
|
||||
"cause2": "The site has a \"managed challenge\" the crawler can't solve",
|
||||
"cause3": "(Rarely) an origin DNS error (1016) rather than a block",
|
||||
"todo1": "Set a browser-like User-Agent (Settings → Requests) — the default crawler UA is the #1 trigger",
|
||||
"todo2": "Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed) — rapid requests from one IP trip rate-based rules",
|
||||
"todo3": "Respect robots.txt and any declared Crawl-delay (Settings → Crawler)",
|
||||
"todo4": "If the site owner is you, allowlist the crawler in Cloudflare; otherwise this site is intentionally blocking automated access"
|
||||
},
|
||||
"401": {
|
||||
"name": "Unauthorized",
|
||||
"explanation": "The resource requires authentication and none (or invalid) credentials were sent.",
|
||||
"cause1": "Page is behind HTTP Basic/Bearer auth",
|
||||
"cause2": "A session cookie is required and none was sent",
|
||||
"todo1": "Configure credentials in Settings → Authentication if you have them",
|
||||
"todo2": "Use Settings → Cookies → \"Accept all\" if the site gates content behind a session"
|
||||
},
|
||||
"403": {
|
||||
"name": "Forbidden",
|
||||
"explanation": "The server understood the request but refuses to serve it. In a crawl this is most often a bot block or a WAF rule rather than a genuine permission issue.",
|
||||
"cause1": "WAF / bot-protection rule triggered by the crawler User-Agent, IP, or request rate",
|
||||
"cause2": "Directory or file genuinely not accessible to anonymous users",
|
||||
"cause3": "Hotlink / referer protection",
|
||||
"todo1": "Try a browser-like User-Agent (Settings → Requests)",
|
||||
"todo2": "Lower the crawl rate (Settings → Speed)",
|
||||
"todo3": "If it's site-wide, the site is actively blocking automated access"
|
||||
},
|
||||
"404": {
|
||||
"name": "Not Found",
|
||||
"explanation": "The server has no resource at this URL.",
|
||||
"cause1": "Broken internal/external link",
|
||||
"cause2": "Page was removed without a redirect",
|
||||
"cause3": "Typo in a link href",
|
||||
"todo1": "Fix or remove the linking pages — see the Inlinks tab for who links here"
|
||||
},
|
||||
"408": {
|
||||
"name": "Request Timeout",
|
||||
"explanation": "The server timed out waiting for the request to complete.",
|
||||
"cause1": "Slow network between crawler and server",
|
||||
"cause2": "Server-side connection timeout set very low",
|
||||
"todo1": "Usually transient — raise Retry Attempts (Settings → Speed)"
|
||||
},
|
||||
"410": {
|
||||
"name": "Gone",
|
||||
"explanation": "The resource was intentionally and permanently removed — stronger than a 404.",
|
||||
"cause1": "Page deliberately retired; the server is telling crawlers to drop it",
|
||||
"todo1": "Remove links to this URL — Google will de-index it faster than a 404"
|
||||
},
|
||||
"429": {
|
||||
"name": "Too Many Requests (rate limited)",
|
||||
"explanation": "The server is rate-limiting the crawler — too many requests in too short a window from this IP/User-Agent.",
|
||||
"cause1": "Crawl rate exceeds the server's per-client limit",
|
||||
"cause2": "Shared hosting with aggressive throttling",
|
||||
"todo1": "Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed)",
|
||||
"todo2": "Honour the Retry-After header — see \"From the response headers\" below if the server sent one",
|
||||
"todo3": "Turn on \"Respect robots.txt Crawl-delay\" (Settings → Crawler)"
|
||||
},
|
||||
"451": {
|
||||
"name": "Unavailable for Legal Reasons",
|
||||
"explanation": "The resource is blocked for legal reasons (court order, GDPR geoblock, DMCA, etc.).",
|
||||
"cause1": "Geo-restricted content",
|
||||
"cause2": "Legally-mandated takedown",
|
||||
"todo1": "Nothing — the block is intentional"
|
||||
},
|
||||
"500": {
|
||||
"name": "Internal Server Error",
|
||||
"explanation": "An unhandled error occurred in the server-side application while building the page.",
|
||||
"cause1": "Bug in the page template / backend code",
|
||||
"cause2": "Database unavailable",
|
||||
"cause3": "Misconfiguration on the server",
|
||||
"todo1": "Retry after the crawl (transient 500s clear; persistent ones are a real bug)",
|
||||
"todo2": "If 500s appear under load, the crawl may be overwhelming a database — slow the crawl (Settings → Speed)"
|
||||
},
|
||||
"502": {
|
||||
"name": "Bad Gateway",
|
||||
"explanation": "A reverse proxy / load balancer in front of the app got an invalid response from the upstream application server.",
|
||||
"cause1": "Upstream app server crashed or returned garbage",
|
||||
"cause2": "Upstream is overloaded — common under crawl load",
|
||||
"cause3": "Proxy↔upstream timeout",
|
||||
"todo1": "Lower the crawl rate (Settings → Speed) if 502s appear in bursts",
|
||||
"todo2": "Retry affected URLs after the crawl"
|
||||
},
|
||||
"503": {
|
||||
"name": "Service Unavailable",
|
||||
"explanation": "The server is temporarily unable to handle the request — usually overload or maintenance. Often comes with a Retry-After header.",
|
||||
"cause1": "Server overloaded — the crawl itself can cause this",
|
||||
"cause2": "Scheduled maintenance window",
|
||||
"cause3": "Auto-scaling lag (new instances spinning up)",
|
||||
"todo1": "Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed)",
|
||||
"todo2": "Honour Retry-After (see \"From the response headers\" below)",
|
||||
"todo3": "Retry affected URLs after the crawl"
|
||||
},
|
||||
"504": {
|
||||
"name": "Gateway Timeout",
|
||||
"explanation": "A reverse proxy / load balancer didn't get a response from the upstream app server in time.",
|
||||
"cause1": "Slow database query / external API call on the page",
|
||||
"cause2": "Upstream overloaded — requests queueing",
|
||||
"cause3": "Under crawl load: the crawl saturated the upstream worker pool",
|
||||
"todo1": "Lower the crawl rate (Settings → Speed)",
|
||||
"todo2": "Increase Response Timeout (Settings → Crawler) for genuinely slow pages",
|
||||
"todo3": "Retry affected URLs after the crawl"
|
||||
},
|
||||
"508": {
|
||||
"name": "Loop Detected",
|
||||
"explanation": "The server detected an infinite loop while processing the request (often WebDAV or a redirect loop server-side).",
|
||||
"cause1": "Server-side redirect loop",
|
||||
"cause2": "Recursive include / symlink loop",
|
||||
"todo1": "Server bug — nothing the crawler can do"
|
||||
},
|
||||
"509": {
|
||||
"name": "Bandwidth Limit Exceeded",
|
||||
"explanation": "A non-standard code (cPanel/Apache) meaning the hosting account hit its monthly bandwidth cap.",
|
||||
"cause1": "Shared hosting bandwidth quota exhausted",
|
||||
"todo1": "Nothing the crawler can do — the host is throttling the whole account"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Behind Cloudflare — cf-ray {{ray}}. The status code describes the Cloudflare↔origin link, not what a browser sees.",
|
||||
"fronted": "Fronted by {{front}}.",
|
||||
"origin": "Origin server: {{server}}.",
|
||||
"retryAfterSeconds": "Server sent Retry-After: {{seconds}} s — it is explicitly asking clients to back off for that long. A polite crawl should not re-request this URL sooner.",
|
||||
"retryAfter": "Server sent Retry-After: {{value}}.",
|
||||
"cfCache": "Cloudflare cache status: {{status}}.",
|
||||
"xRobots": "X-Robots-Tag: {{value}} — affects indexability regardless of the status."
|
||||
},
|
||||
"robots": {
|
||||
"title": "Not Requested — Disallowed by robots.txt",
|
||||
"explanation": "The crawler never sent a request for this URL: a robots.txt rule for the configured user-agent disallows it. There is no status code because no response was ever asked for — this is not a connection failure.",
|
||||
"cause1": "A Disallow rule in the site robots.txt matches this path for the crawl user-agent",
|
||||
"cause2": "The URL is linked from crawled pages, so it is recorded and counted, but never fetched",
|
||||
"todo1": "Nothing, if the exclusion is intended — Google will not crawl it either",
|
||||
"todo2": "Check whether the page should be indexable: a disallowed URL cannot pass on link equity and its content is never seen",
|
||||
"todo3": "Use Help → Robots.txt Tester to see which rule matches, or turn off \"Respect robots.txt\" in Settings → Crawler to audit it anyway"
|
||||
},
|
||||
"network": {
|
||||
"title": "No HTTP Response — Network / Connection Error",
|
||||
"explanationDetail": "The request never received an HTTP response. The crawler recorded: \"{{detail}}\".",
|
||||
"explanation": "The request never received an HTTP response — DNS lookup, TCP connection, TLS handshake, or the response read failed before any status line arrived.",
|
||||
"cause1": "DNS could not resolve the hostname (typo, dead domain, or a name only resolvable from inside a private network)",
|
||||
"cause2": "TCP connection refused or timed out (host down, firewall, wrong port)",
|
||||
"cause3": "TLS handshake failed (expired/invalid certificate, protocol mismatch)",
|
||||
"cause4": "Connection reset or read timed out mid-response",
|
||||
"cause5": "Request aborted by the crawler because it exceeded Response Timeout (Settings → Crawler) or Max response time (Settings → Advanced)",
|
||||
"todo1": "Check the hostname resolves and the site is reachable from a normal browser on this machine",
|
||||
"todo2": "If it is reachable but slow, raise Response Timeout (Settings → Crawler)",
|
||||
"todo3": "If it only fails under load, lower Max Concurrency / Max URL/s (Settings → Speed) — connection-table exhaustion on the origin looks like a network error to the client",
|
||||
"todo4": "Retry the affected URLs after the crawl (transient resets clear on retry)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — Informational",
|
||||
"fallback": "Informational",
|
||||
"explanation": "A 1xx response ({{text}}). These are interim responses; the crawler treats them as transient and waits for the final status."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "Success",
|
||||
"explanation": "The request succeeded. No diagnosis needed — this section only has extra detail for error and redirect codes."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "Redirect",
|
||||
"explanation301": "Permanent redirect. Search engines transfer ranking signals to the target and update their index to the new URL.",
|
||||
"explanation302": "Temporary redirect. Search engines keep the original URL indexed. A 302 used long-term is a common SEO mistake — it should usually be a 301.",
|
||||
"explanation308": "Permanent redirect (like 301 but the request method is preserved).",
|
||||
"explanation304": "Not Modified — the cached copy is still fresh. The crawler normally requests without conditional headers, so this is unusual.",
|
||||
"explanationOther": "A {{code}} redirect ({{text}}).",
|
||||
"location": "Location header points to: {{location}}",
|
||||
"noLocation": "No Location header was captured — unusual for a redirect.",
|
||||
"todoPermanent": "If this redirect is permanent, change it to a 301/308 so ranking signals consolidate",
|
||||
"todoChain": "Check the Response Codes tab to see the full redirect chain and its final target"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "Client Error",
|
||||
"explanation": "A 4xx code means the server rejected the request as the client's fault (bad request, not allowed, not found, etc.). In a crawl, persistent 4xx on linked pages usually means broken links; sporadic 4xx can be bot-protection.",
|
||||
"cause1": "Broken or malformed link",
|
||||
"cause2": "Bot-protection / WAF rule",
|
||||
"cause3": "Method or content the server refuses",
|
||||
"todo1": "Check the Inlinks tab to see what links to this URL",
|
||||
"todo2": "If it looks like a block, try a browser-like User-Agent and a slower rate (Settings → Requests / Speed)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "Server Error",
|
||||
"explanation": "A 5xx code means the server failed to fulfil a request it accepted as valid. Under crawl load these are frequently the crawl overwhelming the origin rather than a genuine page bug.",
|
||||
"cause1": "Server-side error or crash",
|
||||
"cause2": "Origin overloaded — possibly by the crawl itself",
|
||||
"cause3": "Upstream/proxy failure",
|
||||
"todo1": "Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed) if 5xx appear in bursts",
|
||||
"todo2": "Raise Retry Attempts (Settings → Speed) and re-crawl affected URLs"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "This page has no detected headings (no",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL",
|
||||
"percentOfTotal": "% del total",
|
||||
"siteSoft404": "Las páginas inexistentes devuelven {{status}} (soft 404)",
|
||||
"siteSoft404Hint": "Una solicitud a una página que no existe recibió {{status}} en lugar de 404, así que los enlaces rotos de este sitio parecen páginas válidas para los buscadores."
|
||||
"siteSoft404Hint": "Una solicitud a una página que no existe recibió {{status}} en lugar de 404, así que los enlaces rotos de este sitio parecen páginas válidas para los buscadores.",
|
||||
"siteBotBlocked": "La protección antibots bloqueó parte del rastreo",
|
||||
"siteBotBlockedHint": "La protección antibots del sitio respondió con un desafío en lugar de estas páginas. No se rastrearon y quedan fuera de las comprobaciones de enlaces rotos y de sitemap. Reduce la velocidad de rastreo o añade el user agent del rastreador a la lista de permitidos y vuelve a rastrearlas."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "Conecta servicios externos con tus propias credenciales. Las claves de API y los secretos se cifran en reposo mediante el almacenamiento seguro de tu sistema operativo y nunca salen de esta máquina.",
|
||||
@@ -406,7 +408,58 @@
|
||||
"orphanMetricaVisits_other": "{{formatted}} visitas de Metrica (entrada)",
|
||||
"orphanMetricaPageviews_one": "{{formatted}} vista de página de Metrica (hits)",
|
||||
"orphanMetricaPageviews_many": "{{formatted}} de vistas de página de Metrica (hits)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} vistas de página de Metrica (hits)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} vistas de página de Metrica (hits)",
|
||||
"noResponse": "Sin respuesta",
|
||||
"bucketOrphan": "0 (huérfana)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "NO",
|
||||
"warn": "AVISO",
|
||||
"bad": "MAL",
|
||||
"err": "ERROR",
|
||||
"slow": "LENTO",
|
||||
"net": "RED"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} OK · {{rate}} % de errores",
|
||||
"avgMs": "media {{ms}} ms",
|
||||
"distinctIds_one": "{{count}} ID distinto",
|
||||
"distinctIds_many": "{{count}} ID distintos",
|
||||
"distinctIds_other": "{{count}} ID distintos",
|
||||
"pageCount_one": "{{formatted}} página",
|
||||
"pageCount_many": "{{formatted}} páginas",
|
||||
"pageCount_other": "{{formatted}} páginas",
|
||||
"imageCount_one": "{{formatted}} imagen",
|
||||
"imageCount_many": "{{formatted}} imágenes",
|
||||
"imageCount_other": "{{formatted}} imágenes",
|
||||
"hits_one": "{{formatted}} aparición",
|
||||
"hits_many": "{{formatted}} apariciones",
|
||||
"hits_other": "{{formatted}} apariciones",
|
||||
"avgWords": "media {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "de {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "Navegación",
|
||||
"header": "Cabecera",
|
||||
"content": "Contenido",
|
||||
"sidebar": "Barra lateral",
|
||||
"footer": "Pie de página",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "Indexable",
|
||||
"noindex": "No indexable — noindex",
|
||||
"canonical": "No indexable — canonicalizada",
|
||||
"robotsBlocked": "No indexable — bloqueada por robots.txt",
|
||||
"redirect": "No indexable — redirección",
|
||||
"clientError": "No indexable — 4xx",
|
||||
"serverError": "No indexable — 5xx",
|
||||
"botBlocked": "Desconocido — bloqueada por protección antibots"
|
||||
},
|
||||
"kind": {
|
||||
"image": "Imagen",
|
||||
"font": "Fuente",
|
||||
"other": "Otro"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "Validador de sitemap",
|
||||
@@ -1070,7 +1123,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "De las cabeceras de respuesta",
|
||||
"likelyCauses": "Causas probables",
|
||||
"whatToDo": "Qué hacer"
|
||||
"whatToDo": "Qué hacer",
|
||||
"cloudflareStyle": "código de edge al estilo de Cloudflare",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "El servidor web devolvió un error desconocido",
|
||||
"explanation": "Cloudflare se conectó al servidor de origen pero recibió una respuesta vacía, malformada o inesperada (por ejemplo, la conexión se cortó a mitad de la respuesta, el origen devolvió cabeceras demasiado grandes o el proceso del origen falló). Es un código genérico para 'algo salió mal en el origen'.",
|
||||
"cause1": "El servidor de origen falló, se reinició o se quedó sin workers/memoria mientras atendía la petición",
|
||||
"cause2": "El origen cerró la conexión TCP antes de enviar una respuesta completa (a menudo un tiempo de espera de la aplicación o un cierre por falta de memoria)",
|
||||
"cause3": "La velocidad de peticiones del rastreo desbordó el pool de procesos del origen: los 520 que aparecen en ráfagas bajo carga casi siempre significan esto",
|
||||
"cause4": "El origen devolvió cabeceras de respuesta más grandes de lo que Cloudflare permite (poco común; normalmente una aplicación mal configurada)",
|
||||
"todo1": "Si los 520 aparecen en ráfagas en muchas URL, reduce Concurrencia máxima / URL/s máximas y añade un Retardo por worker (Ajustes → Velocidad): el rastreo está sobrecargando el origen",
|
||||
"todo2": "Vuelve a rastrear las URL afectadas cuando termine el rastreo (los 520 transitorios suelen desaparecer al reintentar; sube los Intentos de reintento en Ajustes → Velocidad)",
|
||||
"todo3": "Si solo ciertas URL devuelven 520 de forma constante, esas páginas tienen un error en el servidor: avísale al propietario del sitio"
|
||||
},
|
||||
"521": {
|
||||
"name": "El servidor web está caído",
|
||||
"explanation": "Cloudflare no pudo abrir una conexión TCP con el servidor de origen: el origen rechazó activamente la conexión o no escucha en el puerto esperado.",
|
||||
"cause1": "El servidor web de origen (nginx/Apache/Node, etc.) está detenido o ha fallado",
|
||||
"cause2": "El firewall del origen bloquea los rangos de IP de Cloudflare (un error de configuración: el origen debería permitir a Cloudflare)",
|
||||
"cause3": "El servidor de origen está tan sobrecargado que no acepta conexiones nuevas",
|
||||
"todo1": "Es un problema de disponibilidad del origen, no algo que el rastreador pueda sortear: el sitio está caído en la práctica para estas URL",
|
||||
"todo2": "Vuelve a rastrear más tarde; si persiste, el sitio está roto"
|
||||
},
|
||||
"522": {
|
||||
"name": "Se agotó el tiempo de conexión",
|
||||
"explanation": "Cloudflare inició un handshake TCP con el origen pero no se completó dentro del tiempo límite: el origen aceptó el SYN pero nunca respondió, o un dispositivo de red intermedio descartó los paquetes.",
|
||||
"cause1": "El origen está muy sobrecargado (cola TCP llena)",
|
||||
"cause2": "El firewall del origen descarta en silencio (sin rechazar) los paquetes de Cloudflare",
|
||||
"cause3": "Problema de red/enrutamiento entre Cloudflare y el origen",
|
||||
"cause4": "Bajo la carga del rastreo: las peticiones en paralelo del rastreo agotaron la tabla de conexiones del origen",
|
||||
"todo1": "Reduce la concurrencia (Ajustes → Velocidad → Concurrencia máxima) y vuelve a rastrear: el agotamiento de la tabla de conexiones es la causa habitual cuando aparece en mitad del rastreo",
|
||||
"todo2": "Reintenta las URL afectadas después del rastreo"
|
||||
},
|
||||
"523": {
|
||||
"name": "El origen es inalcanzable",
|
||||
"explanation": "Cloudflare no pudo llegar al servidor de origen en absoluto: normalmente un problema de DNS en el registro de origen de Cloudflare o la red del origen está caída.",
|
||||
"cause1": "El registro DNS del sitio en Cloudflare apunta a una IP incorrecta o inactiva",
|
||||
"cause2": "El proveedor de alojamiento del origen tiene una caída de red",
|
||||
"cause3": "Fallo de BGP / enrutamiento entre Cloudflare y el origen",
|
||||
"todo1": "El rastreador no puede hacer nada: es un error de configuración de DNS/red del origen"
|
||||
},
|
||||
"524": {
|
||||
"name": "Se produjo un tiempo de espera",
|
||||
"explanation": "Cloudflare estableció correctamente una conexión TCP con el origen, pero el origen no envió una respuesta HTTP completa dentro del tiempo límite de Cloudflare (100 s por defecto). El origen funciona, pero es demasiado lento en esta petición.",
|
||||
"cause1": "Una consulta lenta a la base de datos, una llamada a una API externa o un cálculo pesado en la página",
|
||||
"cause2": "El origen está sobrecargado: las peticiones hacen cola detrás de un pool de workers saturado",
|
||||
"cause3": "Bajo la carga del rastreo: el rastreo llenó la cola de peticiones del origen y esta URL esperó demasiado",
|
||||
"todo1": "Reduce Concurrencia máxima / URL/s máximas y añade un Retardo por worker (Ajustes → Velocidad) para no saturar el origen",
|
||||
"todo2": "Aumenta el Tiempo de espera de respuesta (Ajustes → Rastreador) si quieres dar más tiempo a las páginas que de verdad son lentas",
|
||||
"todo3": "Reintenta las URL afectadas después del rastreo"
|
||||
},
|
||||
"525": {
|
||||
"name": "Falló el handshake SSL",
|
||||
"explanation": "Falló el handshake TLS entre Cloudflare y el servidor de origen. Es el tramo Cloudflare↔origen, no el tramo navegador↔Cloudflare que ves en la barra de direcciones, así que el sitio puede verse bien para un visitante normal y aun así devolver 525.",
|
||||
"cause1": "El certificado TLS del origen está caducado, autofirmado o emitido para otro nombre de host (con Cloudflare en modo 'Full (strict)')",
|
||||
"cause2": "El origen solo admite versiones de TLS / conjuntos de cifrado antiguos que Cloudflare rechaza",
|
||||
"cause3": "El servidor de origen está sobrecargado y descarta handshakes TLS",
|
||||
"todo1": "Error de configuración TLS del origen: no es algo que el rastreador pueda cambiar. Revisa el certificado del host (las filas TLS Cert en Detalles de la URL, para HTTPS)"
|
||||
},
|
||||
"526": {
|
||||
"name": "Certificado SSL no válido",
|
||||
"explanation": "Cloudflare (en modo SSL 'Full (strict)') no pudo validar el certificado TLS del servidor de origen.",
|
||||
"cause1": "El certificado del origen está caducado, autofirmado o emitido para otro nombre de host",
|
||||
"cause2": "La cadena de certificados del origen está incompleta (faltan intermedios)",
|
||||
"todo1": "Problema con el certificado del origen: el rastreador no puede hacer nada"
|
||||
},
|
||||
"527": {
|
||||
"name": "Error de Railgun (Cloudflare, obsoleto)",
|
||||
"explanation": "Un error de conexión en Cloudflare Railgun (un producto de aceleración del origen ya retirado). Prácticamente obsoleto; si lo ves, el sitio usa una configuración de Cloudflare muy antigua.",
|
||||
"cause1": "Fallo de conexión del antiguo Cloudflare Railgun",
|
||||
"todo1": "No hay nada que hacer desde el lado del rastreador"
|
||||
},
|
||||
"530": {
|
||||
"name": "Error de Cloudflare (normalmente un bloqueo de firewall / WAF — error 1020)",
|
||||
"explanation": "Un 530 es la forma en que Cloudflare dice \"ocurrió un error, consulta el código 1xxx en el cuerpo\". En un rastreo casi siempre es 1020 \"Access Denied\": una regla de Cloudflare Firewall / WAF / Bot-Fight bloqueó la petición. Con menos frecuencia va con 1016 (error de DNS del origen) u otros códigos 1xxx.",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode marcó al rastreador: por IP, por User-Agent, por velocidad de peticiones o por falta de huellas de navegador",
|
||||
"cause2": "El sitio tiene un \"managed challenge\" que el rastreador no puede resolver",
|
||||
"cause3": "(Rara vez) un error de DNS del origen (1016) en lugar de un bloqueo",
|
||||
"todo1": "Configura un User-Agent de navegador (Ajustes → Peticiones): el UA predeterminado del rastreador es el desencadenante número 1",
|
||||
"todo2": "Reduce Concurrencia máxima / URL/s máximas y añade un Retardo por worker (Ajustes → Velocidad): las peticiones rápidas desde una sola IP activan las reglas por velocidad",
|
||||
"todo3": "Respeta robots.txt y cualquier Crawl-delay declarado (Ajustes → Rastreador)",
|
||||
"todo4": "Si el sitio es tuyo, añade el rastreador a la lista de permitidos en Cloudflare; si no, el sitio bloquea a propósito el acceso automatizado"
|
||||
},
|
||||
"401": {
|
||||
"name": "No autorizado",
|
||||
"explanation": "El recurso requiere autenticación y no se enviaron credenciales (o eran inválidas).",
|
||||
"cause1": "La página está detrás de autenticación HTTP Basic/Bearer",
|
||||
"cause2": "Se requiere una cookie de sesión y no se envió ninguna",
|
||||
"todo1": "Configura las credenciales en Ajustes → Autenticación si las tienes",
|
||||
"todo2": "Usa Ajustes → Cookies → \"Aceptar todas\" si el sitio protege el contenido tras una sesión"
|
||||
},
|
||||
"403": {
|
||||
"name": "Prohibido",
|
||||
"explanation": "El servidor entendió la petición pero se niega a atenderla. En un rastreo suele ser un bloqueo de bots o una regla de WAF más que un problema real de permisos.",
|
||||
"cause1": "Regla de WAF / protección antibots activada por el User-Agent, la IP o la velocidad de peticiones del rastreador",
|
||||
"cause2": "El directorio o archivo realmente no es accesible para usuarios anónimos",
|
||||
"cause3": "Protección contra hotlinking / referer",
|
||||
"todo1": "Prueba un User-Agent de navegador (Ajustes → Peticiones)",
|
||||
"todo2": "Reduce la velocidad del rastreo (Ajustes → Velocidad)",
|
||||
"todo3": "Si ocurre en todo el sitio, el sitio está bloqueando activamente el acceso automatizado"
|
||||
},
|
||||
"404": {
|
||||
"name": "No encontrado",
|
||||
"explanation": "El servidor no tiene ningún recurso en esta URL.",
|
||||
"cause1": "Enlace interno/externo roto",
|
||||
"cause2": "La página se eliminó sin redirección",
|
||||
"cause3": "Errata en el href de un enlace",
|
||||
"todo1": "Corrige o quita las páginas que enlazan: mira la pestaña Enlaces entrantes para ver quién enlaza aquí"
|
||||
},
|
||||
"408": {
|
||||
"name": "Tiempo de espera de la petición",
|
||||
"explanation": "El servidor agotó el tiempo esperando a que se completara la petición.",
|
||||
"cause1": "Red lenta entre el rastreador y el servidor",
|
||||
"cause2": "El tiempo de espera de conexión del servidor es muy bajo",
|
||||
"todo1": "Suele ser transitorio: sube los Intentos de reintento (Ajustes → Velocidad)"
|
||||
},
|
||||
"410": {
|
||||
"name": "Eliminado",
|
||||
"explanation": "El recurso se eliminó de forma intencionada y permanente; es una señal más fuerte que un 404.",
|
||||
"cause1": "Página retirada a propósito; el servidor indica a los rastreadores que la descarten",
|
||||
"todo1": "Quita los enlaces a esta URL: Google la desindexará más rápido que un 404"
|
||||
},
|
||||
"429": {
|
||||
"name": "Demasiadas peticiones (límite de velocidad)",
|
||||
"explanation": "El servidor está limitando la velocidad del rastreador: demasiadas peticiones en muy poco tiempo desde esta IP/User-Agent.",
|
||||
"cause1": "La velocidad del rastreo supera el límite por cliente del servidor",
|
||||
"cause2": "Alojamiento compartido con limitación agresiva",
|
||||
"todo1": "Reduce Concurrencia máxima / URL/s máximas y añade un Retardo por worker (Ajustes → Velocidad)",
|
||||
"todo2": "Respeta la cabecera Retry-After: mira \"De las cabeceras de respuesta\" más abajo si el servidor la envió",
|
||||
"todo3": "Activa \"Respetar el Crawl-delay de robots.txt\" (Ajustes → Rastreador)"
|
||||
},
|
||||
"451": {
|
||||
"name": "No disponible por motivos legales",
|
||||
"explanation": "El recurso está bloqueado por motivos legales (orden judicial, geobloqueo por RGPD, DMCA, etc.).",
|
||||
"cause1": "Contenido con restricción geográfica",
|
||||
"cause2": "Retirada obligatoria por ley",
|
||||
"todo1": "Nada: el bloqueo es intencionado"
|
||||
},
|
||||
"500": {
|
||||
"name": "Error interno del servidor",
|
||||
"explanation": "Se produjo un error no controlado en la aplicación del servidor al generar la página.",
|
||||
"cause1": "Error en la plantilla de la página / código del backend",
|
||||
"cause2": "Base de datos no disponible",
|
||||
"cause3": "Error de configuración en el servidor",
|
||||
"todo1": "Reintenta después del rastreo (los 500 transitorios desaparecen; los persistentes son un error real)",
|
||||
"todo2": "Si los 500 aparecen bajo carga, el rastreo puede estar desbordando una base de datos: ralentiza el rastreo (Ajustes → Velocidad)"
|
||||
},
|
||||
"502": {
|
||||
"name": "Puerta de enlace incorrecta",
|
||||
"explanation": "Un proxy inverso / balanceador de carga delante de la aplicación recibió una respuesta no válida del servidor de aplicaciones upstream.",
|
||||
"cause1": "El servidor de aplicaciones upstream falló o devolvió basura",
|
||||
"cause2": "El upstream está sobrecargado: habitual bajo la carga del rastreo",
|
||||
"cause3": "Tiempo de espera proxy↔upstream",
|
||||
"todo1": "Reduce la velocidad del rastreo (Ajustes → Velocidad) si los 502 aparecen en ráfagas",
|
||||
"todo2": "Reintenta las URL afectadas después del rastreo"
|
||||
},
|
||||
"503": {
|
||||
"name": "Servicio no disponible",
|
||||
"explanation": "El servidor no puede atender la petición temporalmente, normalmente por sobrecarga o mantenimiento. A menudo llega con una cabecera Retry-After.",
|
||||
"cause1": "Servidor sobrecargado: el propio rastreo puede causarlo",
|
||||
"cause2": "Ventana de mantenimiento programada",
|
||||
"cause3": "Retraso del autoescalado (se están levantando instancias nuevas)",
|
||||
"todo1": "Reduce Concurrencia máxima / URL/s máximas y añade un Retardo por worker (Ajustes → Velocidad)",
|
||||
"todo2": "Respeta Retry-After (mira \"De las cabeceras de respuesta\" más abajo)",
|
||||
"todo3": "Reintenta las URL afectadas después del rastreo"
|
||||
},
|
||||
"504": {
|
||||
"name": "Tiempo de espera de la puerta de enlace",
|
||||
"explanation": "Un proxy inverso / balanceador de carga no recibió a tiempo la respuesta del servidor de aplicaciones upstream.",
|
||||
"cause1": "Consulta lenta a la base de datos / llamada a una API externa en la página",
|
||||
"cause2": "Upstream sobrecargado: las peticiones hacen cola",
|
||||
"cause3": "Bajo la carga del rastreo: el rastreo saturó el pool de workers del upstream",
|
||||
"todo1": "Reduce la velocidad del rastreo (Ajustes → Velocidad)",
|
||||
"todo2": "Aumenta el Tiempo de espera de respuesta (Ajustes → Rastreador) para las páginas que de verdad son lentas",
|
||||
"todo3": "Reintenta las URL afectadas después del rastreo"
|
||||
},
|
||||
"508": {
|
||||
"name": "Bucle detectado",
|
||||
"explanation": "El servidor detectó un bucle infinito al procesar la petición (a menudo WebDAV o un bucle de redirección en el servidor).",
|
||||
"cause1": "Bucle de redirección en el servidor",
|
||||
"cause2": "Bucle de include recursivo / enlace simbólico",
|
||||
"todo1": "Error del servidor: el rastreador no puede hacer nada"
|
||||
},
|
||||
"509": {
|
||||
"name": "Límite de ancho de banda superado",
|
||||
"explanation": "Un código no estándar (cPanel/Apache) que indica que la cuenta de alojamiento alcanzó su límite mensual de ancho de banda.",
|
||||
"cause1": "Cuota de ancho de banda del alojamiento compartido agotada",
|
||||
"todo1": "El rastreador no puede hacer nada: el proveedor está limitando toda la cuenta"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Detrás de Cloudflare: cf-ray {{ray}}. El código de estado describe el enlace Cloudflare↔origen, no lo que ve un navegador.",
|
||||
"fronted": "Servido a través de {{front}}.",
|
||||
"origin": "Servidor de origen: {{server}}.",
|
||||
"retryAfterSeconds": "El servidor envió Retry-After: {{seconds}} s; pide explícitamente a los clientes que esperen ese tiempo. Un rastreo respetuoso no debería volver a pedir esta URL antes.",
|
||||
"retryAfter": "El servidor envió Retry-After: {{value}}.",
|
||||
"cfCache": "Estado de caché de Cloudflare: {{status}}.",
|
||||
"xRobots": "X-Robots-Tag: {{value}}: afecta a la indexabilidad sea cual sea el estado."
|
||||
},
|
||||
"robots": {
|
||||
"title": "No solicitada: bloqueada por robots.txt",
|
||||
"explanation": "El rastreador nunca envió una petición para esta URL: una regla de robots.txt para el user-agent configurado la bloquea. No hay código de estado porque nunca se pidió una respuesta; no es un fallo de conexión.",
|
||||
"cause1": "Una regla Disallow del robots.txt del sitio coincide con esta ruta para el user-agent del rastreo",
|
||||
"cause2": "La URL está enlazada desde páginas rastreadas, así que se registra y se cuenta, pero nunca se descarga",
|
||||
"todo1": "Nada, si la exclusión es intencionada: Google tampoco la rastreará",
|
||||
"todo2": "Comprueba si la página debería ser indexable: una URL bloqueada no transmite autoridad de enlaces y su contenido nunca se ve",
|
||||
"todo3": "Usa Ayuda → Probador de robots.txt para ver qué regla coincide, o desactiva \"Respetar robots.txt\" en Ajustes → Rastreador para auditarla igualmente"
|
||||
},
|
||||
"network": {
|
||||
"title": "Sin respuesta HTTP: error de red / conexión",
|
||||
"explanationDetail": "La petición nunca recibió una respuesta HTTP. El rastreador registró: \"{{detail}}\".",
|
||||
"explanation": "La petición nunca recibió una respuesta HTTP: la búsqueda DNS, la conexión TCP, el handshake TLS o la lectura de la respuesta fallaron antes de que llegara una línea de estado.",
|
||||
"cause1": "El DNS no pudo resolver el nombre de host (errata, dominio inactivo o un nombre que solo se resuelve dentro de una red privada)",
|
||||
"cause2": "Conexión TCP rechazada o agotada (host caído, firewall, puerto incorrecto)",
|
||||
"cause3": "Falló el handshake TLS (certificado caducado/no válido, protocolo incompatible)",
|
||||
"cause4": "Conexión restablecida o tiempo de lectura agotado a mitad de la respuesta",
|
||||
"cause5": "El rastreador canceló la petición porque superó el Tiempo de espera de respuesta (Ajustes → Rastreador) o el Tiempo de respuesta máximo (Ajustes → Avanzado)",
|
||||
"todo1": "Comprueba que el nombre de host se resuelve y que el sitio es accesible desde un navegador normal en este equipo",
|
||||
"todo2": "Si es accesible pero lento, aumenta el Tiempo de espera de respuesta (Ajustes → Rastreador)",
|
||||
"todo3": "Si solo falla bajo carga, reduce Concurrencia máxima / URL/s máximas (Ajustes → Velocidad): agotar la tabla de conexiones del origen parece un error de red para el cliente",
|
||||
"todo4": "Reintenta las URL afectadas después del rastreo (los restablecimientos transitorios se resuelven al reintentar)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — Informativo",
|
||||
"fallback": "Informativo",
|
||||
"explanation": "Una respuesta 1xx ({{text}}). Son respuestas provisionales; el rastreador las trata como transitorias y espera el estado final."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "Correcto",
|
||||
"explanation": "La petición se completó correctamente. No hace falta diagnóstico: esta sección solo amplía los códigos de error y de redirección."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "Redirección",
|
||||
"explanation301": "Redirección permanente. Los buscadores transfieren las señales de posicionamiento al destino y actualizan su índice con la nueva URL.",
|
||||
"explanation302": "Redirección temporal. Los buscadores mantienen indexada la URL original. Usar un 302 a largo plazo es un error SEO habitual: normalmente debería ser un 301.",
|
||||
"explanation308": "Redirección permanente (como un 301, pero se conserva el método de la petición).",
|
||||
"explanation304": "No modificado: la copia en caché sigue vigente. El rastreador normalmente pide sin cabeceras condicionales, así que esto es inusual.",
|
||||
"explanationOther": "Una redirección {{code}} ({{text}}).",
|
||||
"location": "La cabecera Location apunta a: {{location}}",
|
||||
"noLocation": "No se capturó ninguna cabecera Location: inusual en una redirección.",
|
||||
"todoPermanent": "Si esta redirección es permanente, cámbiala a 301/308 para que se consoliden las señales de posicionamiento",
|
||||
"todoChain": "Mira la pestaña Códigos de respuesta para ver la cadena de redirecciones completa y su destino final"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "Error del cliente",
|
||||
"explanation": "Un código 4xx significa que el servidor rechazó la petición como error del cliente (petición incorrecta, no permitida, no encontrada, etc.). En un rastreo, un 4xx persistente en páginas enlazadas suele indicar enlaces rotos; un 4xx esporádico puede ser protección antibots.",
|
||||
"cause1": "Enlace roto o malformado",
|
||||
"cause2": "Protección antibots / regla de WAF",
|
||||
"cause3": "Método o contenido que el servidor rechaza",
|
||||
"todo1": "Mira la pestaña Enlaces entrantes para ver qué enlaza a esta URL",
|
||||
"todo2": "Si parece un bloqueo, prueba un User-Agent de navegador y una velocidad menor (Ajustes → Peticiones / Velocidad)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "Error del servidor",
|
||||
"explanation": "Un código 5xx significa que el servidor no pudo completar una petición que aceptó como válida. Bajo la carga del rastreo, a menudo es el rastreo desbordando el origen más que un error real de la página.",
|
||||
"cause1": "Error o fallo en el servidor",
|
||||
"cause2": "Origen sobrecargado, posiblemente por el propio rastreo",
|
||||
"cause3": "Fallo del upstream/proxy",
|
||||
"todo1": "Reduce Concurrencia máxima / URL/s máximas y añade un Retardo por worker (Ajustes → Velocidad) si los 5xx aparecen en ráfagas",
|
||||
"todo2": "Sube los Intentos de reintento (Ajustes → Velocidad) y vuelve a rastrear las URL afectadas"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "Esta página no tiene encabezados detectados (ningún elemento",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL",
|
||||
"percentOfTotal": "% du total",
|
||||
"siteSoft404": "Les pages inexistantes renvoient {{status}} (soft 404)",
|
||||
"siteSoft404Hint": "Une requête vers une page qui n'existe pas a reçu {{status}} au lieu de 404 : les liens cassés de ce site ressemblent donc à des pages valides pour les moteurs de recherche."
|
||||
"siteSoft404Hint": "Une requête vers une page qui n'existe pas a reçu {{status}} au lieu de 404 : les liens cassés de ce site ressemblent donc à des pages valides pour les moteurs de recherche.",
|
||||
"siteBotBlocked": "La protection anti-bots a bloqué une partie de l'exploration",
|
||||
"siteBotBlockedHint": "La protection anti-bots du site a répondu par une vérification au lieu de ces pages. Elles n'ont pas été explorées et sont exclues des contrôles de liens cassés et de sitemap. Réduisez la vitesse d'exploration ou autorisez le user agent du robot, puis explorez-les à nouveau."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "Connectez des services externes avec vos propres identifiants. Les clés d'API et les secrets sont chiffrés au repos via le stockage sécurisé de votre système d'exploitation et ne quittent jamais cette machine.",
|
||||
@@ -406,7 +408,58 @@
|
||||
"orphanMetricaVisits_other": "{{formatted}} visites Metrica (entrée)",
|
||||
"orphanMetricaPageviews_one": "{{formatted}} page vue Metrica (hits)",
|
||||
"orphanMetricaPageviews_many": "{{formatted}} de pages vues Metrica (hits)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} pages vues Metrica (hits)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} pages vues Metrica (hits)",
|
||||
"noResponse": "Aucune réponse",
|
||||
"bucketOrphan": "0 (orpheline)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "NON",
|
||||
"warn": "ALERTE",
|
||||
"bad": "MAUVAIS",
|
||||
"err": "ERREUR",
|
||||
"slow": "LENT",
|
||||
"net": "RÉSEAU"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} OK · {{rate}} % d’erreurs",
|
||||
"avgMs": "moy. {{ms}} ms",
|
||||
"distinctIds_one": "{{count}} ID distinct",
|
||||
"distinctIds_many": "{{count}} ID distincts",
|
||||
"distinctIds_other": "{{count}} ID distincts",
|
||||
"pageCount_one": "{{formatted}} page",
|
||||
"pageCount_many": "{{formatted}} pages",
|
||||
"pageCount_other": "{{formatted}} pages",
|
||||
"imageCount_one": "{{formatted}} image",
|
||||
"imageCount_many": "{{formatted}} images",
|
||||
"imageCount_other": "{{formatted}} images",
|
||||
"hits_one": "{{formatted}} occurrence",
|
||||
"hits_many": "{{formatted}} occurrences",
|
||||
"hits_other": "{{formatted}} occurrences",
|
||||
"avgWords": "moy. {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "depuis {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "Navigation",
|
||||
"header": "En-tête",
|
||||
"content": "Contenu",
|
||||
"sidebar": "Barre latérale",
|
||||
"footer": "Pied de page",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "Indexable",
|
||||
"noindex": "Non indexable — noindex",
|
||||
"canonical": "Non indexable — canonicalisée",
|
||||
"robotsBlocked": "Non indexable — bloquée par robots.txt",
|
||||
"redirect": "Non indexable — redirection",
|
||||
"clientError": "Non indexable — 4xx",
|
||||
"serverError": "Non indexable — 5xx",
|
||||
"botBlocked": "Inconnue — bloquée par la protection anti-bots"
|
||||
},
|
||||
"kind": {
|
||||
"image": "Image",
|
||||
"font": "Police",
|
||||
"other": "Autre"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "Validateur de sitemap",
|
||||
@@ -1070,7 +1123,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "D'après les en-têtes de réponse",
|
||||
"likelyCauses": "Causes probables",
|
||||
"whatToDo": "Que faire"
|
||||
"whatToDo": "Que faire",
|
||||
"cloudflareStyle": "code edge de type Cloudflare",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "Le serveur web a renvoyé une erreur inconnue",
|
||||
"explanation": "Cloudflare s'est connecté au serveur d'origine mais a reçu une réponse vide, malformée ou inattendue (par exemple, la connexion a été coupée en pleine réponse, l'origine a renvoyé des en-têtes trop volumineux ou le processus de l'origine a planté). C'est un code fourre-tout pour « quelque chose s'est mal passé à l'origine ».",
|
||||
"cause1": "Le serveur d'origine a planté, redémarré ou manqué de workers/mémoire en traitant la requête",
|
||||
"cause2": "L'origine a fermé la connexion TCP avant d'envoyer une réponse complète (souvent un délai d'attente applicatif ou un arrêt pour manque de mémoire)",
|
||||
"cause3": "Le débit de requêtes de l'exploration a submergé le pool de processus de l'origine : des 520 en rafales sous charge signifient presque toujours cela",
|
||||
"cause4": "L'origine a renvoyé des en-têtes de réponse plus volumineux que ce que Cloudflare autorise (rare ; généralement une application mal configurée)",
|
||||
"todo1": "Si les 520 apparaissent en rafales sur de nombreuses URL, baissez Concurrence maximale / URL/s maximum et ajoutez un Délai par worker (Paramètres → Vitesse) : l'exploration surcharge l'origine",
|
||||
"todo2": "Explorez à nouveau les URL concernées une fois l'exploration terminée (les 520 passagers disparaissent souvent au nouvel essai ; augmentez le Nombre de tentatives dans Paramètres → Vitesse)",
|
||||
"todo3": "Si seules certaines URL renvoient systématiquement 520, ces pages ont un bug côté serveur : signalez-les au propriétaire du site"
|
||||
},
|
||||
"521": {
|
||||
"name": "Le serveur web est hors service",
|
||||
"explanation": "Cloudflare n'a pas pu ouvrir de connexion TCP vers le serveur d'origine : l'origine a activement refusé la connexion ou n'écoute pas sur le port attendu.",
|
||||
"cause1": "Le serveur web d'origine (nginx/Apache/Node, etc.) est arrêté ou a planté",
|
||||
"cause2": "Le pare-feu de l'origine bloque les plages d'IP de Cloudflare (une erreur de configuration : l'origine devrait autoriser Cloudflare)",
|
||||
"cause3": "Le serveur d'origine est surchargé au point de ne plus accepter de nouvelles connexions",
|
||||
"todo1": "C'est un problème de disponibilité de l'origine, pas quelque chose que le robot d'exploration peut contourner : le site est de fait hors service pour ces URL",
|
||||
"todo2": "Explorez à nouveau plus tard ; si cela persiste, le site est cassé"
|
||||
},
|
||||
"522": {
|
||||
"name": "Délai de connexion dépassé",
|
||||
"explanation": "Cloudflare a commencé une poignée de main TCP avec l'origine, mais elle ne s'est pas terminée dans le délai imparti : l'origine a accepté le SYN sans jamais répondre, ou un équipement réseau intermédiaire a abandonné les paquets.",
|
||||
"cause1": "L'origine est fortement surchargée (file TCP pleine)",
|
||||
"cause2": "Le pare-feu de l'origine abandonne silencieusement (sans les rejeter) les paquets de Cloudflare",
|
||||
"cause3": "Problème de réseau/routage entre Cloudflare et l'origine",
|
||||
"cause4": "Sous la charge de l'exploration : les requêtes parallèles de l'exploration ont épuisé la table de connexions de l'origine",
|
||||
"todo1": "Baissez la concurrence (Paramètres → Vitesse → Concurrence maximale) et explorez à nouveau : l'épuisement de la table de connexions est la cause habituelle quand cela survient en cours d'exploration",
|
||||
"todo2": "Réessayez les URL concernées après l'exploration"
|
||||
},
|
||||
"523": {
|
||||
"name": "L'origine est injoignable",
|
||||
"explanation": "Cloudflare n'a pas du tout pu joindre le serveur d'origine : généralement un problème DNS dans l'enregistrement d'origine chez Cloudflare, ou le réseau de l'origine est hors service.",
|
||||
"cause1": "L'enregistrement DNS du site chez Cloudflare pointe vers une IP erronée / inactive",
|
||||
"cause2": "L'hébergeur de l'origine subit une panne réseau",
|
||||
"cause3": "Défaillance BGP / de routage entre Cloudflare et l'origine",
|
||||
"todo1": "Le robot d'exploration n'y peut rien : c'est une erreur de configuration DNS/réseau de l'origine"
|
||||
},
|
||||
"524": {
|
||||
"name": "Un délai d'attente a expiré",
|
||||
"explanation": "Cloudflare a bien établi une connexion TCP avec l'origine, mais l'origine n'a pas envoyé de réponse HTTP complète dans le délai de Cloudflare (100 s par défaut). L'origine fonctionne, mais elle est trop lente pour cette requête.",
|
||||
"cause1": "Une requête de base de données lente, un appel à une API externe ou un calcul lourd sur la page",
|
||||
"cause2": "L'origine est surchargée : les requêtes s'accumulent derrière un pool de workers saturé",
|
||||
"cause3": "Sous la charge de l'exploration : l'exploration a rempli la file de requêtes de l'origine et cette URL a attendu trop longtemps",
|
||||
"todo1": "Baissez Concurrence maximale / URL/s maximum et ajoutez un Délai par worker (Paramètres → Vitesse) pour ne pas saturer l'origine",
|
||||
"todo2": "Augmentez le Délai d'attente de réponse (Paramètres → Explorateur) si vous voulez laisser plus de temps aux pages réellement lentes",
|
||||
"todo3": "Réessayez les URL concernées après l'exploration"
|
||||
},
|
||||
"525": {
|
||||
"name": "Échec de la poignée de main SSL",
|
||||
"explanation": "La poignée de main TLS entre Cloudflare et le serveur d'origine a échoué. Il s'agit du tronçon Cloudflare↔origine, pas du tronçon navigateur↔Cloudflare visible dans la barre d'adresse : le site peut donc sembler normal pour un visiteur et renvoyer quand même 525.",
|
||||
"cause1": "Le certificat TLS de l'origine est expiré, auto-signé ou émis pour un autre nom d'hôte (avec Cloudflare en mode 'Full (strict)')",
|
||||
"cause2": "L'origine ne prend en charge que d'anciennes versions de TLS / suites de chiffrement que Cloudflare refuse",
|
||||
"cause3": "Le serveur d'origine est surchargé et abandonne des poignées de main TLS",
|
||||
"todo1": "Erreur de configuration TLS de l'origine : le robot d'exploration ne peut pas la modifier. Vérifiez le certificat de l'hôte (les lignes TLS Cert dans Détails de l’URL, pour HTTPS)"
|
||||
},
|
||||
"526": {
|
||||
"name": "Certificat SSL non valide",
|
||||
"explanation": "Cloudflare (en mode SSL 'Full (strict)') n'a pas pu valider le certificat TLS du serveur d'origine.",
|
||||
"cause1": "Le certificat de l'origine est expiré, auto-signé ou émis pour un autre nom d'hôte",
|
||||
"cause2": "La chaîne de certificats de l'origine est incomplète (certificats intermédiaires manquants)",
|
||||
"todo1": "Problème de certificat de l'origine : le robot d'exploration n'y peut rien"
|
||||
},
|
||||
"527": {
|
||||
"name": "Erreur Railgun (Cloudflare, obsolète)",
|
||||
"explanation": "Une erreur de connexion dans Cloudflare Railgun (un produit d'accélération de l'origine désormais retiré). Pratiquement obsolète ; si vous le voyez, le site utilise une configuration Cloudflare très ancienne.",
|
||||
"cause1": "Échec de connexion de l'ancien Cloudflare Railgun",
|
||||
"todo1": "Rien à faire du côté du robot d'exploration"
|
||||
},
|
||||
"530": {
|
||||
"name": "Erreur Cloudflare (généralement un blocage pare-feu / WAF — erreur 1020)",
|
||||
"explanation": "Un 530 est la façon dont Cloudflare dit « une erreur s'est produite, voir le code 1xxx dans le corps ». Lors d'une exploration, il s'agit presque toujours de 1020 « Access Denied » : une règle Cloudflare Firewall / WAF / Bot-Fight a bloqué la requête. Plus rarement, il accompagne 1016 (erreur DNS de l'origine) ou d'autres codes 1xxx.",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode a signalé le robot d'exploration : par IP, par User-Agent, par débit de requêtes ou par absence d'empreintes de navigateur",
|
||||
"cause2": "Le site utilise un « managed challenge » que le robot d'exploration ne peut pas résoudre",
|
||||
"cause3": "(Rarement) une erreur DNS de l'origine (1016) plutôt qu'un blocage",
|
||||
"todo1": "Définissez un User-Agent de navigateur (Paramètres → Requêtes) : l'UA par défaut du robot est le déclencheur n° 1",
|
||||
"todo2": "Baissez Concurrence maximale / URL/s maximum et ajoutez un Délai par worker (Paramètres → Vitesse) : des requêtes rapides depuis une seule IP déclenchent les règles basées sur le débit",
|
||||
"todo3": "Respectez robots.txt et tout Crawl-delay déclaré (Paramètres → Explorateur)",
|
||||
"todo4": "Si le site vous appartient, mettez le robot d'exploration sur liste blanche dans Cloudflare ; sinon, le site bloque volontairement l'accès automatisé"
|
||||
},
|
||||
"401": {
|
||||
"name": "Non autorisé",
|
||||
"explanation": "La ressource exige une authentification et aucun identifiant (ou un identifiant non valide) n'a été envoyé.",
|
||||
"cause1": "La page est protégée par une authentification HTTP Basic/Bearer",
|
||||
"cause2": "Un cookie de session est requis et aucun n'a été envoyé",
|
||||
"todo1": "Configurez les identifiants dans Paramètres → Authentification si vous en disposez",
|
||||
"todo2": "Utilisez Paramètres → Cookies → « Tout accepter » si le site réserve son contenu à une session"
|
||||
},
|
||||
"403": {
|
||||
"name": "Interdit",
|
||||
"explanation": "Le serveur a compris la requête mais refuse de la servir. Lors d'une exploration, il s'agit le plus souvent d'un blocage de robots ou d'une règle WAF plutôt que d'un vrai problème de permissions.",
|
||||
"cause1": "Règle WAF / de protection anti-bots déclenchée par le User-Agent, l'IP ou le débit de requêtes du robot d'exploration",
|
||||
"cause2": "Répertoire ou fichier réellement inaccessible aux utilisateurs anonymes",
|
||||
"cause3": "Protection contre le hotlinking / le referer",
|
||||
"todo1": "Essayez un User-Agent de navigateur (Paramètres → Requêtes)",
|
||||
"todo2": "Réduisez la vitesse d'exploration (Paramètres → Vitesse)",
|
||||
"todo3": "Si cela touche tout le site, le site bloque activement l'accès automatisé"
|
||||
},
|
||||
"404": {
|
||||
"name": "Introuvable",
|
||||
"explanation": "Le serveur n'a aucune ressource à cette URL.",
|
||||
"cause1": "Lien interne/externe cassé",
|
||||
"cause2": "La page a été supprimée sans redirection",
|
||||
"cause3": "Faute de frappe dans le href d'un lien",
|
||||
"todo1": "Corrigez ou supprimez les liens des pages concernées : l'onglet Liens entrants montre qui pointe ici"
|
||||
},
|
||||
"408": {
|
||||
"name": "Délai d'attente de la requête dépassé",
|
||||
"explanation": "Le serveur a dépassé son délai d'attente en attendant la fin de la requête.",
|
||||
"cause1": "Réseau lent entre le robot d'exploration et le serveur",
|
||||
"cause2": "Délai d'attente de connexion du serveur réglé très bas",
|
||||
"todo1": "Généralement passager : augmentez le Nombre de tentatives (Paramètres → Vitesse)"
|
||||
},
|
||||
"410": {
|
||||
"name": "Supprimé",
|
||||
"explanation": "La ressource a été supprimée volontairement et définitivement — un signal plus fort qu'un 404.",
|
||||
"cause1": "Page retirée volontairement ; le serveur demande aux robots de l'abandonner",
|
||||
"todo1": "Supprimez les liens vers cette URL : Google la désindexera plus vite qu'un 404"
|
||||
},
|
||||
"429": {
|
||||
"name": "Trop de requêtes (limitation de débit)",
|
||||
"explanation": "Le serveur limite le débit du robot d'exploration : trop de requêtes en trop peu de temps depuis cette IP/ce User-Agent.",
|
||||
"cause1": "Le débit d'exploration dépasse la limite par client du serveur",
|
||||
"cause2": "Hébergement mutualisé avec une limitation agressive",
|
||||
"todo1": "Baissez Concurrence maximale / URL/s maximum et ajoutez un Délai par worker (Paramètres → Vitesse)",
|
||||
"todo2": "Respectez l'en-tête Retry-After : voyez « D'après les en-têtes de réponse » ci-dessous si le serveur en a envoyé un",
|
||||
"todo3": "Activez « Respecter le Crawl-delay de robots.txt » (Paramètres → Explorateur)"
|
||||
},
|
||||
"451": {
|
||||
"name": "Indisponible pour des raisons légales",
|
||||
"explanation": "La ressource est bloquée pour des raisons légales (décision de justice, géoblocage RGPD, DMCA, etc.).",
|
||||
"cause1": "Contenu géo-restreint",
|
||||
"cause2": "Retrait imposé par la loi",
|
||||
"todo1": "Rien à faire : le blocage est volontaire"
|
||||
},
|
||||
"500": {
|
||||
"name": "Erreur interne du serveur",
|
||||
"explanation": "Une erreur non gérée s'est produite dans l'application côté serveur pendant la génération de la page.",
|
||||
"cause1": "Bug dans le modèle de page / le code du backend",
|
||||
"cause2": "Base de données indisponible",
|
||||
"cause3": "Erreur de configuration sur le serveur",
|
||||
"todo1": "Réessayez après l'exploration (les 500 passagers disparaissent ; les persistants sont un vrai bug)",
|
||||
"todo2": "Si les 500 apparaissent sous charge, l'exploration surcharge peut-être une base de données : ralentissez l'exploration (Paramètres → Vitesse)"
|
||||
},
|
||||
"502": {
|
||||
"name": "Passerelle incorrecte",
|
||||
"explanation": "Un proxy inverse / répartiteur de charge placé devant l'application a reçu une réponse non valide du serveur d'application en amont.",
|
||||
"cause1": "Le serveur d'application en amont a planté ou renvoyé des données incohérentes",
|
||||
"cause2": "Le serveur en amont est surchargé — fréquent sous la charge de l'exploration",
|
||||
"cause3": "Délai d'attente proxy↔amont",
|
||||
"todo1": "Réduisez la vitesse d'exploration (Paramètres → Vitesse) si les 502 apparaissent en rafales",
|
||||
"todo2": "Réessayez les URL concernées après l'exploration"
|
||||
},
|
||||
"503": {
|
||||
"name": "Service indisponible",
|
||||
"explanation": "Le serveur ne peut temporairement pas traiter la requête — généralement une surcharge ou une maintenance. S'accompagne souvent d'un en-tête Retry-After.",
|
||||
"cause1": "Serveur surchargé — l'exploration elle-même peut en être la cause",
|
||||
"cause2": "Fenêtre de maintenance planifiée",
|
||||
"cause3": "Retard de mise à l'échelle automatique (de nouvelles instances démarrent)",
|
||||
"todo1": "Baissez Concurrence maximale / URL/s maximum et ajoutez un Délai par worker (Paramètres → Vitesse)",
|
||||
"todo2": "Respectez Retry-After (voir « D'après les en-têtes de réponse » ci-dessous)",
|
||||
"todo3": "Réessayez les URL concernées après l'exploration"
|
||||
},
|
||||
"504": {
|
||||
"name": "Délai d'attente de la passerelle dépassé",
|
||||
"explanation": "Un proxy inverse / répartiteur de charge n'a pas reçu à temps la réponse du serveur d'application en amont.",
|
||||
"cause1": "Requête de base de données lente / appel à une API externe sur la page",
|
||||
"cause2": "Serveur en amont surchargé — les requêtes s'accumulent",
|
||||
"cause3": "Sous la charge de l'exploration : l'exploration a saturé le pool de workers en amont",
|
||||
"todo1": "Réduisez la vitesse d'exploration (Paramètres → Vitesse)",
|
||||
"todo2": "Augmentez le Délai d'attente de réponse (Paramètres → Explorateur) pour les pages réellement lentes",
|
||||
"todo3": "Réessayez les URL concernées après l'exploration"
|
||||
},
|
||||
"508": {
|
||||
"name": "Boucle détectée",
|
||||
"explanation": "Le serveur a détecté une boucle infinie en traitant la requête (souvent WebDAV ou une boucle de redirection côté serveur).",
|
||||
"cause1": "Boucle de redirection côté serveur",
|
||||
"cause2": "Boucle d'include récursif / de lien symbolique",
|
||||
"todo1": "Bug du serveur : le robot d'exploration n'y peut rien"
|
||||
},
|
||||
"509": {
|
||||
"name": "Limite de bande passante dépassée",
|
||||
"explanation": "Un code non standard (cPanel/Apache) signifiant que le compte d'hébergement a atteint son plafond mensuel de bande passante.",
|
||||
"cause1": "Quota de bande passante de l'hébergement mutualisé épuisé",
|
||||
"todo1": "Le robot d'exploration n'y peut rien : l'hébergeur bride tout le compte"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Derrière Cloudflare — cf-ray {{ray}}. Le code d'état décrit la liaison Cloudflare↔origine, pas ce que voit un navigateur.",
|
||||
"fronted": "Servi via {{front}}.",
|
||||
"origin": "Serveur d'origine : {{server}}.",
|
||||
"retryAfterSeconds": "Le serveur a envoyé Retry-After : {{seconds}} s — il demande explicitement aux clients d'attendre ce délai. Une exploration respectueuse ne devrait pas redemander cette URL plus tôt.",
|
||||
"retryAfter": "Le serveur a envoyé Retry-After : {{value}}.",
|
||||
"cfCache": "État du cache Cloudflare : {{status}}.",
|
||||
"xRobots": "X-Robots-Tag : {{value}} — influe sur l'indexabilité quel que soit le code d'état."
|
||||
},
|
||||
"robots": {
|
||||
"title": "Non demandée — interdite par robots.txt",
|
||||
"explanation": "Le robot d'exploration n'a jamais envoyé de requête pour cette URL : une règle robots.txt pour le user-agent configuré l'interdit. Il n'y a pas de code d'état car aucune réponse n'a jamais été demandée — ce n'est pas un échec de connexion.",
|
||||
"cause1": "Une règle Disallow du robots.txt du site correspond à ce chemin pour le user-agent de l'exploration",
|
||||
"cause2": "L'URL est liée depuis des pages explorées : elle est donc enregistrée et comptée, mais jamais récupérée",
|
||||
"todo1": "Rien à faire si l'exclusion est voulue — Google ne l'explorera pas non plus",
|
||||
"todo2": "Vérifiez si la page devrait être indexable : une URL interdite ne transmet pas de popularité via ses liens et son contenu n'est jamais vu",
|
||||
"todo3": "Utilisez Aide → Testeur de robots.txt pour voir quelle règle correspond, ou désactivez « Respecter robots.txt » dans Paramètres → Explorateur pour l'auditer quand même"
|
||||
},
|
||||
"network": {
|
||||
"title": "Aucune réponse HTTP — erreur réseau / de connexion",
|
||||
"explanationDetail": "La requête n'a jamais reçu de réponse HTTP. Le robot d'exploration a enregistré : « {{detail}} ».",
|
||||
"explanation": "La requête n'a jamais reçu de réponse HTTP : la résolution DNS, la connexion TCP, la poignée de main TLS ou la lecture de la réponse a échoué avant l'arrivée d'une ligne d'état.",
|
||||
"cause1": "Le DNS n'a pas pu résoudre le nom d'hôte (faute de frappe, domaine inactif ou nom résolu uniquement au sein d'un réseau privé)",
|
||||
"cause2": "Connexion TCP refusée ou expirée (hôte hors service, pare-feu, mauvais port)",
|
||||
"cause3": "Échec de la poignée de main TLS (certificat expiré/non valide, protocole incompatible)",
|
||||
"cause4": "Connexion réinitialisée ou délai de lecture dépassé en pleine réponse",
|
||||
"cause5": "Requête interrompue par le robot d'exploration car elle a dépassé le Délai d'attente de réponse (Paramètres → Explorateur) ou le Temps de réponse max. (Paramètres → Avancé)",
|
||||
"todo1": "Vérifiez que le nom d'hôte se résout et que le site est accessible depuis un navigateur normal sur cette machine",
|
||||
"todo2": "S'il est accessible mais lent, augmentez le Délai d'attente de réponse (Paramètres → Explorateur)",
|
||||
"todo3": "S'il n'échoue que sous charge, baissez Concurrence maximale / URL/s maximum (Paramètres → Vitesse) — l'épuisement de la table de connexions de l'origine ressemble à une erreur réseau côté client",
|
||||
"todo4": "Réessayez les URL concernées après l'exploration (les réinitialisations passagères disparaissent au nouvel essai)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — Informatif",
|
||||
"fallback": "Informatif",
|
||||
"explanation": "Une réponse 1xx ({{text}}). Ce sont des réponses intermédiaires ; le robot d'exploration les considère comme passagères et attend le code d'état final."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "Succès",
|
||||
"explanation": "La requête a abouti. Aucun diagnostic nécessaire — cette section ne détaille que les codes d'erreur et de redirection."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "Redirection",
|
||||
"explanation301": "Redirection permanente. Les moteurs de recherche transfèrent les signaux de classement vers la cible et mettent à jour leur index avec la nouvelle URL.",
|
||||
"explanation302": "Redirection temporaire. Les moteurs de recherche gardent l'URL d'origine dans l'index. Un 302 utilisé durablement est une erreur SEO courante — ce devrait généralement être un 301.",
|
||||
"explanation308": "Redirection permanente (comme un 301, mais la méthode de la requête est conservée).",
|
||||
"explanation304": "Non modifié — la copie en cache est toujours à jour. Le robot d'exploration envoie normalement ses requêtes sans en-têtes conditionnels, c'est donc inhabituel.",
|
||||
"explanationOther": "Une redirection {{code}} ({{text}}).",
|
||||
"location": "L'en-tête Location pointe vers : {{location}}",
|
||||
"noLocation": "Aucun en-tête Location n'a été capturé — inhabituel pour une redirection.",
|
||||
"todoPermanent": "Si cette redirection est permanente, passez-la en 301/308 pour que les signaux de classement se consolident",
|
||||
"todoChain": "Consultez l'onglet Codes de réponse pour voir la chaîne de redirections complète et sa cible finale"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "Erreur client",
|
||||
"explanation": "Un code 4xx signifie que le serveur a rejeté la requête comme une faute du client (requête incorrecte, non autorisée, introuvable, etc.). Lors d'une exploration, un 4xx persistant sur des pages liées signale généralement des liens cassés ; un 4xx sporadique peut venir d'une protection anti-bots.",
|
||||
"cause1": "Lien cassé ou malformé",
|
||||
"cause2": "Protection anti-bots / règle WAF",
|
||||
"cause3": "Méthode ou contenu refusé par le serveur",
|
||||
"todo1": "Consultez l'onglet Liens entrants pour voir ce qui pointe vers cette URL",
|
||||
"todo2": "Si cela ressemble à un blocage, essayez un User-Agent de navigateur et un débit plus faible (Paramètres → Requêtes / Vitesse)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "Erreur serveur",
|
||||
"explanation": "Un code 5xx signifie que le serveur n'a pas pu traiter une requête qu'il avait acceptée comme valide. Sous la charge de l'exploration, c'est souvent l'exploration qui submerge l'origine plutôt qu'un vrai bug de page.",
|
||||
"cause1": "Erreur ou plantage côté serveur",
|
||||
"cause2": "Origine surchargée — peut-être par l'exploration elle-même",
|
||||
"cause3": "Défaillance en amont/du proxy",
|
||||
"todo1": "Baissez Concurrence maximale / URL/s maximum et ajoutez un Délai par worker (Paramètres → Vitesse) si les 5xx apparaissent en rafales",
|
||||
"todo2": "Augmentez le Nombre de tentatives (Paramètres → Vitesse) et explorez à nouveau les URL concernées"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "Cette page n'a aucun titre détecté (aucun élément",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL",
|
||||
"percentOfTotal": "कुल का %",
|
||||
"siteSoft404": "मौजूद न होने वाले पेज {{status}} लौटाते हैं (soft 404)",
|
||||
"siteSoft404Hint": "मौजूद न होने वाले पेज के अनुरोध का उत्तर 404 के बजाय {{status}} मिला, इसलिए इस साइट के टूटे लिंक सर्च इंजनों को काम करते पेज जैसे दिखते हैं।"
|
||||
"siteSoft404Hint": "मौजूद न होने वाले पेज के अनुरोध का उत्तर 404 के बजाय {{status}} मिला, इसलिए इस साइट के टूटे लिंक सर्च इंजनों को काम करते पेज जैसे दिखते हैं।",
|
||||
"siteBotBlocked": "बॉट सुरक्षा ने क्रॉल का एक हिस्सा रोक दिया",
|
||||
"siteBotBlockedHint": "साइट की बॉट सुरक्षा ने इन पेजों की जगह एक चुनौती पेज से उत्तर दिया। ये पेज क्रॉल नहीं हुए और टूटे लिंक व साइटमैप जाँचों से बाहर रखे गए हैं। क्रॉल की गति कम करें या क्रॉलर के यूज़र एजेंट को अनुमति सूची में जोड़ें, फिर इन्हें दोबारा क्रॉल करें।"
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "अपनी ही क्रेडेंशियल से बाहरी सेवाएँ जोड़ें। API कुंजियाँ और सीक्रेट आपके ऑपरेटिंग सिस्टम के सुरक्षित स्टोरेज से एन्क्रिप्ट होकर रखे जाते हैं और कभी इस मशीन से बाहर नहीं जाते।",
|
||||
@@ -394,7 +396,54 @@
|
||||
"orphanMetricaVisits_one": "{{formatted}} Metrica विज़िट (लैंडिंग)",
|
||||
"orphanMetricaVisits_other": "{{formatted}} Metrica विज़िट (लैंडिंग)",
|
||||
"orphanMetricaPageviews_one": "{{formatted}} Metrica पृष्ठ दृश्य (हिट)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} Metrica पृष्ठ दृश्य (हिट)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} Metrica पृष्ठ दृश्य (हिट)",
|
||||
"noResponse": "कोई प्रतिक्रिया नहीं",
|
||||
"bucketOrphan": "0 (अनाथ)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "नहीं",
|
||||
"warn": "चेतावनी",
|
||||
"bad": "खराब",
|
||||
"err": "त्रुटि",
|
||||
"slow": "धीमा",
|
||||
"net": "नेटवर्क"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} OK · {{rate}}% त्रुटियाँ",
|
||||
"avgMs": "औसत {{ms}} मि.से.",
|
||||
"distinctIds_one": "{{count}} अलग ID",
|
||||
"distinctIds_other": "{{count}} अलग ID",
|
||||
"pageCount_one": "{{formatted}} पेज",
|
||||
"pageCount_other": "{{formatted}} पेज",
|
||||
"imageCount_one": "{{formatted}} छवि",
|
||||
"imageCount_other": "{{formatted}} छवियाँ",
|
||||
"hits_one": "{{formatted}} बार",
|
||||
"hits_other": "{{formatted}} बार",
|
||||
"avgWords": "औसत {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "{{sitemap}} से",
|
||||
"position": {
|
||||
"navigation": "नेविगेशन",
|
||||
"header": "हेडर",
|
||||
"content": "सामग्री",
|
||||
"sidebar": "साइडबार",
|
||||
"footer": "फ़ुटर",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "अनुक्रमणीय",
|
||||
"noindex": "अनुक्रमणीय नहीं — noindex",
|
||||
"canonical": "अनुक्रमणीय नहीं — कैनोनिकलाइज़्ड",
|
||||
"robotsBlocked": "अनुक्रमणीय नहीं — robots.txt से अवरुद्ध",
|
||||
"redirect": "अनुक्रमणीय नहीं — रीडायरेक्ट",
|
||||
"clientError": "अनुक्रमणीय नहीं — 4xx",
|
||||
"serverError": "अनुक्रमणीय नहीं — 5xx",
|
||||
"botBlocked": "अज्ञात — बॉट सुरक्षा से अवरुद्ध"
|
||||
},
|
||||
"kind": {
|
||||
"image": "छवि",
|
||||
"font": "फ़ॉन्ट",
|
||||
"other": "अन्य"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "साइटमैप सत्यापक",
|
||||
@@ -1049,7 +1098,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "प्रतिक्रिया हेडर से",
|
||||
"likelyCauses": "संभावित कारण",
|
||||
"whatToDo": "क्या करें"
|
||||
"whatToDo": "क्या करें",
|
||||
"cloudflareStyle": "Cloudflare जैसा एज कोड",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "वेब सर्वर ने अज्ञात त्रुटि लौटाई",
|
||||
"explanation": "Cloudflare ओरिजिन सर्वर से जुड़ गया, लेकिन उसे खाली, बिगड़ी हुई या अप्रत्याशित प्रतिक्रिया मिली (जैसे प्रतिक्रिया के बीच में कनेक्शन रीसेट हो गया, ओरिजिन ने बहुत बड़े हेडर लौटाए या ओरिजिन प्रोसेस क्रैश हो गई)। यह 'ओरिजिन पर कुछ गड़बड़ हुई' बताने वाला एक सामान्य कोड है।",
|
||||
"cause1": "अनुरोध संभालते समय ओरिजिन सर्वर क्रैश हुआ, रीस्टार्ट हुआ या उसके वर्कर/मेमोरी खत्म हो गए",
|
||||
"cause2": "ओरिजिन ने पूरी प्रतिक्रिया भेजने से पहले TCP कनेक्शन बंद कर दिया (अक्सर ऐप-स्तर का टाइमआउट या मेमोरी खत्म होने पर प्रोसेस बंद होना)",
|
||||
"cause3": "क्रॉल की अनुरोध दर ने ओरिजिन के प्रोसेस पूल को दबा दिया — लोड में एक साथ दिखने वाले 520 का लगभग हमेशा यही मतलब होता है",
|
||||
"cause4": "ओरिजिन ने Cloudflare की अनुमति से बड़े प्रतिक्रिया हेडर लौटाए (दुर्लभ; आमतौर पर गलत कॉन्फ़िगर किया गया ऐप)",
|
||||
"todo1": "अगर कई URL पर 520 एक साथ दिखें, तो अधिकतम समानांतरता / अधिकतम URL/से कम करें और प्रति वर्कर विलंब जोड़ें (सेटिंग्स → गति) — क्रॉल ओरिजिन पर ज़्यादा बोझ डाल रहा है",
|
||||
"todo2": "क्रॉल पूरा होने के बाद प्रभावित URL दोबारा क्रॉल करें (अस्थायी 520 अक्सर पुनः प्रयास में ठीक हो जाते हैं — सेटिंग्स → गति में पुनः प्रयास बढ़ाएँ)",
|
||||
"todo3": "अगर केवल कुछ खास URL लगातार 520 दें, तो उन पेजों में सर्वर-साइड बग है — साइट मालिक को बताएँ"
|
||||
},
|
||||
"521": {
|
||||
"name": "वेब सर्वर बंद है",
|
||||
"explanation": "Cloudflare ओरिजिन सर्वर से TCP कनेक्शन नहीं खोल सका — ओरिजिन ने कनेक्शन सीधे अस्वीकार कर दिया या वह अपेक्षित पोर्ट पर नहीं सुन रहा है।",
|
||||
"cause1": "ओरिजिन वेब सर्वर (nginx/Apache/Node आदि) बंद है या क्रैश हो गया है",
|
||||
"cause2": "ओरिजिन का फ़ायरवॉल Cloudflare की IP रेंज को ब्लॉक कर रहा है (गलत कॉन्फ़िगरेशन — ओरिजिन को Cloudflare को अनुमति देनी चाहिए)",
|
||||
"cause3": "ओरिजिन सर्वर इतना ओवरलोड है कि नए कनेक्शन स्वीकार नहीं कर रहा",
|
||||
"todo1": "यह ओरिजिन की उपलब्धता की समस्या है, जिसे क्रॉलर टाल नहीं सकता — इन URL के लिए साइट असल में बंद है",
|
||||
"todo2": "बाद में दोबारा क्रॉल करें; अगर समस्या बनी रहे, तो साइट खराब है"
|
||||
},
|
||||
"522": {
|
||||
"name": "कनेक्शन का समय समाप्त",
|
||||
"explanation": "Cloudflare ने ओरिजिन के साथ TCP हैंडशेक शुरू किया, लेकिन वह समय-सीमा में पूरा नहीं हुआ — ओरिजिन ने SYN स्वीकार किया पर कभी जवाब नहीं दिया, या बीच के किसी नेटवर्क उपकरण ने पैकेट गिरा दिए।",
|
||||
"cause1": "ओरिजिन बहुत ज़्यादा ओवरलोड है (TCP बैकलॉग भरा हुआ)",
|
||||
"cause2": "ओरिजिन का फ़ायरवॉल Cloudflare के पैकेट को चुपचाप गिरा रहा है (अस्वीकार नहीं कर रहा)",
|
||||
"cause3": "Cloudflare और ओरिजिन के बीच नेटवर्क/रूटिंग समस्या",
|
||||
"cause4": "क्रॉल लोड में: क्रॉल के समानांतर अनुरोधों ने ओरिजिन की कनेक्शन तालिका भर दी",
|
||||
"todo1": "समानांतरता कम करें (सेटिंग्स → गति → अधिकतम समानांतरता) और दोबारा क्रॉल करें — क्रॉल के बीच में दिखने पर आमतौर पर कनेक्शन तालिका भर जाना ही कारण होता है",
|
||||
"todo2": "क्रॉल के बाद प्रभावित URL का पुनः प्रयास करें"
|
||||
},
|
||||
"523": {
|
||||
"name": "ओरिजिन तक पहुँचा नहीं जा सकता",
|
||||
"explanation": "Cloudflare ओरिजिन सर्वर तक बिल्कुल नहीं पहुँच सका — आमतौर पर Cloudflare के ओरिजिन रिकॉर्ड में DNS समस्या होती है, या ओरिजिन का नेटवर्क बंद होता है।",
|
||||
"cause1": "Cloudflare में साइट का DNS रिकॉर्ड गलत / निष्क्रिय IP की ओर इशारा करता है",
|
||||
"cause2": "ओरिजिन के होस्टिंग प्रदाता का नेटवर्क बाधित है",
|
||||
"cause3": "Cloudflare और ओरिजिन के बीच BGP / रूटिंग विफलता",
|
||||
"todo1": "क्रॉलर कुछ नहीं कर सकता — यह ओरिजिन के DNS/नेटवर्क की गलत कॉन्फ़िगरेशन है"
|
||||
},
|
||||
"524": {
|
||||
"name": "टाइमआउट हुआ",
|
||||
"explanation": "Cloudflare ने ओरिजिन से TCP कनेक्शन सफलतापूर्वक बना लिया, लेकिन ओरिजिन ने Cloudflare की समय-सीमा (डिफ़ॉल्ट 100 सेकंड) में पूरी HTTP प्रतिक्रिया नहीं भेजी। ओरिजिन चालू है, पर इस अनुरोध पर बहुत धीमा है।",
|
||||
"cause1": "पेज पर धीमी डेटाबेस क्वेरी, बाहरी API कॉल या भारी गणना",
|
||||
"cause2": "ओरिजिन ओवरलोड है — अनुरोध भरे हुए वर्कर पूल के पीछे कतार में हैं",
|
||||
"cause3": "क्रॉल लोड में: क्रॉल ने ओरिजिन की अनुरोध कतार भर दी और यह URL बहुत देर तक इंतज़ार करता रहा",
|
||||
"todo1": "ओरिजिन पर बोझ न पड़े, इसके लिए अधिकतम समानांतरता / अधिकतम URL/से कम करें और प्रति वर्कर विलंब जोड़ें (सेटिंग्स → गति)",
|
||||
"todo2": "अगर सच में धीमे पेजों को ज़्यादा समय देना हो, तो प्रतिक्रिया समय-सीमा बढ़ाएँ (सेटिंग्स → क्रॉलर)",
|
||||
"todo3": "क्रॉल के बाद प्रभावित URL का पुनः प्रयास करें"
|
||||
},
|
||||
"525": {
|
||||
"name": "SSL हैंडशेक विफल",
|
||||
"explanation": "Cloudflare और ओरिजिन सर्वर के बीच TLS हैंडशेक विफल हो गया। यह Cloudflare↔ओरिजिन वाला हिस्सा है, एड्रेस बार में दिखने वाला ब्राउज़र↔Cloudflare हिस्सा नहीं — इसलिए साइट सामान्य विज़िटर को ठीक दिख सकती है और फिर भी 525 दे सकती है।",
|
||||
"cause1": "ओरिजिन का TLS प्रमाणपत्र समाप्त हो चुका है, स्व-हस्ताक्षरित है या किसी दूसरे होस्टनेम के लिए है (जब Cloudflare 'Full (strict)' मोड में हो)",
|
||||
"cause2": "ओरिजिन केवल पुराने TLS संस्करण / सिफ़र सूट समर्थित करता है, जिन्हें Cloudflare अस्वीकार करता है",
|
||||
"cause3": "ओरिजिन सर्वर ओवरलोड है और TLS हैंडशेक गिरा रहा है",
|
||||
"todo1": "ओरिजिन TLS की गलत कॉन्फ़िगरेशन — क्रॉलर इसे नहीं बदल सकता। होस्ट का प्रमाणपत्र जाँचें (HTTPS के लिए URL विवरण में TLS Cert पंक्तियाँ)"
|
||||
},
|
||||
"526": {
|
||||
"name": "अमान्य SSL प्रमाणपत्र",
|
||||
"explanation": "Cloudflare ('Full (strict)' SSL मोड में) ओरिजिन सर्वर के TLS प्रमाणपत्र को सत्यापित नहीं कर सका।",
|
||||
"cause1": "ओरिजिन प्रमाणपत्र समाप्त हो चुका है, स्व-हस्ताक्षरित है या किसी दूसरे होस्टनेम के लिए जारी हुआ है",
|
||||
"cause2": "ओरिजिन पर प्रमाणपत्र शृंखला अधूरी है (इंटरमीडिएट प्रमाणपत्र गायब)",
|
||||
"todo1": "ओरिजिन प्रमाणपत्र की समस्या — क्रॉलर कुछ नहीं कर सकता"
|
||||
},
|
||||
"527": {
|
||||
"name": "Railgun त्रुटि (Cloudflare, बंद हो चुका)",
|
||||
"explanation": "Cloudflare Railgun (अब बंद हो चुका ओरिजिन-एक्सेलेरेशन उत्पाद) में कनेक्शन त्रुटि। यह व्यावहारिक रूप से पुराना कोड है; अगर यह दिखे तो साइट बहुत पुराने Cloudflare सेटअप पर है।",
|
||||
"cause1": "पुराने Cloudflare Railgun कनेक्शन की विफलता",
|
||||
"todo1": "क्रॉलर की ओर से कुछ करने को नहीं है"
|
||||
},
|
||||
"530": {
|
||||
"name": "Cloudflare त्रुटि (आमतौर पर फ़ायरवॉल / WAF ब्लॉक — त्रुटि 1020)",
|
||||
"explanation": "530 Cloudflare का यह कहने का तरीका है कि \"एक त्रुटि हुई, बॉडी में 1xxx त्रुटि कोड देखें\"। क्रॉल में यह लगभग हमेशा 1020 \"Access Denied\" होता है — किसी Cloudflare Firewall / WAF / Bot-Fight नियम ने अनुरोध ब्लॉक कर दिया। कम ही यह 1016 (ओरिजिन DNS त्रुटि) या दूसरे 1xxx कोड के साथ आता है।",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode ने क्रॉलर को चिह्नित किया — IP, User-Agent, अनुरोध दर या ब्राउज़र फ़िंगरप्रिंट न होने के आधार पर",
|
||||
"cause2": "साइट पर ऐसा \"managed challenge\" है जिसे क्रॉलर हल नहीं कर सकता",
|
||||
"cause3": "(कभी-कभार) ब्लॉक के बजाय ओरिजिन DNS त्रुटि (1016)",
|
||||
"todo1": "ब्राउज़र जैसा User-Agent सेट करें (सेटिंग्स → अनुरोध) — डिफ़ॉल्ट क्रॉलर UA सबसे बड़ा कारण है",
|
||||
"todo2": "अधिकतम समानांतरता / अधिकतम URL/से कम करें और प्रति वर्कर विलंब जोड़ें (सेटिंग्स → गति) — एक ही IP से तेज़ अनुरोध दर-आधारित नियमों को सक्रिय कर देते हैं",
|
||||
"todo3": "robots.txt और उसमें घोषित Crawl-delay का पालन करें (सेटिंग्स → क्रॉलर)",
|
||||
"todo4": "अगर साइट आपकी है, तो Cloudflare में क्रॉलर को अनुमति सूची में डालें; नहीं तो यह साइट जानबूझकर स्वचालित पहुँच रोक रही है"
|
||||
},
|
||||
"401": {
|
||||
"name": "अनधिकृत",
|
||||
"explanation": "संसाधन के लिए प्रमाणीकरण ज़रूरी है और कोई (या अमान्य) क्रेडेंशियल नहीं भेजे गए।",
|
||||
"cause1": "पेज HTTP Basic/Bearer प्रमाणीकरण के पीछे है",
|
||||
"cause2": "सेशन कुकी ज़रूरी है, लेकिन नहीं भेजी गई",
|
||||
"todo1": "अगर आपके पास क्रेडेंशियल हैं, तो उन्हें सेटिंग्स → प्रमाणीकरण में कॉन्फ़िगर करें",
|
||||
"todo2": "अगर साइट सामग्री को सेशन के पीछे रखती है, तो सेटिंग्स → कुकियाँ → \"सभी स्वीकार करें\" का उपयोग करें"
|
||||
},
|
||||
"403": {
|
||||
"name": "प्रतिबंधित",
|
||||
"explanation": "सर्वर ने अनुरोध समझा, लेकिन उसे पूरा करने से मना करता है। क्रॉल में यह असली अनुमति समस्या के बजाय ज़्यादातर बॉट ब्लॉक या WAF नियम होता है।",
|
||||
"cause1": "क्रॉलर के User-Agent, IP या अनुरोध दर ने WAF / बॉट-सुरक्षा नियम को सक्रिय कर दिया",
|
||||
"cause2": "निर्देशिका या फ़ाइल वाकई अनाम उपयोगकर्ताओं के लिए उपलब्ध नहीं है",
|
||||
"cause3": "हॉटलिंक / रेफ़रर सुरक्षा",
|
||||
"todo1": "ब्राउज़र जैसा User-Agent आज़माएँ (सेटिंग्स → अनुरोध)",
|
||||
"todo2": "क्रॉल की दर कम करें (सेटिंग्स → गति)",
|
||||
"todo3": "अगर यह पूरी साइट पर हो रहा है, तो साइट स्वचालित पहुँच को सक्रिय रूप से रोक रही है"
|
||||
},
|
||||
"404": {
|
||||
"name": "नहीं मिला",
|
||||
"explanation": "सर्वर पर इस URL पर कोई संसाधन नहीं है।",
|
||||
"cause1": "टूटा आंतरिक/बाहरी लिंक",
|
||||
"cause2": "पेज बिना रीडायरेक्ट के हटा दिया गया",
|
||||
"cause3": "किसी लिंक के href में टाइपिंग की गलती",
|
||||
"todo1": "लिंक करने वाले पेजों को ठीक करें या लिंक हटाएँ — यहाँ कौन लिंक करता है, यह इनलिंक टैब में देखें"
|
||||
},
|
||||
"408": {
|
||||
"name": "अनुरोध का समय समाप्त",
|
||||
"explanation": "अनुरोध पूरा होने का इंतज़ार करते-करते सर्वर का समय समाप्त हो गया।",
|
||||
"cause1": "क्रॉलर और सर्वर के बीच धीमा नेटवर्क",
|
||||
"cause2": "सर्वर-साइड कनेक्शन टाइमआउट बहुत कम सेट है",
|
||||
"todo1": "आमतौर पर अस्थायी — पुनः प्रयास बढ़ाएँ (सेटिंग्स → गति)"
|
||||
},
|
||||
"410": {
|
||||
"name": "स्थायी रूप से हटाया गया",
|
||||
"explanation": "संसाधन जानबूझकर और स्थायी रूप से हटाया गया — यह 404 से ज़्यादा स्पष्ट संकेत है।",
|
||||
"cause1": "पेज जानबूझकर हटाया गया; सर्वर क्रॉलरों को इसे छोड़ने के लिए कह रहा है",
|
||||
"todo1": "इस URL के लिंक हटाएँ — Google इसे 404 से जल्दी इंडेक्स से हटाएगा"
|
||||
},
|
||||
"429": {
|
||||
"name": "बहुत अधिक अनुरोध (दर सीमा)",
|
||||
"explanation": "सर्वर क्रॉलर की दर सीमित कर रहा है — इस IP/User-Agent से बहुत कम समय में बहुत ज़्यादा अनुरोध आए।",
|
||||
"cause1": "क्रॉल की दर सर्वर की प्रति-क्लाइंट सीमा से ज़्यादा है",
|
||||
"cause2": "आक्रामक थ्रॉटलिंग वाली साझा होस्टिंग",
|
||||
"todo1": "अधिकतम समानांतरता / अधिकतम URL/से कम करें और प्रति वर्कर विलंब जोड़ें (सेटिंग्स → गति)",
|
||||
"todo2": "Retry-After हेडर का पालन करें — अगर सर्वर ने भेजा है, तो नीचे \"प्रतिक्रिया हेडर से\" देखें",
|
||||
"todo3": "\"robots.txt के Crawl-delay का पालन करें\" चालू करें (सेटिंग्स → क्रॉलर)"
|
||||
},
|
||||
"451": {
|
||||
"name": "कानूनी कारणों से अनुपलब्ध",
|
||||
"explanation": "संसाधन कानूनी कारणों से अवरुद्ध है (अदालती आदेश, GDPR जियोब्लॉक, DMCA आदि)।",
|
||||
"cause1": "भौगोलिक रूप से प्रतिबंधित सामग्री",
|
||||
"cause2": "कानूनन अनिवार्य हटाया जाना",
|
||||
"todo1": "कुछ नहीं — ब्लॉक जानबूझकर है"
|
||||
},
|
||||
"500": {
|
||||
"name": "आंतरिक सर्वर त्रुटि",
|
||||
"explanation": "पेज बनाते समय सर्वर-साइड एप्लिकेशन में एक अनसंभाली त्रुटि हुई।",
|
||||
"cause1": "पेज टेम्पलेट / बैकएंड कोड में बग",
|
||||
"cause2": "डेटाबेस उपलब्ध नहीं",
|
||||
"cause3": "सर्वर पर गलत कॉन्फ़िगरेशन",
|
||||
"todo1": "क्रॉल के बाद पुनः प्रयास करें (अस्थायी 500 ठीक हो जाते हैं; लगातार आने वाले असली बग हैं)",
|
||||
"todo2": "अगर 500 लोड में दिखें, तो क्रॉल किसी डेटाबेस पर ज़्यादा बोझ डाल सकता है — क्रॉल धीमा करें (सेटिंग्स → गति)"
|
||||
},
|
||||
"502": {
|
||||
"name": "खराब गेटवे",
|
||||
"explanation": "ऐप के आगे लगे रिवर्स प्रॉक्सी / लोड बैलेंसर को अपस्ट्रीम एप्लिकेशन सर्वर से अमान्य प्रतिक्रिया मिली।",
|
||||
"cause1": "अपस्ट्रीम ऐप सर्वर क्रैश हुआ या बेकार डेटा लौटाया",
|
||||
"cause2": "अपस्ट्रीम ओवरलोड है — क्रॉल लोड में आम बात",
|
||||
"cause3": "प्रॉक्सी↔अपस्ट्रीम टाइमआउट",
|
||||
"todo1": "अगर 502 एक साथ दिखें, तो क्रॉल की दर कम करें (सेटिंग्स → गति)",
|
||||
"todo2": "क्रॉल के बाद प्रभावित URL का पुनः प्रयास करें"
|
||||
},
|
||||
"503": {
|
||||
"name": "सेवा अनुपलब्ध",
|
||||
"explanation": "सर्वर अस्थायी रूप से अनुरोध नहीं संभाल पा रहा — आमतौर पर ओवरलोड या रखरखाव के कारण। अक्सर इसके साथ Retry-After हेडर आता है।",
|
||||
"cause1": "सर्वर ओवरलोड — इसका कारण खुद क्रॉल भी हो सकता है",
|
||||
"cause2": "निर्धारित रखरखाव का समय",
|
||||
"cause3": "ऑटो-स्केलिंग में देरी (नए इंस्टेंस शुरू हो रहे हैं)",
|
||||
"todo1": "अधिकतम समानांतरता / अधिकतम URL/से कम करें और प्रति वर्कर विलंब जोड़ें (सेटिंग्स → गति)",
|
||||
"todo2": "Retry-After का पालन करें (नीचे \"प्रतिक्रिया हेडर से\" देखें)",
|
||||
"todo3": "क्रॉल के बाद प्रभावित URL का पुनः प्रयास करें"
|
||||
},
|
||||
"504": {
|
||||
"name": "गेटवे टाइमआउट",
|
||||
"explanation": "रिवर्स प्रॉक्सी / लोड बैलेंसर को अपस्ट्रीम ऐप सर्वर से समय पर प्रतिक्रिया नहीं मिली।",
|
||||
"cause1": "पेज पर धीमी डेटाबेस क्वेरी / बाहरी API कॉल",
|
||||
"cause2": "अपस्ट्रीम ओवरलोड — अनुरोध कतार में हैं",
|
||||
"cause3": "क्रॉल लोड में: क्रॉल ने अपस्ट्रीम वर्कर पूल को भर दिया",
|
||||
"todo1": "क्रॉल की दर कम करें (सेटिंग्स → गति)",
|
||||
"todo2": "सच में धीमे पेजों के लिए प्रतिक्रिया समय-सीमा बढ़ाएँ (सेटिंग्स → क्रॉलर)",
|
||||
"todo3": "क्रॉल के बाद प्रभावित URL का पुनः प्रयास करें"
|
||||
},
|
||||
"508": {
|
||||
"name": "लूप मिला",
|
||||
"explanation": "अनुरोध प्रोसेस करते समय सर्वर को एक अनंत लूप मिला (अक्सर WebDAV या सर्वर-साइड रीडायरेक्ट लूप)।",
|
||||
"cause1": "सर्वर-साइड रीडायरेक्ट लूप",
|
||||
"cause2": "रिकर्सिव include / सिमलिंक लूप",
|
||||
"todo1": "सर्वर बग — क्रॉलर कुछ नहीं कर सकता"
|
||||
},
|
||||
"509": {
|
||||
"name": "बैंडविड्थ सीमा पार",
|
||||
"explanation": "एक गैर-मानक कोड (cPanel/Apache), जिसका मतलब है कि होस्टिंग खाते ने अपनी मासिक बैंडविड्थ सीमा पूरी कर ली है।",
|
||||
"cause1": "साझा होस्टिंग का बैंडविड्थ कोटा खत्म",
|
||||
"todo1": "क्रॉलर कुछ नहीं कर सकता — होस्ट पूरे खाते को सीमित कर रहा है"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Cloudflare के पीछे — cf-ray {{ray}}। स्थिति कोड Cloudflare↔ओरिजिन लिंक के बारे में बताता है, ब्राउज़र जो देखता है उसके बारे में नहीं।",
|
||||
"fronted": "{{front}} के ज़रिए परोसा गया।",
|
||||
"origin": "ओरिजिन सर्वर: {{server}}।",
|
||||
"retryAfterSeconds": "सर्वर ने Retry-After: {{seconds}} से भेजा — वह क्लाइंट से साफ़ तौर पर इतनी देर रुकने को कह रहा है। एक शिष्ट क्रॉल को इससे पहले यह URL दोबारा नहीं माँगना चाहिए।",
|
||||
"retryAfter": "सर्वर ने Retry-After: {{value}} भेजा।",
|
||||
"cfCache": "Cloudflare कैश स्थिति: {{status}}।",
|
||||
"xRobots": "X-Robots-Tag: {{value}} — स्थिति चाहे जो हो, अनुक्रमणीयता को प्रभावित करता है।"
|
||||
},
|
||||
"robots": {
|
||||
"title": "अनुरोध नहीं किया गया — robots.txt अनुमति नहीं देता",
|
||||
"explanation": "क्रॉलर ने इस URL के लिए कभी अनुरोध नहीं भेजा: कॉन्फ़िगर किए गए user-agent के लिए robots.txt का एक नियम इसकी अनुमति नहीं देता। कोई स्थिति कोड नहीं है क्योंकि कभी प्रतिक्रिया माँगी ही नहीं गई — यह कनेक्शन विफलता नहीं है।",
|
||||
"cause1": "साइट के robots.txt का एक Disallow नियम क्रॉल user-agent के लिए इस पथ से मेल खाता है",
|
||||
"cause2": "क्रॉल किए गए पेजों से इस URL पर लिंक हैं, इसलिए यह दर्ज और गिना जाता है, पर कभी लाया नहीं जाता",
|
||||
"todo1": "अगर बहिष्करण जानबूझकर है तो कुछ नहीं — Google भी इसे क्रॉल नहीं करेगा",
|
||||
"todo2": "जाँचें कि पेज को अनुक्रमणीय होना चाहिए या नहीं: अवरुद्ध URL लिंक मूल्य आगे नहीं बढ़ा सकता और उसकी सामग्री कभी नहीं दिखती",
|
||||
"todo3": "कौन-सा नियम मेल खाता है, यह देखने के लिए सहायता → robots.txt परीक्षक का उपयोग करें, या फिर भी ऑडिट करने के लिए सेटिंग्स → क्रॉलर में \"robots.txt का पालन करें\" बंद करें"
|
||||
},
|
||||
"network": {
|
||||
"title": "कोई HTTP प्रतिक्रिया नहीं — नेटवर्क / कनेक्शन त्रुटि",
|
||||
"explanationDetail": "अनुरोध को कभी HTTP प्रतिक्रिया नहीं मिली। क्रॉलर ने दर्ज किया: \"{{detail}}\"।",
|
||||
"explanation": "अनुरोध को कभी HTTP प्रतिक्रिया नहीं मिली — कोई स्थिति पंक्ति आने से पहले ही DNS लुकअप, TCP कनेक्शन, TLS हैंडशेक या प्रतिक्रिया पढ़ना विफल हो गया।",
|
||||
"cause1": "DNS होस्टनेम हल नहीं कर सका (टाइपिंग की गलती, निष्क्रिय डोमेन, या ऐसा नाम जो सिर्फ़ निजी नेटवर्क के भीतर हल होता है)",
|
||||
"cause2": "TCP कनेक्शन अस्वीकार हुआ या उसका समय समाप्त हो गया (होस्ट बंद, फ़ायरवॉल, गलत पोर्ट)",
|
||||
"cause3": "TLS हैंडशेक विफल (समाप्त/अमान्य प्रमाणपत्र, प्रोटोकॉल मेल नहीं खाता)",
|
||||
"cause4": "प्रतिक्रिया के बीच में कनेक्शन रीसेट हुआ या पढ़ने का समय समाप्त हो गया",
|
||||
"cause5": "अनुरोध प्रतिक्रिया समय-सीमा (सेटिंग्स → क्रॉलर) या अधिकतम प्रतिक्रिया समय (सेटिंग्स → उन्नत) से ज़्यादा हो गया, इसलिए क्रॉलर ने उसे रद्द कर दिया",
|
||||
"todo1": "जाँचें कि होस्टनेम हल होता है और इस कंप्यूटर के सामान्य ब्राउज़र से साइट खुलती है",
|
||||
"todo2": "अगर साइट खुलती है पर धीमी है, तो प्रतिक्रिया समय-सीमा बढ़ाएँ (सेटिंग्स → क्रॉलर)",
|
||||
"todo3": "अगर यह सिर्फ़ लोड में विफल होता है, तो अधिकतम समानांतरता / अधिकतम URL/से कम करें (सेटिंग्स → गति) — ओरिजिन की कनेक्शन तालिका भर जाना क्लाइंट को नेटवर्क त्रुटि जैसा दिखता है",
|
||||
"todo4": "क्रॉल के बाद प्रभावित URL का पुनः प्रयास करें (अस्थायी रीसेट पुनः प्रयास में ठीक हो जाते हैं)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — सूचनात्मक",
|
||||
"fallback": "सूचनात्मक",
|
||||
"explanation": "एक 1xx प्रतिक्रिया ({{text}})। ये अंतरिम प्रतिक्रियाएँ हैं; क्रॉलर इन्हें अस्थायी मानता है और अंतिम स्थिति का इंतज़ार करता है।"
|
||||
},
|
||||
"success": {
|
||||
"fallback": "सफल",
|
||||
"explanation": "अनुरोध सफल रहा। निदान की ज़रूरत नहीं — यह भाग सिर्फ़ त्रुटि और रीडायरेक्ट कोड के लिए अतिरिक्त जानकारी देता है।"
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "रीडायरेक्ट",
|
||||
"explanation301": "स्थायी रीडायरेक्ट। सर्च इंजन रैंकिंग संकेत लक्ष्य पर स्थानांतरित करते हैं और अपने इंडेक्स को नए URL से अपडेट करते हैं।",
|
||||
"explanation302": "अस्थायी रीडायरेक्ट। सर्च इंजन मूल URL को इंडेक्स में रखते हैं। लंबे समय तक 302 का उपयोग एक आम SEO गलती है — आमतौर पर इसे 301 होना चाहिए।",
|
||||
"explanation308": "स्थायी रीडायरेक्ट (301 जैसा, लेकिन अनुरोध की विधि बनी रहती है)।",
|
||||
"explanation304": "संशोधित नहीं — कैश की गई प्रति अभी भी ताज़ा है। क्रॉलर आमतौर पर बिना कंडीशनल हेडर के अनुरोध करता है, इसलिए यह असामान्य है।",
|
||||
"explanationOther": "एक {{code}} रीडायरेक्ट ({{text}})।",
|
||||
"location": "Location हेडर इस ओर इशारा करता है: {{location}}",
|
||||
"noLocation": "कोई Location हेडर नहीं मिला — रीडायरेक्ट के लिए यह असामान्य है।",
|
||||
"todoPermanent": "अगर यह रीडायरेक्ट स्थायी है, तो इसे 301/308 में बदलें ताकि रैंकिंग संकेत एक जगह इकट्ठे हों",
|
||||
"todoChain": "पूरी रीडायरेक्ट शृंखला और उसका अंतिम लक्ष्य देखने के लिए प्रतिक्रिया कोड टैब देखें"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "क्लाइंट त्रुटि",
|
||||
"explanation": "4xx कोड का मतलब है कि सर्वर ने अनुरोध को क्लाइंट की गलती मानकर अस्वीकार कर दिया (गलत अनुरोध, अनुमति नहीं, नहीं मिला आदि)। क्रॉल में लिंक किए गए पेजों पर लगातार 4xx आमतौर पर टूटे लिंक का संकेत है; कभी-कभार आने वाले 4xx बॉट-सुरक्षा हो सकते हैं।",
|
||||
"cause1": "टूटा या गलत फ़ॉर्मैट वाला लिंक",
|
||||
"cause2": "बॉट-सुरक्षा / WAF नियम",
|
||||
"cause3": "ऐसी विधि या सामग्री जिसे सर्वर अस्वीकार करता है",
|
||||
"todo1": "इस URL पर क्या लिंक करता है, यह देखने के लिए इनलिंक टैब देखें",
|
||||
"todo2": "अगर यह ब्लॉक जैसा लगे, तो ब्राउज़र जैसा User-Agent और धीमी दर आज़माएँ (सेटिंग्स → अनुरोध / गति)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "सर्वर त्रुटि",
|
||||
"explanation": "5xx कोड का मतलब है कि सर्वर उस अनुरोध को पूरा नहीं कर सका जिसे उसने वैध माना था। क्रॉल लोड में यह अक्सर असली पेज बग के बजाय क्रॉल द्वारा ओरिजिन पर ज़्यादा बोझ डालने का नतीजा होता है।",
|
||||
"cause1": "सर्वर-साइड त्रुटि या क्रैश",
|
||||
"cause2": "ओरिजिन ओवरलोड — शायद खुद क्रॉल की वजह से",
|
||||
"cause3": "अपस्ट्रीम/प्रॉक्सी विफलता",
|
||||
"todo1": "अगर 5xx एक साथ दिखें, तो अधिकतम समानांतरता / अधिकतम URL/से कम करें और प्रति वर्कर विलंब जोड़ें (सेटिंग्स → गति)",
|
||||
"todo2": "पुनः प्रयास बढ़ाएँ (सेटिंग्स → गति) और प्रभावित URL दोबारा क्रॉल करें"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "इस पृष्ठ पर कोई शीर्षक नहीं मिला (कोई",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL",
|
||||
"percentOfTotal": "% del totale",
|
||||
"siteSoft404": "Le pagine inesistenti restituiscono {{status}} (soft 404)",
|
||||
"siteSoft404Hint": "Una richiesta a una pagina che non esiste ha ricevuto {{status}} invece di 404, quindi i link interrotti di questo sito sembrano pagine funzionanti ai motori di ricerca."
|
||||
"siteSoft404Hint": "Una richiesta a una pagina che non esiste ha ricevuto {{status}} invece di 404, quindi i link interrotti di questo sito sembrano pagine funzionanti ai motori di ricerca.",
|
||||
"siteBotBlocked": "La protezione anti-bot ha bloccato parte della scansione",
|
||||
"siteBotBlockedHint": "La protezione anti-bot del sito ha risposto con una verifica al posto di queste pagine. Non sono state scansionate e sono escluse dai controlli su link interrotti e sitemap. Riduci la velocità di scansione o autorizza lo user agent del crawler, poi scansionale di nuovo."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "Collega servizi esterni con le tue credenziali. Le chiavi API e i segreti sono cifrati a riposo tramite l'archivio sicuro del tuo sistema operativo e non lasciano mai questa macchina.",
|
||||
@@ -406,7 +408,58 @@
|
||||
"orphanMetricaVisits_other": "{{formatted}} visite Metrica (ingresso)",
|
||||
"orphanMetricaPageviews_one": "{{formatted}} visualizzazione di pagina Metrica (hit)",
|
||||
"orphanMetricaPageviews_many": "{{formatted}} di visualizzazioni di pagina Metrica (hit)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} visualizzazioni di pagina Metrica (hit)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} visualizzazioni di pagina Metrica (hit)",
|
||||
"noResponse": "Nessuna risposta",
|
||||
"bucketOrphan": "0 (orfana)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "NO",
|
||||
"warn": "AVVISO",
|
||||
"bad": "MALE",
|
||||
"err": "ERRORE",
|
||||
"slow": "LENTO",
|
||||
"net": "RETE"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} OK · {{rate}}% di errori",
|
||||
"avgMs": "media {{ms}} ms",
|
||||
"distinctIds_one": "{{count}} ID distinto",
|
||||
"distinctIds_many": "{{count}} ID distinti",
|
||||
"distinctIds_other": "{{count}} ID distinti",
|
||||
"pageCount_one": "{{formatted}} pagina",
|
||||
"pageCount_many": "{{formatted}} pagine",
|
||||
"pageCount_other": "{{formatted}} pagine",
|
||||
"imageCount_one": "{{formatted}} immagine",
|
||||
"imageCount_many": "{{formatted}} immagini",
|
||||
"imageCount_other": "{{formatted}} immagini",
|
||||
"hits_one": "{{formatted}} occorrenza",
|
||||
"hits_many": "{{formatted}} occorrenze",
|
||||
"hits_other": "{{formatted}} occorrenze",
|
||||
"avgWords": "media {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "da {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "Navigazione",
|
||||
"header": "Intestazione",
|
||||
"content": "Contenuto",
|
||||
"sidebar": "Barra laterale",
|
||||
"footer": "Footer",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "Indicizzabile",
|
||||
"noindex": "Non indicizzabile — noindex",
|
||||
"canonical": "Non indicizzabile — canonicalizzata",
|
||||
"robotsBlocked": "Non indicizzabile — bloccata da robots.txt",
|
||||
"redirect": "Non indicizzabile — reindirizzamento",
|
||||
"clientError": "Non indicizzabile — 4xx",
|
||||
"serverError": "Non indicizzabile — 5xx",
|
||||
"botBlocked": "Sconosciuta — bloccata dalla protezione anti-bot"
|
||||
},
|
||||
"kind": {
|
||||
"image": "Immagine",
|
||||
"font": "Font",
|
||||
"other": "Altro"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "Validatore di sitemap",
|
||||
@@ -1070,7 +1123,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "Dagli header di risposta",
|
||||
"likelyCauses": "Cause probabili",
|
||||
"whatToDo": "Cosa fare"
|
||||
"whatToDo": "Cosa fare",
|
||||
"cloudflareStyle": "codice edge in stile Cloudflare",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "Il server web ha restituito un errore sconosciuto",
|
||||
"explanation": "Cloudflare si è collegato al server di origine ma ha ricevuto una risposta vuota, malformata o inattesa (ad esempio la connessione si è interrotta a metà risposta, l'origine ha restituito header troppo grandi o il processo dell'origine si è bloccato). È un codice generico per 'qualcosa è andato storto sull'origine'.",
|
||||
"cause1": "Il server di origine si è bloccato, si è riavviato o ha esaurito worker/memoria mentre serviva la richiesta",
|
||||
"cause2": "L'origine ha chiuso la connessione TCP prima di inviare una risposta completa (spesso un timeout dell'applicazione o una chiusura per memoria esaurita)",
|
||||
"cause3": "Il ritmo delle richieste della scansione ha sommerso il pool di processi dell'origine: i 520 che compaiono a raffica sotto carico significano quasi sempre questo",
|
||||
"cause4": "L'origine ha restituito header di risposta più grandi di quanto Cloudflare consente (raro; di solito un'applicazione configurata male)",
|
||||
"todo1": "Se i 520 compaiono a raffica su molti URL, abbassa Concorrenza massima / URL/s massimi e aggiungi un Ritardo per worker (Impostazioni → Velocità): la scansione sta sovraccaricando l'origine",
|
||||
"todo2": "Scansiona di nuovo gli URL interessati a scansione finita (i 520 transitori spesso spariscono al nuovo tentativo: aumenta i Tentativi in Impostazioni → Velocità)",
|
||||
"todo3": "Se solo alcuni URL restituiscono sempre 520, quelle pagine hanno un bug lato server: segnalale al proprietario del sito"
|
||||
},
|
||||
"521": {
|
||||
"name": "Il server web non è raggiungibile",
|
||||
"explanation": "Cloudflare non è riuscito ad aprire una connessione TCP con il server di origine: l'origine ha rifiutato attivamente la connessione o non è in ascolto sulla porta prevista.",
|
||||
"cause1": "Il server web di origine (nginx/Apache/Node ecc.) è fermo o si è bloccato",
|
||||
"cause2": "Il firewall dell'origine blocca gli intervalli IP di Cloudflare (un errore di configurazione: l'origine dovrebbe consentire Cloudflare)",
|
||||
"cause3": "Il server di origine è così sovraccarico da non accettare nuove connessioni",
|
||||
"todo1": "È un problema di disponibilità dell'origine, non qualcosa che il crawler possa aggirare: per questi URL il sito è di fatto irraggiungibile",
|
||||
"todo2": "Scansiona di nuovo più tardi; se persiste, il sito è guasto"
|
||||
},
|
||||
"522": {
|
||||
"name": "Connessione scaduta",
|
||||
"explanation": "Cloudflare ha avviato un handshake TCP con l'origine, ma non si è concluso entro il timeout: l'origine ha accettato il SYN senza mai rispondere, oppure un dispositivo di rete intermedio ha scartato i pacchetti.",
|
||||
"cause1": "L'origine è gravemente sovraccarica (coda TCP piena)",
|
||||
"cause2": "Il firewall dell'origine scarta in silenzio (senza rifiutarli) i pacchetti di Cloudflare",
|
||||
"cause3": "Problema di rete/routing tra Cloudflare e l'origine",
|
||||
"cause4": "Sotto il carico della scansione: le richieste parallele della scansione hanno esaurito la tabella delle connessioni dell'origine",
|
||||
"todo1": "Abbassa la concorrenza (Impostazioni → Velocità → Concorrenza massima) e scansiona di nuovo: l'esaurimento della tabella delle connessioni è la causa tipica quando compare a metà scansione",
|
||||
"todo2": "Riprova gli URL interessati dopo la scansione"
|
||||
},
|
||||
"523": {
|
||||
"name": "L'origine non è raggiungibile",
|
||||
"explanation": "Cloudflare non è riuscito in alcun modo a raggiungere il server di origine: di solito un problema DNS nel record di origine su Cloudflare, oppure la rete dell'origine è fuori servizio.",
|
||||
"cause1": "Il record DNS del sito su Cloudflare punta a un IP sbagliato / non attivo",
|
||||
"cause2": "Il provider di hosting dell'origine ha un guasto di rete",
|
||||
"cause3": "Guasto BGP / di routing tra Cloudflare e l'origine",
|
||||
"todo1": "Il crawler non può farci nulla: è un errore di configurazione DNS/di rete dell'origine"
|
||||
},
|
||||
"524": {
|
||||
"name": "Si è verificato un timeout",
|
||||
"explanation": "Cloudflare ha stabilito correttamente una connessione TCP con l'origine, ma l'origine non ha inviato una risposta HTTP completa entro il timeout di Cloudflare (100 s per impostazione predefinita). L'origine funziona, ma è troppo lenta su questa richiesta.",
|
||||
"cause1": "Una query lenta al database, una chiamata a un'API esterna o un'elaborazione pesante nella pagina",
|
||||
"cause2": "L'origine è sovraccarica: le richieste sono in coda dietro un pool di worker saturo",
|
||||
"cause3": "Sotto il carico della scansione: la scansione ha riempito la coda delle richieste dell'origine e questo URL ha atteso troppo",
|
||||
"todo1": "Abbassa Concorrenza massima / URL/s massimi e aggiungi un Ritardo per worker (Impostazioni → Velocità) per non saturare l'origine",
|
||||
"todo2": "Aumenta il Timeout di risposta (Impostazioni → Scanner) se vuoi concedere più tempo alle pagine davvero lente",
|
||||
"todo3": "Riprova gli URL interessati dopo la scansione"
|
||||
},
|
||||
"525": {
|
||||
"name": "Handshake SSL non riuscito",
|
||||
"explanation": "L'handshake TLS tra Cloudflare e il server di origine non è riuscito. Si tratta del tratto Cloudflare↔origine, non del tratto browser↔Cloudflare che vedi nella barra degli indirizzi: per questo il sito può sembrare normale a un visitatore e restituire comunque 525.",
|
||||
"cause1": "Il certificato TLS dell'origine è scaduto, autofirmato o emesso per un altro nome host (con Cloudflare in modalità 'Full (strict)')",
|
||||
"cause2": "L'origine supporta solo vecchie versioni di TLS / suite di cifratura che Cloudflare rifiuta",
|
||||
"cause3": "Il server di origine è sovraccarico e scarta gli handshake TLS",
|
||||
"todo1": "Errore di configurazione TLS dell'origine: non è qualcosa che il crawler possa cambiare. Controlla il certificato dell'host (le righe TLS Cert in Dettagli dell’URL, per HTTPS)"
|
||||
},
|
||||
"526": {
|
||||
"name": "Certificato SSL non valido",
|
||||
"explanation": "Cloudflare (in modalità SSL 'Full (strict)') non è riuscito a convalidare il certificato TLS del server di origine.",
|
||||
"cause1": "Il certificato dell'origine è scaduto, autofirmato o emesso per un altro nome host",
|
||||
"cause2": "La catena di certificati dell'origine è incompleta (mancano gli intermedi)",
|
||||
"todo1": "Problema del certificato dell'origine: il crawler non può farci nulla"
|
||||
},
|
||||
"527": {
|
||||
"name": "Errore Railgun (Cloudflare, dismesso)",
|
||||
"explanation": "Un errore di connessione in Cloudflare Railgun (un prodotto di accelerazione dell'origine ormai dismesso). Di fatto obsoleto; se lo vedi, il sito usa una configurazione Cloudflare molto vecchia.",
|
||||
"cause1": "Errore di connessione del vecchio Cloudflare Railgun",
|
||||
"todo1": "Niente da fare lato crawler"
|
||||
},
|
||||
"530": {
|
||||
"name": "Errore Cloudflare (di solito un blocco firewall / WAF — errore 1020)",
|
||||
"explanation": "Un 530 è il modo di Cloudflare per dire \"si è verificato un errore, vedi il codice 1xxx nel corpo\". In una scansione è quasi sempre 1020 \"Access Denied\": una regola Cloudflare Firewall / WAF / Bot-Fight ha bloccato la richiesta. Più raramente si accompagna a 1016 (errore DNS dell'origine) o ad altri codici 1xxx.",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode ha segnalato il crawler: per IP, per User-Agent, per ritmo delle richieste o per assenza di impronte del browser",
|
||||
"cause2": "Il sito usa una \"managed challenge\" che il crawler non può risolvere",
|
||||
"cause3": "(Raramente) un errore DNS dell'origine (1016) anziché un blocco",
|
||||
"todo1": "Imposta un User-Agent da browser (Impostazioni → Richieste): l'UA predefinito del crawler è la causa numero 1",
|
||||
"todo2": "Abbassa Concorrenza massima / URL/s massimi e aggiungi un Ritardo per worker (Impostazioni → Velocità): richieste rapide da un solo IP attivano le regole basate sul ritmo",
|
||||
"todo3": "Rispetta il robots.txt e qualsiasi Crawl-delay dichiarato (Impostazioni → Scanner)",
|
||||
"todo4": "Se il sito è tuo, metti il crawler nella lista consentiti di Cloudflare; altrimenti il sito blocca di proposito l'accesso automatico"
|
||||
},
|
||||
"401": {
|
||||
"name": "Non autorizzato",
|
||||
"explanation": "La risorsa richiede l'autenticazione e non sono state inviate credenziali (o erano non valide).",
|
||||
"cause1": "La pagina è protetta da un'autenticazione HTTP Basic/Bearer",
|
||||
"cause2": "Serve un cookie di sessione e non ne è stato inviato nessuno",
|
||||
"todo1": "Configura le credenziali in Impostazioni → Autenticazione se le hai",
|
||||
"todo2": "Usa Impostazioni → Cookie → \"Accetta tutti\" se il sito riserva i contenuti a una sessione"
|
||||
},
|
||||
"403": {
|
||||
"name": "Accesso negato",
|
||||
"explanation": "Il server ha capito la richiesta ma si rifiuta di servirla. In una scansione si tratta più spesso di un blocco dei bot o di una regola WAF che di un vero problema di permessi.",
|
||||
"cause1": "Regola WAF / di protezione anti-bot attivata dallo User-Agent, dall'IP o dal ritmo delle richieste del crawler",
|
||||
"cause2": "Cartella o file davvero non accessibile agli utenti anonimi",
|
||||
"cause3": "Protezione da hotlink / referer",
|
||||
"todo1": "Prova uno User-Agent da browser (Impostazioni → Richieste)",
|
||||
"todo2": "Riduci il ritmo della scansione (Impostazioni → Velocità)",
|
||||
"todo3": "Se riguarda tutto il sito, il sito sta bloccando attivamente l'accesso automatico"
|
||||
},
|
||||
"404": {
|
||||
"name": "Non trovato",
|
||||
"explanation": "Il server non ha alcuna risorsa a questo URL.",
|
||||
"cause1": "Link interno/esterno interrotto",
|
||||
"cause2": "La pagina è stata rimossa senza reindirizzamento",
|
||||
"cause3": "Errore di battitura nell'href di un link",
|
||||
"todo1": "Correggi o rimuovi i link nelle pagine che puntano qui: la scheda Link in entrata mostra chi rimanda a questo URL"
|
||||
},
|
||||
"408": {
|
||||
"name": "Timeout della richiesta",
|
||||
"explanation": "Il server è andato in timeout aspettando il completamento della richiesta.",
|
||||
"cause1": "Rete lenta tra il crawler e il server",
|
||||
"cause2": "Timeout di connessione del server impostato molto basso",
|
||||
"todo1": "Di solito è transitorio: aumenta i Tentativi (Impostazioni → Velocità)"
|
||||
},
|
||||
"410": {
|
||||
"name": "Rimosso",
|
||||
"explanation": "La risorsa è stata rimossa in modo intenzionale e definitivo: un segnale più forte di un 404.",
|
||||
"cause1": "Pagina dismessa di proposito; il server dice ai crawler di eliminarla",
|
||||
"todo1": "Rimuovi i link a questo URL: Google lo deindicizzerà più in fretta di un 404"
|
||||
},
|
||||
"429": {
|
||||
"name": "Troppe richieste (limite di velocità)",
|
||||
"explanation": "Il server sta limitando il ritmo del crawler: troppe richieste in troppo poco tempo da questo IP/User-Agent.",
|
||||
"cause1": "Il ritmo della scansione supera il limite per client del server",
|
||||
"cause2": "Hosting condiviso con limitazioni aggressive",
|
||||
"todo1": "Abbassa Concorrenza massima / URL/s massimi e aggiungi un Ritardo per worker (Impostazioni → Velocità)",
|
||||
"todo2": "Rispetta l'header Retry-After: vedi \"Dagli header di risposta\" qui sotto se il server lo ha inviato",
|
||||
"todo3": "Attiva \"Rispetta il Crawl-delay del robots.txt\" (Impostazioni → Scanner)"
|
||||
},
|
||||
"451": {
|
||||
"name": "Non disponibile per motivi legali",
|
||||
"explanation": "La risorsa è bloccata per motivi legali (provvedimento giudiziario, geoblocco per il GDPR, DMCA ecc.).",
|
||||
"cause1": "Contenuto con limitazioni geografiche",
|
||||
"cause2": "Rimozione imposta per legge",
|
||||
"todo1": "Niente: il blocco è voluto"
|
||||
},
|
||||
"500": {
|
||||
"name": "Errore interno del server",
|
||||
"explanation": "Si è verificato un errore non gestito nell'applicazione lato server durante la creazione della pagina.",
|
||||
"cause1": "Bug nel template della pagina / nel codice di backend",
|
||||
"cause2": "Database non disponibile",
|
||||
"cause3": "Configurazione errata sul server",
|
||||
"todo1": "Riprova dopo la scansione (i 500 transitori spariscono; quelli persistenti sono un bug reale)",
|
||||
"todo2": "Se i 500 compaiono sotto carico, la scansione potrebbe sovraccaricare un database: rallenta la scansione (Impostazioni → Velocità)"
|
||||
},
|
||||
"502": {
|
||||
"name": "Gateway non valido",
|
||||
"explanation": "Un reverse proxy / bilanciatore di carico davanti all'applicazione ha ricevuto una risposta non valida dal server applicativo upstream.",
|
||||
"cause1": "Il server applicativo upstream si è bloccato o ha restituito dati senza senso",
|
||||
"cause2": "L'upstream è sovraccarico: frequente sotto il carico della scansione",
|
||||
"cause3": "Timeout proxy↔upstream",
|
||||
"todo1": "Riduci il ritmo della scansione (Impostazioni → Velocità) se i 502 compaiono a raffica",
|
||||
"todo2": "Riprova gli URL interessati dopo la scansione"
|
||||
},
|
||||
"503": {
|
||||
"name": "Servizio non disponibile",
|
||||
"explanation": "Il server non può gestire temporaneamente la richiesta, di solito per sovraccarico o manutenzione. Spesso arriva con un header Retry-After.",
|
||||
"cause1": "Server sovraccarico: può causarlo la scansione stessa",
|
||||
"cause2": "Finestra di manutenzione programmata",
|
||||
"cause3": "Ritardo dello scaling automatico (nuove istanze in avvio)",
|
||||
"todo1": "Abbassa Concorrenza massima / URL/s massimi e aggiungi un Ritardo per worker (Impostazioni → Velocità)",
|
||||
"todo2": "Rispetta Retry-After (vedi \"Dagli header di risposta\" qui sotto)",
|
||||
"todo3": "Riprova gli URL interessati dopo la scansione"
|
||||
},
|
||||
"504": {
|
||||
"name": "Timeout del gateway",
|
||||
"explanation": "Un reverse proxy / bilanciatore di carico non ha ricevuto in tempo la risposta dal server applicativo upstream.",
|
||||
"cause1": "Query lenta al database / chiamata a un'API esterna nella pagina",
|
||||
"cause2": "Upstream sovraccarico: richieste in coda",
|
||||
"cause3": "Sotto il carico della scansione: la scansione ha saturato il pool di worker upstream",
|
||||
"todo1": "Riduci il ritmo della scansione (Impostazioni → Velocità)",
|
||||
"todo2": "Aumenta il Timeout di risposta (Impostazioni → Scanner) per le pagine davvero lente",
|
||||
"todo3": "Riprova gli URL interessati dopo la scansione"
|
||||
},
|
||||
"508": {
|
||||
"name": "Loop rilevato",
|
||||
"explanation": "Il server ha rilevato un loop infinito durante l'elaborazione della richiesta (spesso WebDAV o un loop di reindirizzamento lato server).",
|
||||
"cause1": "Loop di reindirizzamento lato server",
|
||||
"cause2": "Loop di include ricorsivi / link simbolici",
|
||||
"todo1": "Bug del server: il crawler non può farci nulla"
|
||||
},
|
||||
"509": {
|
||||
"name": "Limite di banda superato",
|
||||
"explanation": "Un codice non standard (cPanel/Apache) che indica che l'account di hosting ha raggiunto il limite mensile di banda.",
|
||||
"cause1": "Quota di banda dell'hosting condiviso esaurita",
|
||||
"todo1": "Il crawler non può farci nulla: il provider sta limitando l'intero account"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Dietro Cloudflare — cf-ray {{ray}}. Il codice di stato descrive il collegamento Cloudflare↔origine, non ciò che vede un browser.",
|
||||
"fronted": "Servito tramite {{front}}.",
|
||||
"origin": "Server di origine: {{server}}.",
|
||||
"retryAfterSeconds": "Il server ha inviato Retry-After: {{seconds}} s: chiede esplicitamente ai client di aspettare questo tempo. Una scansione rispettosa non dovrebbe richiedere di nuovo questo URL prima.",
|
||||
"retryAfter": "Il server ha inviato Retry-After: {{value}}.",
|
||||
"cfCache": "Stato della cache Cloudflare: {{status}}.",
|
||||
"xRobots": "X-Robots-Tag: {{value}} — influisce sull'indicizzabilità indipendentemente dallo stato."
|
||||
},
|
||||
"robots": {
|
||||
"title": "Non richiesto — bloccato da robots.txt",
|
||||
"explanation": "Il crawler non ha mai inviato una richiesta per questo URL: una regola del robots.txt per lo user-agent configurato lo blocca. Non c'è un codice di stato perché nessuna risposta è mai stata richiesta: non è un errore di connessione.",
|
||||
"cause1": "Una regola Disallow nel robots.txt del sito corrisponde a questo percorso per lo user-agent della scansione",
|
||||
"cause2": "L'URL riceve link da pagine scansionate, quindi viene registrato e contato, ma non viene mai scaricato",
|
||||
"todo1": "Niente, se l'esclusione è voluta: nemmeno Google lo scansionerà",
|
||||
"todo2": "Verifica se la pagina dovrebbe essere indicizzabile: un URL bloccato non trasmette autorità tramite i link e il suo contenuto non viene mai visto",
|
||||
"todo3": "Usa Aiuto → Tester robots.txt per vedere quale regola corrisponde, oppure disattiva \"Rispetta il robots.txt\" in Impostazioni → Scanner per analizzarlo comunque"
|
||||
},
|
||||
"network": {
|
||||
"title": "Nessuna risposta HTTP — errore di rete / connessione",
|
||||
"explanationDetail": "La richiesta non ha mai ricevuto una risposta HTTP. Il crawler ha registrato: \"{{detail}}\".",
|
||||
"explanation": "La richiesta non ha mai ricevuto una risposta HTTP: la risoluzione DNS, la connessione TCP, l'handshake TLS o la lettura della risposta non sono riusciti prima che arrivasse una riga di stato.",
|
||||
"cause1": "Il DNS non è riuscito a risolvere il nome host (errore di battitura, dominio non attivo o un nome risolvibile solo all'interno di una rete privata)",
|
||||
"cause2": "Connessione TCP rifiutata o scaduta (host non attivo, firewall, porta errata)",
|
||||
"cause3": "Handshake TLS non riuscito (certificato scaduto/non valido, protocollo incompatibile)",
|
||||
"cause4": "Connessione reimpostata o lettura scaduta a metà risposta",
|
||||
"cause5": "Richiesta interrotta dal crawler perché ha superato il Timeout di risposta (Impostazioni → Scanner) o il Tempo di risposta max (Impostazioni → Avanzate)",
|
||||
"todo1": "Verifica che il nome host venga risolto e che il sito si apra da un normale browser su questo computer",
|
||||
"todo2": "Se è raggiungibile ma lento, aumenta il Timeout di risposta (Impostazioni → Scanner)",
|
||||
"todo3": "Se fallisce solo sotto carico, abbassa Concorrenza massima / URL/s massimi (Impostazioni → Velocità): l'esaurimento della tabella delle connessioni dell'origine sembra un errore di rete al client",
|
||||
"todo4": "Riprova gli URL interessati dopo la scansione (le interruzioni transitorie spariscono al nuovo tentativo)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — Informativo",
|
||||
"fallback": "Informativo",
|
||||
"explanation": "Una risposta 1xx ({{text}}). Sono risposte intermedie; il crawler le considera transitorie e attende lo stato finale."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "Riuscito",
|
||||
"explanation": "La richiesta è riuscita. Nessuna diagnosi necessaria: questa sezione approfondisce solo i codici di errore e di reindirizzamento."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "Reindirizzamento",
|
||||
"explanation301": "Reindirizzamento permanente. I motori di ricerca trasferiscono i segnali di ranking alla destinazione e aggiornano l'indice con il nuovo URL.",
|
||||
"explanation302": "Reindirizzamento temporaneo. I motori di ricerca mantengono indicizzato l'URL originale. Usare un 302 a lungo termine è un errore SEO comune: di solito dovrebbe essere un 301.",
|
||||
"explanation308": "Reindirizzamento permanente (come un 301, ma il metodo della richiesta viene mantenuto).",
|
||||
"explanation304": "Non modificato: la copia in cache è ancora valida. Il crawler di norma invia richieste senza header condizionali, quindi è insolito.",
|
||||
"explanationOther": "Un reindirizzamento {{code}} ({{text}}).",
|
||||
"location": "L'header Location punta a: {{location}}",
|
||||
"noLocation": "Nessun header Location rilevato: insolito per un reindirizzamento.",
|
||||
"todoPermanent": "Se questo reindirizzamento è permanente, trasformalo in 301/308 così i segnali di ranking si consolidano",
|
||||
"todoChain": "Controlla la scheda Codici di risposta per vedere la catena completa di reindirizzamenti e la destinazione finale"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "Errore del client",
|
||||
"explanation": "Un codice 4xx significa che il server ha respinto la richiesta come errore del client (richiesta non valida, non consentita, non trovata ecc.). In una scansione, un 4xx persistente su pagine collegate di solito indica link interrotti; un 4xx sporadico può essere una protezione anti-bot.",
|
||||
"cause1": "Link interrotto o malformato",
|
||||
"cause2": "Protezione anti-bot / regola WAF",
|
||||
"cause3": "Metodo o contenuto rifiutato dal server",
|
||||
"todo1": "Controlla la scheda Link in entrata per vedere cosa punta a questo URL",
|
||||
"todo2": "Se sembra un blocco, prova uno User-Agent da browser e un ritmo più lento (Impostazioni → Richieste / Velocità)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "Errore del server",
|
||||
"explanation": "Un codice 5xx significa che il server non è riuscito a evadere una richiesta che aveva accettato come valida. Sotto il carico della scansione spesso è la scansione a sommergere l'origine, più che un vero bug della pagina.",
|
||||
"cause1": "Errore o blocco lato server",
|
||||
"cause2": "Origine sovraccarica, forse a causa della scansione stessa",
|
||||
"cause3": "Errore upstream/del proxy",
|
||||
"todo1": "Abbassa Concorrenza massima / URL/s massimi e aggiungi un Ritardo per worker (Impostazioni → Velocità) se i 5xx compaiono a raffica",
|
||||
"todo2": "Aumenta i Tentativi (Impostazioni → Velocità) e scansiona di nuovo gli URL interessati"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "Questa pagina non ha titoli rilevati (nessun elemento",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL",
|
||||
"percentOfTotal": "전체 대비 %",
|
||||
"siteSoft404": "없는 페이지가 {{status}} 반환 (soft 404)",
|
||||
"siteSoft404Hint": "존재하지 않는 페이지를 요청했을 때 404 대신 {{status}} 응답을 받았습니다. 따라서 이 사이트의 깨진 링크가 검색 엔진에는 정상 페이지처럼 보입니다."
|
||||
"siteSoft404Hint": "존재하지 않는 페이지를 요청했을 때 404 대신 {{status}} 응답을 받았습니다. 따라서 이 사이트의 깨진 링크가 검색 엔진에는 정상 페이지처럼 보입니다.",
|
||||
"siteBotBlocked": "봇 보호가 크롤의 일부를 차단했습니다",
|
||||
"siteBotBlockedHint": "사이트의 봇 보호가 이 페이지 대신 확인 페이지로 응답했습니다. 이 페이지는 크롤되지 않았으며 깨진 링크 및 사이트맵 검사에서 제외됩니다. 크롤 속도를 낮추거나 크롤러의 User-Agent를 허용 목록에 추가한 뒤 다시 크롤하세요."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "자신의 자격 증명으로 외부 서비스를 연결하십시오. API 키와 비밀 값은 운영체제의 보안 저장소를 통해 암호화되어 저장되며 이 컴퓨터를 벗어나지 않습니다.",
|
||||
@@ -382,7 +384,50 @@
|
||||
"orphanGscImpressions_other": "노출 {{formatted}}회",
|
||||
"orphanGa4Sessions_other": "GA4 세션 {{formatted}}회",
|
||||
"orphanMetricaVisits_other": "Metrica 방문 {{formatted}}회(랜딩)",
|
||||
"orphanMetricaPageviews_other": "Metrica 페이지뷰 {{formatted}}회(히트)"
|
||||
"orphanMetricaPageviews_other": "Metrica 페이지뷰 {{formatted}}회(히트)",
|
||||
"noResponse": "응답 없음",
|
||||
"bucketOrphan": "0 (고립)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "불가",
|
||||
"warn": "경고",
|
||||
"bad": "나쁨",
|
||||
"err": "오류",
|
||||
"slow": "느림",
|
||||
"net": "네트워크"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} 정상 · 오류 {{rate}}%",
|
||||
"avgMs": "평균 {{ms}}ms",
|
||||
"distinctIds_other": "고유 ID {{count}}개",
|
||||
"pageCount_other": "{{formatted}}개 페이지",
|
||||
"imageCount_other": "이미지 {{formatted}}개",
|
||||
"hits_other": "{{formatted}}회",
|
||||
"avgWords": "평균 {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "출처: {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "내비게이션",
|
||||
"header": "헤더",
|
||||
"content": "본문",
|
||||
"sidebar": "사이드바",
|
||||
"footer": "푸터",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "색인 가능",
|
||||
"noindex": "색인 불가 — noindex",
|
||||
"canonical": "색인 불가 — canonical 처리됨",
|
||||
"robotsBlocked": "색인 불가 — robots.txt로 차단됨",
|
||||
"redirect": "색인 불가 — 리다이렉트",
|
||||
"clientError": "색인 불가 — 4xx",
|
||||
"serverError": "색인 불가 — 5xx",
|
||||
"botBlocked": "알 수 없음 — 봇 보호로 차단됨"
|
||||
},
|
||||
"kind": {
|
||||
"image": "이미지",
|
||||
"font": "글꼴",
|
||||
"other": "기타"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "사이트맵 검사기",
|
||||
@@ -1028,7 +1073,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "응답 헤더 기준",
|
||||
"likelyCauses": "가능한 원인",
|
||||
"whatToDo": "조치 방법"
|
||||
"whatToDo": "조치 방법",
|
||||
"cloudflareStyle": "Cloudflare 방식 엣지 코드",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "웹 서버가 알 수 없는 오류를 반환함",
|
||||
"explanation": "Cloudflare가 원본 서버에 연결했지만 비어 있거나 형식이 잘못됐거나 예상치 못한 응답을 받았습니다(예: 응답 도중 연결이 재설정됨, 원본이 너무 큰 헤더를 반환함, 원본 프로세스가 비정상 종료됨). \"원본에서 무언가 잘못됐다\"는 뜻의 포괄적인 코드입니다.",
|
||||
"cause1": "요청을 처리하는 동안 원본 서버가 다운되거나 재시작되었거나 워커/메모리가 부족해졌습니다",
|
||||
"cause2": "원본이 응답을 끝까지 보내기 전에 TCP 연결을 닫았습니다(대개 애플리케이션 수준의 시간 초과나 메모리 부족으로 인한 강제 종료)",
|
||||
"cause3": "크롤의 요청 속도가 원본의 프로세스 풀을 압도했습니다 — 부하 상황에서 한꺼번에 나타나는 520은 거의 항상 이 경우입니다",
|
||||
"cause4": "원본이 Cloudflare가 허용하는 것보다 큰 응답 헤더를 반환했습니다(드묾, 대개 애플리케이션 설정 오류)",
|
||||
"todo1": "여러 URL에서 520이 한꺼번에 나타나면 최대 동시 실행 수 / 최대 URL/초를 낮추고 워커별 지연을 추가하세요(설정 → 속도) — 크롤이 원본에 과부하를 주고 있습니다",
|
||||
"todo2": "크롤이 끝난 뒤 영향을 받은 URL을 다시 크롤하세요(일시적인 520은 재시도하면 대개 사라집니다 — 설정 → 속도에서 재시도 횟수를 늘리세요)",
|
||||
"todo3": "특정 URL만 계속 520을 반환한다면 해당 페이지에 서버 측 버그가 있는 것입니다 — 사이트 소유자에게 알리세요"
|
||||
},
|
||||
"521": {
|
||||
"name": "웹 서버가 다운됨",
|
||||
"explanation": "Cloudflare가 원본 서버와 TCP 연결을 열 수 없었습니다 — 원본이 연결을 적극적으로 거부했거나 예상 포트에서 수신 대기하지 않습니다.",
|
||||
"cause1": "원본 웹 서버(nginx/Apache/Node 등)가 중지되었거나 다운되었습니다",
|
||||
"cause2": "원본 방화벽이 Cloudflare의 IP 대역을 차단하고 있습니다(설정 오류 — 원본은 Cloudflare를 허용해야 합니다)",
|
||||
"cause3": "원본 서버가 새 연결을 받을 수 없을 만큼 과부하 상태입니다",
|
||||
"todo1": "원본 가용성 문제로, 크롤러가 우회할 수 있는 문제가 아닙니다 — 이 URL들에 대해 사이트는 사실상 다운된 상태입니다",
|
||||
"todo2": "나중에 다시 크롤하세요. 계속되면 사이트가 고장 난 것입니다"
|
||||
},
|
||||
"522": {
|
||||
"name": "연결 시간 초과",
|
||||
"explanation": "Cloudflare가 원본과 TCP 핸드셰이크를 시작했지만 제한 시간 안에 완료되지 않았습니다 — 원본이 SYN을 받고 응답하지 않았거나, 중간의 네트워크 장비가 패킷을 버렸습니다.",
|
||||
"cause1": "원본이 심각한 과부하 상태입니다(TCP 백로그가 가득 참)",
|
||||
"cause2": "원본 방화벽이 Cloudflare의 패킷을 거부하지 않고 조용히 버리고 있습니다",
|
||||
"cause3": "Cloudflare와 원본 사이의 네트워크/라우팅 문제",
|
||||
"cause4": "크롤 부하 상황: 크롤의 병렬 요청이 원본의 연결 테이블을 고갈시켰습니다",
|
||||
"todo1": "동시 실행 수를 낮추고(설정 → 속도 → 최대 동시 실행 수) 다시 크롤하세요 — 크롤 도중에 나타난다면 대개 연결 테이블 고갈이 원인입니다",
|
||||
"todo2": "크롤이 끝난 뒤 영향을 받은 URL을 다시 시도하세요"
|
||||
},
|
||||
"523": {
|
||||
"name": "원본에 연결할 수 없음",
|
||||
"explanation": "Cloudflare가 원본 서버로 전혀 라우팅할 수 없었습니다 — 대개 Cloudflare의 원본 레코드에 DNS 문제가 있거나 원본 네트워크가 다운된 경우입니다.",
|
||||
"cause1": "Cloudflare에 있는 사이트의 DNS 레코드가 잘못되었거나 작동하지 않는 IP를 가리킵니다",
|
||||
"cause2": "원본 호스팅 업체에 네트워크 장애가 있습니다",
|
||||
"cause3": "Cloudflare와 원본 사이의 BGP / 라우팅 장애",
|
||||
"todo1": "크롤러가 할 수 있는 일은 없습니다 — 원본의 DNS/네트워크 설정 오류입니다"
|
||||
},
|
||||
"524": {
|
||||
"name": "시간 초과 발생",
|
||||
"explanation": "Cloudflare가 원본과 TCP 연결은 성공적으로 맺었지만, 원본이 Cloudflare의 제한 시간(기본 100초) 안에 완전한 HTTP 응답을 보내지 않았습니다. 원본은 살아 있지만 이 요청에서는 너무 느립니다.",
|
||||
"cause1": "페이지에 느린 데이터베이스 쿼리, 외부 API 호출 또는 무거운 연산이 있습니다",
|
||||
"cause2": "원본이 과부하 상태입니다 — 요청이 포화된 워커 풀 뒤에서 대기하고 있습니다",
|
||||
"cause3": "크롤 부하 상황: 크롤이 원본의 요청 큐를 채워 이 URL이 너무 오래 기다렸습니다",
|
||||
"todo1": "원본이 포화되지 않도록 최대 동시 실행 수 / 최대 URL/초를 낮추고 워커별 지연을 추가하세요(설정 → 속도)",
|
||||
"todo2": "정말 느린 페이지에 시간을 더 주려면 응답 제한 시간을 늘리세요(설정 → 크롤러)",
|
||||
"todo3": "크롤이 끝난 뒤 영향을 받은 URL을 다시 시도하세요"
|
||||
},
|
||||
"525": {
|
||||
"name": "SSL 핸드셰이크 실패",
|
||||
"explanation": "Cloudflare와 원본 서버 사이의 TLS 핸드셰이크가 실패했습니다. 이는 주소 표시줄에서 보이는 브라우저↔Cloudflare 구간이 아니라 Cloudflare↔원본 구간이므로, 일반 방문자에게는 사이트가 정상으로 보이면서도 525가 날 수 있습니다.",
|
||||
"cause1": "원본의 TLS 인증서가 만료되었거나 자체 서명되었거나 다른 호스트 이름용입니다(Cloudflare가 'Full (strict)' 모드일 때)",
|
||||
"cause2": "원본이 Cloudflare가 거부하는 오래된 TLS 버전 / 암호 스위트만 지원합니다",
|
||||
"cause3": "원본 서버가 과부하 상태라 TLS 핸드셰이크를 버리고 있습니다",
|
||||
"todo1": "원본 TLS 설정 오류로, 크롤러가 바꿀 수 있는 것이 아닙니다. 호스트의 인증서를 확인하세요(HTTPS라면 URL 상세의 TLS Cert 행)"
|
||||
},
|
||||
"526": {
|
||||
"name": "잘못된 SSL 인증서",
|
||||
"explanation": "Cloudflare('Full (strict)' SSL 모드)가 원본 서버의 TLS 인증서를 검증하지 못했습니다.",
|
||||
"cause1": "원본 인증서가 만료되었거나 자체 서명되었거나 다른 호스트 이름용으로 발급되었습니다",
|
||||
"cause2": "원본의 인증서 체인이 불완전합니다(중간 인증서 누락)",
|
||||
"todo1": "원본 인증서 문제로, 크롤러가 할 수 있는 일은 없습니다"
|
||||
},
|
||||
"527": {
|
||||
"name": "Railgun 오류(Cloudflare, 지원 종료)",
|
||||
"explanation": "Cloudflare Railgun(이미 서비스가 종료된 원본 가속 제품)의 연결 오류입니다. 사실상 쓰이지 않는 코드이며, 이 코드가 보인다면 사이트가 아주 오래된 Cloudflare 설정을 사용하고 있는 것입니다.",
|
||||
"cause1": "구형 Cloudflare Railgun 연결 실패",
|
||||
"todo1": "크롤러 쪽에서 할 수 있는 일은 없습니다"
|
||||
},
|
||||
"530": {
|
||||
"name": "Cloudflare 오류(대개 방화벽 / WAF 차단 — 오류 1020)",
|
||||
"explanation": "530은 Cloudflare가 \"오류가 발생했으니 본문의 1xxx 오류 코드를 확인하라\"고 알리는 방식입니다. 크롤에서는 거의 항상 1020 \"Access Denied\"로, Cloudflare Firewall / WAF / Bot-Fight 규칙이 요청을 차단한 것입니다. 드물게 1016(원본 DNS 오류)이나 다른 1xxx 코드와 함께 나타납니다.",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode가 IP, User-Agent, 요청 속도 또는 브라우저 지문 부재를 근거로 크롤러를 표시했습니다",
|
||||
"cause2": "사이트에 크롤러가 통과할 수 없는 \"managed challenge\"가 있습니다",
|
||||
"cause3": "(드물게) 차단이 아니라 원본 DNS 오류(1016)",
|
||||
"todo1": "브라우저와 같은 User-Agent를 설정하세요(설정 → 요청) — 기본 크롤러 UA가 가장 흔한 원인입니다",
|
||||
"todo2": "최대 동시 실행 수 / 최대 URL/초를 낮추고 워커별 지연을 추가하세요(설정 → 속도) — 한 IP에서 오는 빠른 요청은 속도 기반 규칙에 걸립니다",
|
||||
"todo3": "robots.txt와 선언된 Crawl-delay를 준수하세요(설정 → 크롤러)",
|
||||
"todo4": "사이트가 본인 소유라면 Cloudflare에서 크롤러를 허용 목록에 추가하세요. 그렇지 않다면 사이트가 의도적으로 자동 접근을 막고 있는 것입니다"
|
||||
},
|
||||
"401": {
|
||||
"name": "인증되지 않음",
|
||||
"explanation": "리소스에 인증이 필요한데 자격 증명이 전송되지 않았거나 잘못되었습니다.",
|
||||
"cause1": "페이지가 HTTP Basic/Bearer 인증 뒤에 있습니다",
|
||||
"cause2": "세션 쿠키가 필요한데 전송되지 않았습니다",
|
||||
"todo1": "자격 증명이 있다면 설정 → 인증에서 구성하세요",
|
||||
"todo2": "사이트가 세션 뒤에 콘텐츠를 두고 있다면 설정 → 쿠키 → \"모두 허용\"을 사용하세요"
|
||||
},
|
||||
"403": {
|
||||
"name": "금지됨",
|
||||
"explanation": "서버가 요청을 이해했지만 제공을 거부합니다. 크롤에서는 실제 권한 문제라기보다 봇 차단이나 WAF 규칙인 경우가 대부분입니다.",
|
||||
"cause1": "크롤러의 User-Agent, IP 또는 요청 속도가 WAF / 봇 차단 규칙을 작동시켰습니다",
|
||||
"cause2": "디렉터리나 파일이 실제로 익명 사용자에게 공개되지 않습니다",
|
||||
"cause3": "핫링크 / 리퍼러 보호",
|
||||
"todo1": "브라우저와 같은 User-Agent를 시도해 보세요(설정 → 요청)",
|
||||
"todo2": "크롤 속도를 낮추세요(설정 → 속도)",
|
||||
"todo3": "사이트 전체에서 발생한다면 사이트가 자동 접근을 적극적으로 차단하고 있는 것입니다"
|
||||
},
|
||||
"404": {
|
||||
"name": "찾을 수 없음",
|
||||
"explanation": "서버에 이 URL의 리소스가 없습니다.",
|
||||
"cause1": "깨진 내부/외부 링크",
|
||||
"cause2": "페이지가 리다이렉트 없이 삭제되었습니다",
|
||||
"cause3": "링크 href의 오타",
|
||||
"todo1": "링크를 건 페이지를 수정하거나 링크를 제거하세요 — 누가 여기에 링크했는지는 인바운드 링크 탭에서 확인할 수 있습니다"
|
||||
},
|
||||
"408": {
|
||||
"name": "요청 시간 초과",
|
||||
"explanation": "서버가 요청이 완료되기를 기다리다 시간이 초과되었습니다.",
|
||||
"cause1": "크롤러와 서버 사이의 네트워크가 느립니다",
|
||||
"cause2": "서버 측 연결 제한 시간이 매우 낮게 설정되어 있습니다",
|
||||
"todo1": "대개 일시적입니다 — 재시도 횟수를 늘리세요(설정 → 속도)"
|
||||
},
|
||||
"410": {
|
||||
"name": "영구 삭제됨",
|
||||
"explanation": "리소스가 의도적으로 영구 삭제되었습니다 — 404보다 강한 신호입니다.",
|
||||
"cause1": "페이지를 의도적으로 내렸으며, 서버가 크롤러에게 이 페이지를 제외하라고 알리고 있습니다",
|
||||
"todo1": "이 URL로 가는 링크를 제거하세요 — Google은 404보다 더 빨리 색인에서 제외합니다"
|
||||
},
|
||||
"429": {
|
||||
"name": "요청이 너무 많음(속도 제한)",
|
||||
"explanation": "서버가 크롤러의 속도를 제한하고 있습니다 — 이 IP/User-Agent에서 너무 짧은 시간에 너무 많은 요청이 왔습니다.",
|
||||
"cause1": "크롤 속도가 서버의 클라이언트당 한도를 넘었습니다",
|
||||
"cause2": "공격적으로 속도를 제한하는 공유 호스팅",
|
||||
"todo1": "최대 동시 실행 수 / 최대 URL/초를 낮추고 워커별 지연을 추가하세요(설정 → 속도)",
|
||||
"todo2": "Retry-After 헤더를 따르세요 — 서버가 보냈다면 아래 \"응답 헤더 기준\"을 확인하세요",
|
||||
"todo3": "\"robots.txt의 Crawl-delay 준수\"를 켜세요(설정 → 크롤러)"
|
||||
},
|
||||
"451": {
|
||||
"name": "법적 사유로 이용 불가",
|
||||
"explanation": "리소스가 법적 사유(법원 명령, GDPR 지역 차단, DMCA 등)로 차단되었습니다.",
|
||||
"cause1": "지역 제한 콘텐츠",
|
||||
"cause2": "법적으로 요구된 게시 중단",
|
||||
"todo1": "할 일이 없습니다 — 의도된 차단입니다"
|
||||
},
|
||||
"500": {
|
||||
"name": "내부 서버 오류",
|
||||
"explanation": "페이지를 생성하는 중 서버 측 애플리케이션에서 처리되지 않은 오류가 발생했습니다.",
|
||||
"cause1": "페이지 템플릿 / 백엔드 코드의 버그",
|
||||
"cause2": "데이터베이스를 사용할 수 없음",
|
||||
"cause3": "서버 설정 오류",
|
||||
"todo1": "크롤이 끝난 뒤 다시 시도하세요(일시적인 500은 사라지고, 계속되는 500은 실제 버그입니다)",
|
||||
"todo2": "부하 상황에서 500이 나타난다면 크롤이 데이터베이스에 과부하를 주고 있을 수 있습니다 — 크롤 속도를 늦추세요(설정 → 속도)"
|
||||
},
|
||||
"502": {
|
||||
"name": "잘못된 게이트웨이",
|
||||
"explanation": "애플리케이션 앞의 리버스 프록시 / 로드 밸런서가 업스트림 애플리케이션 서버로부터 잘못된 응답을 받았습니다.",
|
||||
"cause1": "업스트림 애플리케이션 서버가 다운되었거나 엉뚱한 데이터를 반환했습니다",
|
||||
"cause2": "업스트림이 과부하 상태입니다 — 크롤 부하 상황에서 흔합니다",
|
||||
"cause3": "프록시↔업스트림 시간 초과",
|
||||
"todo1": "502가 한꺼번에 나타나면 크롤 속도를 낮추세요(설정 → 속도)",
|
||||
"todo2": "크롤이 끝난 뒤 영향을 받은 URL을 다시 시도하세요"
|
||||
},
|
||||
"503": {
|
||||
"name": "서비스를 사용할 수 없음",
|
||||
"explanation": "서버가 일시적으로 요청을 처리할 수 없습니다 — 대개 과부하나 유지보수 때문입니다. Retry-After 헤더가 함께 오는 경우가 많습니다.",
|
||||
"cause1": "서버 과부하 — 크롤 자체가 원인일 수 있습니다",
|
||||
"cause2": "예정된 유지보수 시간",
|
||||
"cause3": "자동 확장 지연(새 인스턴스가 시작되는 중)",
|
||||
"todo1": "최대 동시 실행 수 / 최대 URL/초를 낮추고 워커별 지연을 추가하세요(설정 → 속도)",
|
||||
"todo2": "Retry-After를 따르세요(아래 \"응답 헤더 기준\" 참고)",
|
||||
"todo3": "크롤이 끝난 뒤 영향을 받은 URL을 다시 시도하세요"
|
||||
},
|
||||
"504": {
|
||||
"name": "게이트웨이 시간 초과",
|
||||
"explanation": "리버스 프록시 / 로드 밸런서가 업스트림 애플리케이션 서버로부터 제때 응답을 받지 못했습니다.",
|
||||
"cause1": "페이지의 느린 데이터베이스 쿼리 / 외부 API 호출",
|
||||
"cause2": "업스트림 과부하 — 요청이 대기열에 쌓여 있습니다",
|
||||
"cause3": "크롤 부하 상황: 크롤이 업스트림 워커 풀을 포화시켰습니다",
|
||||
"todo1": "크롤 속도를 낮추세요(설정 → 속도)",
|
||||
"todo2": "정말 느린 페이지라면 응답 제한 시간을 늘리세요(설정 → 크롤러)",
|
||||
"todo3": "크롤이 끝난 뒤 영향을 받은 URL을 다시 시도하세요"
|
||||
},
|
||||
"508": {
|
||||
"name": "루프 감지됨",
|
||||
"explanation": "서버가 요청을 처리하는 중 무한 루프를 감지했습니다(대개 WebDAV 또는 서버 측 리다이렉트 루프).",
|
||||
"cause1": "서버 측 리다이렉트 루프",
|
||||
"cause2": "재귀 include / 심볼릭 링크 루프",
|
||||
"todo1": "서버 버그로, 크롤러가 할 수 있는 일은 없습니다"
|
||||
},
|
||||
"509": {
|
||||
"name": "대역폭 한도 초과",
|
||||
"explanation": "호스팅 계정이 월간 대역폭 한도에 도달했다는 뜻의 비표준 코드(cPanel/Apache)입니다.",
|
||||
"cause1": "공유 호스팅의 대역폭 할당량 소진",
|
||||
"todo1": "크롤러가 할 수 있는 일은 없습니다 — 호스팅 업체가 계정 전체를 제한하고 있습니다"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Cloudflare 뒤에 있음 — cf-ray {{ray}}. 상태 코드는 브라우저가 보는 결과가 아니라 Cloudflare↔원본 구간을 설명합니다.",
|
||||
"fronted": "{{front}}을(를) 거쳐 제공됩니다.",
|
||||
"origin": "원본 서버: {{server}}.",
|
||||
"retryAfterSeconds": "서버가 Retry-After: {{seconds}}초를 보냈습니다 — 클라이언트에게 그동안 기다리라고 명시적으로 요청하는 것입니다. 예의 바른 크롤이라면 그 전에 이 URL을 다시 요청하지 않아야 합니다.",
|
||||
"retryAfter": "서버가 Retry-After: {{value}}을(를) 보냈습니다.",
|
||||
"cfCache": "Cloudflare 캐시 상태: {{status}}.",
|
||||
"xRobots": "X-Robots-Tag: {{value}} — 상태 코드와 관계없이 색인 가능성에 영향을 줍니다."
|
||||
},
|
||||
"robots": {
|
||||
"title": "요청하지 않음 — robots.txt가 허용하지 않음",
|
||||
"explanation": "크롤러는 이 URL에 요청을 보낸 적이 없습니다. 설정된 user-agent에 대한 robots.txt 규칙이 이 URL을 허용하지 않기 때문입니다. 응답을 요청한 적이 없으므로 상태 코드가 없으며, 연결 실패가 아닙니다.",
|
||||
"cause1": "사이트 robots.txt의 Disallow 규칙이 크롤 user-agent에 대해 이 경로와 일치합니다",
|
||||
"cause2": "크롤한 페이지에서 이 URL로 링크가 걸려 있어 기록되고 집계되지만, 실제로 가져오지는 않습니다",
|
||||
"todo1": "의도한 제외라면 할 일이 없습니다 — Google도 이 URL을 크롤하지 않습니다",
|
||||
"todo2": "이 페이지가 색인되어야 하는지 확인하세요. 차단된 URL은 링크 가치를 전달하지 못하고 콘텐츠도 노출되지 않습니다",
|
||||
"todo3": "도움말 → robots.txt 테스터에서 어떤 규칙이 일치하는지 확인하거나, 그래도 점검하려면 설정 → 크롤러에서 \"robots.txt 준수\"를 끄세요"
|
||||
},
|
||||
"network": {
|
||||
"title": "HTTP 응답 없음 — 네트워크 / 연결 오류",
|
||||
"explanationDetail": "요청이 HTTP 응답을 받지 못했습니다. 크롤러가 기록한 내용: \"{{detail}}\".",
|
||||
"explanation": "요청이 HTTP 응답을 받지 못했습니다 — 상태 줄이 도착하기 전에 DNS 조회, TCP 연결, TLS 핸드셰이크 또는 응답 읽기가 실패했습니다.",
|
||||
"cause1": "DNS가 호스트 이름을 확인하지 못했습니다(오타, 사용하지 않는 도메인, 또는 사설망 안에서만 확인되는 이름)",
|
||||
"cause2": "TCP 연결이 거부되었거나 시간 초과되었습니다(호스트 다운, 방화벽, 잘못된 포트)",
|
||||
"cause3": "TLS 핸드셰이크 실패(만료되었거나 잘못된 인증서, 프로토콜 불일치)",
|
||||
"cause4": "응답 도중 연결이 재설정되었거나 읽기 시간이 초과되었습니다",
|
||||
"cause5": "요청이 응답 제한 시간(설정 → 크롤러) 또는 최대 응답 시간(설정 → 고급)을 넘어 크롤러가 중단했습니다",
|
||||
"todo1": "호스트 이름이 확인되는지, 이 컴퓨터의 일반 브라우저에서 사이트에 접속되는지 확인하세요",
|
||||
"todo2": "접속은 되지만 느리다면 응답 제한 시간을 늘리세요(설정 → 크롤러)",
|
||||
"todo3": "부하 상황에서만 실패한다면 최대 동시 실행 수 / 최대 URL/초를 낮추세요(설정 → 속도) — 원본의 연결 테이블 고갈은 클라이언트에게 네트워크 오류처럼 보입니다",
|
||||
"todo4": "크롤이 끝난 뒤 영향을 받은 URL을 다시 시도하세요(일시적인 연결 재설정은 재시도하면 해결됩니다)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — 정보",
|
||||
"fallback": "정보",
|
||||
"explanation": "1xx 응답({{text}})입니다. 중간 응답이므로 크롤러는 이를 일시적인 것으로 보고 최종 상태를 기다립니다."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "성공",
|
||||
"explanation": "요청이 성공했습니다. 진단할 내용이 없습니다 — 이 섹션은 오류 및 리다이렉트 코드에 대해서만 추가 설명을 제공합니다."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "리다이렉트",
|
||||
"explanation301": "영구 리다이렉트입니다. 검색 엔진은 순위 신호를 대상 URL로 넘기고 색인을 새 URL로 갱신합니다.",
|
||||
"explanation302": "임시 리다이렉트입니다. 검색 엔진은 원래 URL을 색인에 유지합니다. 302를 오래 쓰는 것은 흔한 SEO 실수이며, 대개 301이어야 합니다.",
|
||||
"explanation308": "영구 리다이렉트입니다(301과 같지만 요청 메서드가 유지됩니다).",
|
||||
"explanation304": "수정되지 않음 — 캐시된 사본이 아직 유효합니다. 크롤러는 보통 조건부 헤더 없이 요청하므로 이례적인 경우입니다.",
|
||||
"explanationOther": "{{code}} 리다이렉트({{text}})입니다.",
|
||||
"location": "Location 헤더가 가리키는 곳: {{location}}",
|
||||
"noLocation": "Location 헤더가 수집되지 않았습니다 — 리다이렉트로서는 이례적입니다.",
|
||||
"todoPermanent": "이 리다이렉트가 영구적이라면 순위 신호가 통합되도록 301/308로 바꾸세요",
|
||||
"todoChain": "응답 코드 탭에서 전체 리다이렉트 체인과 최종 대상을 확인하세요"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "클라이언트 오류",
|
||||
"explanation": "4xx 코드는 서버가 요청을 클라이언트의 잘못으로 보고 거부했다는 뜻입니다(잘못된 요청, 허용되지 않음, 찾을 수 없음 등). 크롤에서 링크가 걸린 페이지가 계속 4xx라면 대개 깨진 링크이고, 간헐적인 4xx는 봇 차단일 수 있습니다.",
|
||||
"cause1": "깨졌거나 형식이 잘못된 링크",
|
||||
"cause2": "봇 차단 / WAF 규칙",
|
||||
"cause3": "서버가 거부하는 메서드나 콘텐츠",
|
||||
"todo1": "인바운드 링크 탭에서 이 URL로 링크하는 곳을 확인하세요",
|
||||
"todo2": "차단처럼 보인다면 브라우저와 같은 User-Agent와 더 느린 속도를 시도하세요(설정 → 요청 / 속도)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "서버 오류",
|
||||
"explanation": "5xx 코드는 서버가 유효하다고 받아들인 요청을 처리하지 못했다는 뜻입니다. 크롤 부하 상황에서는 실제 페이지 버그보다 크롤이 원본을 압도한 경우가 많습니다.",
|
||||
"cause1": "서버 측 오류 또는 다운",
|
||||
"cause2": "원본 과부하 — 크롤 자체가 원인일 수 있습니다",
|
||||
"cause3": "업스트림/프록시 장애",
|
||||
"todo1": "5xx가 한꺼번에 나타나면 최대 동시 실행 수 / 최대 URL/초를 낮추고 워커별 지연을 추가하세요(설정 → 속도)",
|
||||
"todo2": "재시도 횟수를 늘리고(설정 → 속도) 영향을 받은 URL을 다시 크롤하세요"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "이 페이지에서는 제목이 감지되지 않았습니다(",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URLs",
|
||||
"percentOfTotal": "% do total",
|
||||
"siteSoft404": "Páginas inexistentes retornam {{status}} (soft 404)",
|
||||
"siteSoft404Hint": "Uma solicitação para uma página que não existe recebeu {{status}} em vez de 404, então os links quebrados deste site parecem páginas válidas para os buscadores."
|
||||
"siteSoft404Hint": "Uma solicitação para uma página que não existe recebeu {{status}} em vez de 404, então os links quebrados deste site parecem páginas válidas para os buscadores.",
|
||||
"siteBotBlocked": "A proteção contra bots bloqueou parte do rastreamento",
|
||||
"siteBotBlockedHint": "A proteção contra bots do site respondeu com um desafio no lugar destas páginas. Elas não foram rastreadas e ficam fora das verificações de links quebrados e de sitemap. Reduza a velocidade de rastreamento ou libere o user agent do rastreador e rastreie-as novamente."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "Conecte serviços externos com suas próprias credenciais. As chaves de API e os segredos são criptografados em repouso pelo armazenamento seguro do seu sistema operacional e nunca saem desta máquina.",
|
||||
@@ -406,7 +408,58 @@
|
||||
"orphanMetricaVisits_other": "{{formatted}} visitas do Metrica (entrada)",
|
||||
"orphanMetricaPageviews_one": "{{formatted}} visualização do Metrica (hits)",
|
||||
"orphanMetricaPageviews_many": "{{formatted}} de visualizações do Metrica (hits)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} visualizações do Metrica (hits)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} visualizações do Metrica (hits)",
|
||||
"noResponse": "Sem resposta",
|
||||
"bucketOrphan": "0 (órfã)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "NÃO",
|
||||
"warn": "AVISO",
|
||||
"bad": "RUIM",
|
||||
"err": "ERRO",
|
||||
"slow": "LENTO",
|
||||
"net": "REDE"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} OK · {{rate}}% de erros",
|
||||
"avgMs": "média {{ms}} ms",
|
||||
"distinctIds_one": "{{count}} ID distinto",
|
||||
"distinctIds_many": "{{count}} IDs distintos",
|
||||
"distinctIds_other": "{{count}} IDs distintos",
|
||||
"pageCount_one": "{{formatted}} página",
|
||||
"pageCount_many": "{{formatted}} páginas",
|
||||
"pageCount_other": "{{formatted}} páginas",
|
||||
"imageCount_one": "{{formatted}} imagem",
|
||||
"imageCount_many": "{{formatted}} imagens",
|
||||
"imageCount_other": "{{formatted}} imagens",
|
||||
"hits_one": "{{formatted}} ocorrência",
|
||||
"hits_many": "{{formatted}} ocorrências",
|
||||
"hits_other": "{{formatted}} ocorrências",
|
||||
"avgWords": "média {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "de {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "Navegação",
|
||||
"header": "Cabeçalho",
|
||||
"content": "Conteúdo",
|
||||
"sidebar": "Barra lateral",
|
||||
"footer": "Rodapé",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "Indexável",
|
||||
"noindex": "Não indexável — noindex",
|
||||
"canonical": "Não indexável — canonicalizada",
|
||||
"robotsBlocked": "Não indexável — bloqueada pelo robots.txt",
|
||||
"redirect": "Não indexável — redirecionamento",
|
||||
"clientError": "Não indexável — 4xx",
|
||||
"serverError": "Não indexável — 5xx",
|
||||
"botBlocked": "Desconhecida — bloqueada pela proteção contra bots"
|
||||
},
|
||||
"kind": {
|
||||
"image": "Imagem",
|
||||
"font": "Fonte",
|
||||
"other": "Outro"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "Validador de sitemap",
|
||||
@@ -1070,7 +1123,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "Pelos cabeçalhos de resposta",
|
||||
"likelyCauses": "Causas prováveis",
|
||||
"whatToDo": "O que fazer"
|
||||
"whatToDo": "O que fazer",
|
||||
"cloudflareStyle": "código de edge no estilo Cloudflare",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "O servidor web retornou um erro desconhecido",
|
||||
"explanation": "O Cloudflare se conectou ao servidor de origem, mas recebeu uma resposta vazia, malformada ou inesperada (por exemplo, a conexão caiu no meio da resposta, a origem retornou cabeçalhos grandes demais ou o processo da origem travou). É um código genérico para 'algo deu errado na origem'.",
|
||||
"cause1": "O servidor de origem travou, reiniciou ou ficou sem workers/memória enquanto atendia a requisição",
|
||||
"cause2": "A origem fechou a conexão TCP antes de enviar uma resposta completa (muitas vezes um tempo limite da aplicação ou um encerramento por falta de memória)",
|
||||
"cause3": "A taxa de requisições do rastreamento sobrecarregou o pool de processos da origem — 520s em rajadas sob carga quase sempre significam isso",
|
||||
"cause4": "A origem retornou cabeçalhos de resposta maiores do que o Cloudflare permite (raro; geralmente uma aplicação mal configurada)",
|
||||
"todo1": "Se os 520s aparecerem em rajadas em muitas URLs, reduza Concorrência máxima / URL/s máximo e adicione um Atraso por worker (Configurações → Velocidade) — o rastreamento está sobrecarregando a origem",
|
||||
"todo2": "Rastreie de novo as URLs afetadas quando o rastreamento terminar (520s transitórios costumam sumir ao tentar de novo — aumente as Tentativas em Configurações → Velocidade)",
|
||||
"todo3": "Se apenas URLs específicas retornam 520 sempre, essas páginas têm um bug no servidor — avise o dono do site"
|
||||
},
|
||||
"521": {
|
||||
"name": "O servidor web está fora do ar",
|
||||
"explanation": "O Cloudflare não conseguiu abrir uma conexão TCP com o servidor de origem — a origem recusou ativamente a conexão ou não está escutando na porta esperada.",
|
||||
"cause1": "O servidor web de origem (nginx/Apache/Node etc.) está parado ou travou",
|
||||
"cause2": "O firewall da origem está bloqueando as faixas de IP do Cloudflare (um erro de configuração — a origem deveria permitir o Cloudflare)",
|
||||
"cause3": "O servidor de origem está tão sobrecarregado que não aceita novas conexões",
|
||||
"todo1": "É um problema de disponibilidade da origem, não algo que o rastreador consiga contornar — na prática, o site está fora do ar para essas URLs",
|
||||
"todo2": "Rastreie de novo mais tarde; se persistir, o site está quebrado"
|
||||
},
|
||||
"522": {
|
||||
"name": "A conexão expirou",
|
||||
"explanation": "O Cloudflare iniciou um handshake TCP com a origem, mas ele não terminou dentro do tempo limite — a origem aceitou o SYN e nunca respondeu, ou um equipamento de rede no caminho descartou os pacotes.",
|
||||
"cause1": "A origem está muito sobrecarregada (fila TCP cheia)",
|
||||
"cause2": "O firewall da origem está descartando em silêncio (sem rejeitar) os pacotes do Cloudflare",
|
||||
"cause3": "Problema de rede/roteamento entre o Cloudflare e a origem",
|
||||
"cause4": "Sob a carga do rastreamento: as requisições paralelas do rastreamento esgotaram a tabela de conexões da origem",
|
||||
"todo1": "Reduza a concorrência (Configurações → Velocidade → Concorrência máxima) e rastreie de novo — o esgotamento da tabela de conexões é a causa comum quando isso aparece no meio do rastreamento",
|
||||
"todo2": "Tente de novo as URLs afetadas depois do rastreamento"
|
||||
},
|
||||
"523": {
|
||||
"name": "A origem está inacessível",
|
||||
"explanation": "O Cloudflare não conseguiu chegar ao servidor de origem — normalmente um problema de DNS no registro de origem do Cloudflare, ou a rede da origem está fora do ar.",
|
||||
"cause1": "O registro DNS do site no Cloudflare aponta para um IP errado / inativo",
|
||||
"cause2": "O provedor de hospedagem da origem está com uma falha de rede",
|
||||
"cause3": "Falha de BGP / roteamento entre o Cloudflare e a origem",
|
||||
"todo1": "O rastreador não pode fazer nada — é um erro de configuração de DNS/rede da origem"
|
||||
},
|
||||
"524": {
|
||||
"name": "Ocorreu um tempo limite",
|
||||
"explanation": "O Cloudflare estabeleceu uma conexão TCP com a origem, mas a origem não enviou uma resposta HTTP completa dentro do tempo limite do Cloudflare (100 s por padrão). A origem está no ar, mas lenta demais nesta requisição.",
|
||||
"cause1": "Uma consulta lenta ao banco de dados, uma chamada a uma API externa ou um processamento pesado na página",
|
||||
"cause2": "A origem está sobrecarregada — as requisições estão na fila atrás de um pool de workers saturado",
|
||||
"cause3": "Sob a carga do rastreamento: o rastreamento encheu a fila de requisições da origem e esta URL esperou demais",
|
||||
"todo1": "Reduza Concorrência máxima / URL/s máximo e adicione um Atraso por worker (Configurações → Velocidade) para não saturar a origem",
|
||||
"todo2": "Aumente o Tempo limite de resposta (Configurações → Rastreador) se quiser dar mais tempo às páginas realmente lentas",
|
||||
"todo3": "Tente de novo as URLs afetadas depois do rastreamento"
|
||||
},
|
||||
"525": {
|
||||
"name": "Falha no handshake SSL",
|
||||
"explanation": "O handshake TLS entre o Cloudflare e o servidor de origem falhou. Esse é o trecho Cloudflare↔origem, não o trecho navegador↔Cloudflare que você vê na barra de endereço — por isso o site pode parecer normal para um visitante e ainda assim retornar 525.",
|
||||
"cause1": "O certificado TLS da origem está expirado, é autoassinado ou foi emitido para outro nome de host (com o Cloudflare no modo 'Full (strict)')",
|
||||
"cause2": "A origem só aceita versões antigas de TLS / conjuntos de cifras que o Cloudflare rejeita",
|
||||
"cause3": "O servidor de origem está sobrecarregado e descartando handshakes TLS",
|
||||
"todo1": "Erro de configuração TLS da origem — não é algo que o rastreador possa mudar. Verifique o certificado do host (as linhas TLS Cert em Detalhes da URL, para HTTPS)"
|
||||
},
|
||||
"526": {
|
||||
"name": "Certificado SSL inválido",
|
||||
"explanation": "O Cloudflare (no modo SSL 'Full (strict)') não conseguiu validar o certificado TLS do servidor de origem.",
|
||||
"cause1": "O certificado da origem está expirado, é autoassinado ou foi emitido para outro nome de host",
|
||||
"cause2": "A cadeia de certificados da origem está incompleta (faltam certificados intermediários)",
|
||||
"todo1": "Problema no certificado da origem — o rastreador não pode fazer nada"
|
||||
},
|
||||
"527": {
|
||||
"name": "Erro do Railgun (Cloudflare, descontinuado)",
|
||||
"explanation": "Um erro de conexão no Cloudflare Railgun (um produto de aceleração da origem já descontinuado). Na prática obsoleto; se você vir isso, o site usa uma configuração muito antiga do Cloudflare.",
|
||||
"cause1": "Falha de conexão do antigo Cloudflare Railgun",
|
||||
"todo1": "Nada a fazer do lado do rastreador"
|
||||
},
|
||||
"530": {
|
||||
"name": "Erro do Cloudflare (geralmente um bloqueio de firewall / WAF — erro 1020)",
|
||||
"explanation": "Um 530 é o jeito do Cloudflare dizer \"aconteceu um erro, veja o código 1xxx no corpo\". Em um rastreamento, quase sempre é 1020 \"Access Denied\" — uma regra do Cloudflare Firewall / WAF / Bot-Fight bloqueou a requisição. Com menos frequência, vem junto de 1016 (erro de DNS da origem) ou de outros códigos 1xxx.",
|
||||
"cause1": "O Cloudflare WAF / Firewall Rules / Bot Fight Mode sinalizou o rastreador — por IP, por User-Agent, pela taxa de requisições ou pela falta de impressões digitais de navegador",
|
||||
"cause2": "O site tem um \"managed challenge\" que o rastreador não consegue resolver",
|
||||
"cause3": "(Raramente) um erro de DNS da origem (1016) em vez de um bloqueio",
|
||||
"todo1": "Defina um User-Agent de navegador (Configurações → Requisições) — o UA padrão do rastreador é o gatilho número 1",
|
||||
"todo2": "Reduza Concorrência máxima / URL/s máximo e adicione um Atraso por worker (Configurações → Velocidade) — requisições rápidas de um único IP disparam regras baseadas em taxa",
|
||||
"todo3": "Respeite o robots.txt e qualquer Crawl-delay declarado (Configurações → Rastreador)",
|
||||
"todo4": "Se o site é seu, libere o rastreador na lista de permissões do Cloudflare; caso contrário, o site bloqueia o acesso automatizado de propósito"
|
||||
},
|
||||
"401": {
|
||||
"name": "Não autorizado",
|
||||
"explanation": "O recurso exige autenticação e nenhuma credencial (ou uma credencial inválida) foi enviada.",
|
||||
"cause1": "A página está protegida por autenticação HTTP Basic/Bearer",
|
||||
"cause2": "É necessário um cookie de sessão e nenhum foi enviado",
|
||||
"todo1": "Configure as credenciais em Configurações → Autenticação, se você as tiver",
|
||||
"todo2": "Use Configurações → Cookies → \"Aceitar todos\" se o site restringe o conteúdo a uma sessão"
|
||||
},
|
||||
"403": {
|
||||
"name": "Proibido",
|
||||
"explanation": "O servidor entendeu a requisição, mas se recusa a atendê-la. Em um rastreamento, isso costuma ser um bloqueio de bots ou uma regra de WAF, e não um problema real de permissão.",
|
||||
"cause1": "Regra de WAF / proteção contra bots acionada pelo User-Agent, pelo IP ou pela taxa de requisições do rastreador",
|
||||
"cause2": "Diretório ou arquivo realmente inacessível a usuários anônimos",
|
||||
"cause3": "Proteção contra hotlink / referer",
|
||||
"todo1": "Experimente um User-Agent de navegador (Configurações → Requisições)",
|
||||
"todo2": "Reduza a velocidade do rastreamento (Configurações → Velocidade)",
|
||||
"todo3": "Se acontece no site inteiro, o site está bloqueando ativamente o acesso automatizado"
|
||||
},
|
||||
"404": {
|
||||
"name": "Não encontrado",
|
||||
"explanation": "O servidor não tem nenhum recurso nesta URL.",
|
||||
"cause1": "Link interno/externo quebrado",
|
||||
"cause2": "A página foi removida sem redirecionamento",
|
||||
"cause3": "Erro de digitação no href de um link",
|
||||
"todo1": "Corrija ou remova os links das páginas que apontam para cá — veja na aba Links de entrada quem aponta para esta URL"
|
||||
},
|
||||
"408": {
|
||||
"name": "Tempo limite da requisição",
|
||||
"explanation": "O servidor esgotou o tempo esperando a requisição terminar.",
|
||||
"cause1": "Rede lenta entre o rastreador e o servidor",
|
||||
"cause2": "Tempo limite de conexão do servidor configurado muito baixo",
|
||||
"todo1": "Geralmente é transitório — aumente as Tentativas (Configurações → Velocidade)"
|
||||
},
|
||||
"410": {
|
||||
"name": "Removido",
|
||||
"explanation": "O recurso foi removido de forma intencional e permanente — um sinal mais forte que um 404.",
|
||||
"cause1": "Página desativada de propósito; o servidor está dizendo aos rastreadores para descartá-la",
|
||||
"todo1": "Remova os links para esta URL — o Google vai desindexá-la mais rápido que um 404"
|
||||
},
|
||||
"429": {
|
||||
"name": "Requisições demais (limite de taxa)",
|
||||
"explanation": "O servidor está limitando a taxa do rastreador — requisições demais em pouco tempo a partir deste IP/User-Agent.",
|
||||
"cause1": "A taxa do rastreamento excede o limite por cliente do servidor",
|
||||
"cause2": "Hospedagem compartilhada com limitação agressiva",
|
||||
"todo1": "Reduza Concorrência máxima / URL/s máximo e adicione um Atraso por worker (Configurações → Velocidade)",
|
||||
"todo2": "Respeite o cabeçalho Retry-After — veja \"Pelos cabeçalhos de resposta\" abaixo, se o servidor tiver enviado um",
|
||||
"todo3": "Ative \"Respeitar o Crawl-delay do robots.txt\" (Configurações → Rastreador)"
|
||||
},
|
||||
"451": {
|
||||
"name": "Indisponível por motivos legais",
|
||||
"explanation": "O recurso está bloqueado por motivos legais (ordem judicial, bloqueio geográfico por GDPR/LGPD, DMCA etc.).",
|
||||
"cause1": "Conteúdo com restrição geográfica",
|
||||
"cause2": "Remoção exigida por lei",
|
||||
"todo1": "Nada — o bloqueio é intencional"
|
||||
},
|
||||
"500": {
|
||||
"name": "Erro interno do servidor",
|
||||
"explanation": "Ocorreu um erro não tratado na aplicação do servidor ao montar a página.",
|
||||
"cause1": "Bug no template da página / no código do backend",
|
||||
"cause2": "Banco de dados indisponível",
|
||||
"cause3": "Erro de configuração no servidor",
|
||||
"todo1": "Tente de novo depois do rastreamento (500s transitórios somem; os persistentes são um bug real)",
|
||||
"todo2": "Se os 500s aparecem sob carga, o rastreamento pode estar sobrecarregando um banco de dados — deixe o rastreamento mais lento (Configurações → Velocidade)"
|
||||
},
|
||||
"502": {
|
||||
"name": "Gateway inválido",
|
||||
"explanation": "Um proxy reverso / balanceador de carga na frente da aplicação recebeu uma resposta inválida do servidor de aplicação upstream.",
|
||||
"cause1": "O servidor de aplicação upstream travou ou retornou dados sem sentido",
|
||||
"cause2": "O upstream está sobrecarregado — comum sob a carga do rastreamento",
|
||||
"cause3": "Tempo limite proxy↔upstream",
|
||||
"todo1": "Reduza a velocidade do rastreamento (Configurações → Velocidade) se os 502s aparecerem em rajadas",
|
||||
"todo2": "Tente de novo as URLs afetadas depois do rastreamento"
|
||||
},
|
||||
"503": {
|
||||
"name": "Serviço indisponível",
|
||||
"explanation": "O servidor não consegue atender a requisição temporariamente — normalmente por sobrecarga ou manutenção. Muitas vezes vem com um cabeçalho Retry-After.",
|
||||
"cause1": "Servidor sobrecarregado — o próprio rastreamento pode causar isso",
|
||||
"cause2": "Janela de manutenção programada",
|
||||
"cause3": "Atraso no escalonamento automático (novas instâncias subindo)",
|
||||
"todo1": "Reduza Concorrência máxima / URL/s máximo e adicione um Atraso por worker (Configurações → Velocidade)",
|
||||
"todo2": "Respeite o Retry-After (veja \"Pelos cabeçalhos de resposta\" abaixo)",
|
||||
"todo3": "Tente de novo as URLs afetadas depois do rastreamento"
|
||||
},
|
||||
"504": {
|
||||
"name": "Tempo limite do gateway",
|
||||
"explanation": "Um proxy reverso / balanceador de carga não recebeu a tempo a resposta do servidor de aplicação upstream.",
|
||||
"cause1": "Consulta lenta ao banco de dados / chamada a uma API externa na página",
|
||||
"cause2": "Upstream sobrecarregado — requisições na fila",
|
||||
"cause3": "Sob a carga do rastreamento: o rastreamento saturou o pool de workers do upstream",
|
||||
"todo1": "Reduza a velocidade do rastreamento (Configurações → Velocidade)",
|
||||
"todo2": "Aumente o Tempo limite de resposta (Configurações → Rastreador) para páginas realmente lentas",
|
||||
"todo3": "Tente de novo as URLs afetadas depois do rastreamento"
|
||||
},
|
||||
"508": {
|
||||
"name": "Loop detectado",
|
||||
"explanation": "O servidor detectou um loop infinito ao processar a requisição (muitas vezes WebDAV ou um loop de redirecionamento no servidor).",
|
||||
"cause1": "Loop de redirecionamento no servidor",
|
||||
"cause2": "Loop de include recursivo / link simbólico",
|
||||
"todo1": "Bug do servidor — o rastreador não pode fazer nada"
|
||||
},
|
||||
"509": {
|
||||
"name": "Limite de largura de banda excedido",
|
||||
"explanation": "Um código não padrão (cPanel/Apache) indicando que a conta de hospedagem atingiu o limite mensal de largura de banda.",
|
||||
"cause1": "Cota de largura de banda da hospedagem compartilhada esgotada",
|
||||
"todo1": "O rastreador não pode fazer nada — o provedor está limitando a conta inteira"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Atrás do Cloudflare — cf-ray {{ray}}. O código de status descreve a ligação Cloudflare↔origem, não o que um navegador vê.",
|
||||
"fronted": "Servido por meio de {{front}}.",
|
||||
"origin": "Servidor de origem: {{server}}.",
|
||||
"retryAfterSeconds": "O servidor enviou Retry-After: {{seconds}} s — ele está pedindo explicitamente que os clientes esperem esse tempo. Um rastreamento educado não deveria pedir esta URL de novo antes disso.",
|
||||
"retryAfter": "O servidor enviou Retry-After: {{value}}.",
|
||||
"cfCache": "Status de cache do Cloudflare: {{status}}.",
|
||||
"xRobots": "X-Robots-Tag: {{value}} — afeta a indexabilidade independentemente do status."
|
||||
},
|
||||
"robots": {
|
||||
"title": "Não solicitada — bloqueada pelo robots.txt",
|
||||
"explanation": "O rastreador nunca enviou uma requisição para esta URL: uma regra do robots.txt para o user-agent configurado a bloqueia. Não há código de status porque nenhuma resposta chegou a ser pedida — não é uma falha de conexão.",
|
||||
"cause1": "Uma regra Disallow no robots.txt do site corresponde a este caminho para o user-agent do rastreamento",
|
||||
"cause2": "A URL recebe links de páginas rastreadas, então é registrada e contada, mas nunca é baixada",
|
||||
"todo1": "Nada, se a exclusão for intencional — o Google também não vai rastreá-la",
|
||||
"todo2": "Verifique se a página deveria ser indexável: uma URL bloqueada não repassa autoridade de links e seu conteúdo nunca é visto",
|
||||
"todo3": "Use Ajuda → Testador de robots.txt para ver qual regra corresponde, ou desative \"Respeitar o robots.txt\" em Configurações → Rastreador para auditá-la mesmo assim"
|
||||
},
|
||||
"network": {
|
||||
"title": "Sem resposta HTTP — erro de rede / conexão",
|
||||
"explanationDetail": "A requisição nunca recebeu uma resposta HTTP. O rastreador registrou: \"{{detail}}\".",
|
||||
"explanation": "A requisição nunca recebeu uma resposta HTTP — a consulta DNS, a conexão TCP, o handshake TLS ou a leitura da resposta falhou antes de chegar qualquer linha de status.",
|
||||
"cause1": "O DNS não conseguiu resolver o nome de host (erro de digitação, domínio inativo ou um nome que só se resolve dentro de uma rede privada)",
|
||||
"cause2": "Conexão TCP recusada ou expirada (host fora do ar, firewall, porta errada)",
|
||||
"cause3": "Falha no handshake TLS (certificado expirado/inválido, protocolo incompatível)",
|
||||
"cause4": "Conexão redefinida ou tempo de leitura esgotado no meio da resposta",
|
||||
"cause5": "Requisição cancelada pelo rastreador por ter excedido o Tempo limite de resposta (Configurações → Rastreador) ou o Tempo de resposta máximo (Configurações → Avançado)",
|
||||
"todo1": "Verifique se o nome de host é resolvido e se o site abre em um navegador comum nesta máquina",
|
||||
"todo2": "Se estiver acessível, mas lento, aumente o Tempo limite de resposta (Configurações → Rastreador)",
|
||||
"todo3": "Se só falha sob carga, reduza Concorrência máxima / URL/s máximo (Configurações → Velocidade) — o esgotamento da tabela de conexões da origem parece um erro de rede para o cliente",
|
||||
"todo4": "Tente de novo as URLs afetadas depois do rastreamento (falhas transitórias de conexão costumam sumir ao tentar de novo)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — Informativo",
|
||||
"fallback": "Informativo",
|
||||
"explanation": "Uma resposta 1xx ({{text}}). São respostas provisórias; o rastreador as trata como transitórias e espera o status final."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "Sucesso",
|
||||
"explanation": "A requisição foi bem-sucedida. Não há o que diagnosticar — esta seção só traz detalhes extras para códigos de erro e de redirecionamento."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "Redirecionamento",
|
||||
"explanation301": "Redirecionamento permanente. Os buscadores transferem os sinais de ranqueamento para o destino e atualizam o índice com a nova URL.",
|
||||
"explanation302": "Redirecionamento temporário. Os buscadores mantêm a URL original indexada. Usar um 302 por muito tempo é um erro comum de SEO — normalmente deveria ser um 301.",
|
||||
"explanation308": "Redirecionamento permanente (como um 301, mas o método da requisição é mantido).",
|
||||
"explanation304": "Não modificado — a cópia em cache ainda está válida. O rastreador normalmente faz requisições sem cabeçalhos condicionais, então isso é incomum.",
|
||||
"explanationOther": "Um redirecionamento {{code}} ({{text}}).",
|
||||
"location": "O cabeçalho Location aponta para: {{location}}",
|
||||
"noLocation": "Nenhum cabeçalho Location foi capturado — incomum para um redirecionamento.",
|
||||
"todoPermanent": "Se este redirecionamento for permanente, troque-o por 301/308 para consolidar os sinais de ranqueamento",
|
||||
"todoChain": "Veja a aba Códigos de resposta para conferir a cadeia completa de redirecionamentos e o destino final"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "Erro do cliente",
|
||||
"explanation": "Um código 4xx significa que o servidor rejeitou a requisição como erro do cliente (requisição inválida, não permitida, não encontrada etc.). Em um rastreamento, um 4xx persistente em páginas com links costuma indicar links quebrados; um 4xx esporádico pode ser proteção contra bots.",
|
||||
"cause1": "Link quebrado ou malformado",
|
||||
"cause2": "Proteção contra bots / regra de WAF",
|
||||
"cause3": "Método ou conteúdo que o servidor recusa",
|
||||
"todo1": "Veja a aba Links de entrada para saber o que aponta para esta URL",
|
||||
"todo2": "Se parecer um bloqueio, experimente um User-Agent de navegador e uma taxa menor (Configurações → Requisições / Velocidade)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "Erro do servidor",
|
||||
"explanation": "Um código 5xx significa que o servidor não conseguiu atender uma requisição que aceitou como válida. Sob a carga do rastreamento, muitas vezes é o rastreamento sobrecarregando a origem, e não um bug real da página.",
|
||||
"cause1": "Erro ou travamento no servidor",
|
||||
"cause2": "Origem sobrecarregada — possivelmente pelo próprio rastreamento",
|
||||
"cause3": "Falha no upstream/proxy",
|
||||
"todo1": "Reduza Concorrência máxima / URL/s máximo e adicione um Atraso por worker (Configurações → Velocidade) se os 5xx aparecerem em rajadas",
|
||||
"todo2": "Aumente as Tentativas (Configurações → Velocidade) e rastreie de novo as URLs afetadas"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "Esta página não tem títulos detectados (nenhum elemento",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL",
|
||||
"percentOfTotal": "% от общего числа",
|
||||
"siteSoft404": "Несуществующие страницы возвращают {{status}} (soft 404)",
|
||||
"siteSoft404Hint": "Запрос к несуществующей странице получил ответ {{status}} вместо 404, поэтому битые ссылки на этом сайте выглядят для поисковых систем как рабочие страницы."
|
||||
"siteSoft404Hint": "Запрос к несуществующей странице получил ответ {{status}} вместо 404, поэтому битые ссылки на этом сайте выглядят для поисковых систем как рабочие страницы.",
|
||||
"siteBotBlocked": "Защита от ботов заблокировала часть обхода",
|
||||
"siteBotBlockedHint": "Защита сайта от ботов ответила проверкой вместо этих страниц. Они не обойдены и исключены из проверок битых ссылок и sitemap. Снизьте скорость обхода или добавьте User-Agent краулера в список разрешённых, затем обойдите их снова."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "Подключайте внешние сервисы со своими собственными учётными данными. API-ключи и секреты шифруются на диске средствами защищённого хранилища вашей операционной системы и никогда не покидают эту машину.",
|
||||
@@ -418,7 +420,62 @@
|
||||
"orphanMetricaPageviews_one": "{{formatted}} просмотр Metrica (хиты)",
|
||||
"orphanMetricaPageviews_few": "{{formatted}} просмотра Metrica (хиты)",
|
||||
"orphanMetricaPageviews_many": "{{formatted}} просмотров Metrica (хиты)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} просмотра Metrica (хиты)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} просмотра Metrica (хиты)",
|
||||
"noResponse": "Нет ответа",
|
||||
"bucketOrphan": "0 (осиротевшие)",
|
||||
"badge": {
|
||||
"ok": "OK",
|
||||
"non": "НЕТ",
|
||||
"warn": "ВНИМ.",
|
||||
"bad": "ПЛОХО",
|
||||
"err": "ОШИБ.",
|
||||
"slow": "МЕДЛ.",
|
||||
"net": "СЕТЬ"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} OK · {{rate}}% ошибок",
|
||||
"avgMs": "в среднем {{ms}} мс",
|
||||
"distinctIds_one": "{{count}} уникальный ID",
|
||||
"distinctIds_few": "{{count}} уникальных ID",
|
||||
"distinctIds_many": "{{count}} уникальных ID",
|
||||
"distinctIds_other": "{{count}} уникального ID",
|
||||
"pageCount_one": "{{formatted}} страница",
|
||||
"pageCount_few": "{{formatted}} страницы",
|
||||
"pageCount_many": "{{formatted}} страниц",
|
||||
"pageCount_other": "{{formatted}} страницы",
|
||||
"imageCount_one": "{{formatted}} изображение",
|
||||
"imageCount_few": "{{formatted}} изображения",
|
||||
"imageCount_many": "{{formatted}} изображений",
|
||||
"imageCount_other": "{{formatted}} изображения",
|
||||
"hits_one": "{{formatted}} вхождение",
|
||||
"hits_few": "{{formatted}} вхождения",
|
||||
"hits_many": "{{formatted}} вхождений",
|
||||
"hits_other": "{{formatted}} вхождения",
|
||||
"avgWords": "в среднем {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "из {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "Навигация",
|
||||
"header": "Шапка",
|
||||
"content": "Контент",
|
||||
"sidebar": "Боковая панель",
|
||||
"footer": "Подвал",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "Индексируемый",
|
||||
"noindex": "Неиндексируемый — noindex",
|
||||
"canonical": "Неиндексируемый — canonical на другой URL",
|
||||
"robotsBlocked": "Неиндексируемый — заблокирован robots.txt",
|
||||
"redirect": "Неиндексируемый — перенаправление",
|
||||
"clientError": "Неиндексируемый — 4xx",
|
||||
"serverError": "Неиндексируемый — 5xx",
|
||||
"botBlocked": "Неизвестно — заблокирован защитой от ботов"
|
||||
},
|
||||
"kind": {
|
||||
"image": "Изображение",
|
||||
"font": "Шрифт",
|
||||
"other": "Другое"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "Валидатор sitemap",
|
||||
@@ -1091,7 +1148,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "Из заголовков ответа",
|
||||
"likelyCauses": "Вероятные причины",
|
||||
"whatToDo": "Что делать"
|
||||
"whatToDo": "Что делать",
|
||||
"cloudflareStyle": "edge-код в стиле Cloudflare",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "Веб-сервер вернул неизвестную ошибку",
|
||||
"explanation": "Cloudflare подключился к исходному серверу, но получил пустой, искажённый или неожиданный ответ (например, соединение оборвалось посреди ответа, origin вернул слишком большие заголовки или процесс origin аварийно завершился). Это общий код для случая «на origin что-то пошло не так».",
|
||||
"cause1": "Исходный сервер упал, перезапустился или исчерпал воркеры/память во время обработки запроса",
|
||||
"cause2": "Origin закрыл TCP-соединение, не отправив ответ целиком (часто из-за тайм-аута приложения или завершения по нехватке памяти)",
|
||||
"cause3": "Темп запросов обхода перегрузил пул процессов origin — 520, идущие пачками под нагрузкой, почти всегда означают именно это",
|
||||
"cause4": "Origin вернул заголовки ответа больше, чем допускает Cloudflare (редко; обычно неправильно настроенное приложение)",
|
||||
"todo1": "Если 520 идут пачками по многим URL, уменьшите Макс. параллелизм / Макс. URL/с и добавьте Задержку на воркер (Настройки → Скорость) — обход перегружает origin",
|
||||
"todo2": "После завершения обхода повторно обойдите затронутые URL (временные 520 часто исчезают при повторе — увеличьте Число попыток в Настройки → Скорость)",
|
||||
"todo3": "Если 520 стабильно возвращают только отдельные URL, на этих страницах ошибка на сервере — сообщите владельцу сайта"
|
||||
},
|
||||
"521": {
|
||||
"name": "Веб-сервер недоступен",
|
||||
"explanation": "Cloudflare не смог открыть TCP-соединение с исходным сервером — origin активно отклонил соединение или не слушает ожидаемый порт.",
|
||||
"cause1": "Исходный веб-сервер (nginx/Apache/Node и т. п.) остановлен или упал",
|
||||
"cause2": "Файрвол origin блокирует диапазоны IP Cloudflare (ошибка настройки — origin должен пропускать Cloudflare)",
|
||||
"cause3": "Исходный сервер перегружен настолько, что не принимает новые соединения",
|
||||
"todo1": "Это проблема доступности origin, краулер не может её обойти — для этих URL сайт фактически недоступен",
|
||||
"todo2": "Повторите обход позже; если проблема сохраняется, сайт сломан"
|
||||
},
|
||||
"522": {
|
||||
"name": "Истекло время ожидания соединения",
|
||||
"explanation": "Cloudflare начал TCP-рукопожатие с origin, но оно не завершилось за отведённое время — origin принял SYN и так и не ответил, либо сетевое устройство между ними отбросило пакеты.",
|
||||
"cause1": "Origin сильно перегружен (очередь TCP заполнена)",
|
||||
"cause2": "Файрвол origin молча отбрасывает (а не отклоняет) пакеты Cloudflare",
|
||||
"cause3": "Проблема сети/маршрутизации между Cloudflare и origin",
|
||||
"cause4": "Под нагрузкой обхода: параллельные запросы обхода исчерпали таблицу соединений origin",
|
||||
"todo1": "Снизьте параллелизм (Настройки → Скорость → Макс. параллелизм) и повторите обход — если ошибка появляется посреди обхода, обычная причина в исчерпании таблицы соединений",
|
||||
"todo2": "Повторите затронутые URL после обхода"
|
||||
},
|
||||
"523": {
|
||||
"name": "Origin недоступен",
|
||||
"explanation": "Cloudflare вообще не смог добраться до исходного сервера — обычно это проблема DNS в записи origin в Cloudflare или не работает сеть origin.",
|
||||
"cause1": "DNS-запись сайта в Cloudflare указывает на неверный / неработающий IP",
|
||||
"cause2": "У хостинг-провайдера origin сбой сети",
|
||||
"cause3": "Сбой BGP / маршрутизации между Cloudflare и origin",
|
||||
"todo1": "Краулер ничего не может сделать — это ошибка настройки DNS/сети origin"
|
||||
},
|
||||
"524": {
|
||||
"name": "Истекло время ожидания",
|
||||
"explanation": "Cloudflare успешно установил TCP-соединение с origin, но origin не прислал полный HTTP-ответ за время ожидания Cloudflare (по умолчанию 100 с). Origin работает, но на этом запросе слишком медленный.",
|
||||
"cause1": "Медленный запрос к базе данных, вызов внешнего API или тяжёлые вычисления на странице",
|
||||
"cause2": "Origin перегружен — запросы стоят в очереди за заполненным пулом воркеров",
|
||||
"cause3": "Под нагрузкой обхода: обход заполнил очередь запросов origin, и этот URL ждал слишком долго",
|
||||
"todo1": "Уменьшите Макс. параллелизм / Макс. URL/с и добавьте Задержку на воркер (Настройки → Скорость), чтобы не перегружать origin",
|
||||
"todo2": "Увеличьте Тайм-аут ответа (Настройки → Обходчик), если хотите дать действительно медленным страницам больше времени",
|
||||
"todo3": "Повторите затронутые URL после обхода"
|
||||
},
|
||||
"525": {
|
||||
"name": "Сбой SSL-рукопожатия",
|
||||
"explanation": "TLS-рукопожатие между Cloudflare и исходным сервером не удалось. Это участок Cloudflare↔origin, а не участок браузер↔Cloudflare, который вы видите в адресной строке, — поэтому сайт может нормально открываться у посетителя и всё равно отдавать 525.",
|
||||
"cause1": "TLS-сертификат origin просрочен, самоподписан или выдан на другое имя хоста (при режиме Cloudflare 'Full (strict)')",
|
||||
"cause2": "Origin поддерживает только старые версии TLS / наборы шифров, которые Cloudflare отклоняет",
|
||||
"cause3": "Исходный сервер перегружен и сбрасывает TLS-рукопожатия",
|
||||
"todo1": "Ошибка настройки TLS на origin — краулер не может это изменить. Проверьте сертификат хоста (строки TLS Cert в разделе Детали URL, для HTTPS)"
|
||||
},
|
||||
"526": {
|
||||
"name": "Недействительный SSL-сертификат",
|
||||
"explanation": "Cloudflare (в режиме SSL 'Full (strict)') не смог проверить TLS-сертификат исходного сервера.",
|
||||
"cause1": "Сертификат origin просрочен, самоподписан или выдан на другое имя хоста",
|
||||
"cause2": "Цепочка сертификатов на origin неполная (нет промежуточных сертификатов)",
|
||||
"todo1": "Проблема с сертификатом origin — краулер ничего не может сделать"
|
||||
},
|
||||
"527": {
|
||||
"name": "Ошибка Railgun (Cloudflare, устарело)",
|
||||
"explanation": "Ошибка соединения в Cloudflare Railgun (уже снятом с поддержки продукте для ускорения origin). Фактически устаревший код; если вы его видите, сайт работает на очень старой конфигурации Cloudflare.",
|
||||
"cause1": "Сбой соединения устаревшего Cloudflare Railgun",
|
||||
"todo1": "Со стороны краулера сделать ничего нельзя"
|
||||
},
|
||||
"530": {
|
||||
"name": "Ошибка Cloudflare (обычно блокировка файрволом / WAF — ошибка 1020)",
|
||||
"explanation": "Код 530 — это способ Cloudflare сказать «произошла ошибка, смотрите код 1xxx в теле ответа». При обходе это почти всегда 1020 «Access Denied» — запрос заблокировало правило Cloudflare Firewall / WAF / Bot-Fight. Реже он сопровождается кодом 1016 (ошибка DNS origin) или другими кодами 1xxx.",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode пометил краулер — по IP, по User-Agent, по темпу запросов или из-за отсутствия отпечатков браузера",
|
||||
"cause2": "На сайте включён «managed challenge», который краулер не может пройти",
|
||||
"cause3": "(Редко) ошибка DNS origin (1016), а не блокировка",
|
||||
"todo1": "Укажите User-Agent браузера (Настройки → Запросы) — стандартный UA краулера является причиной № 1",
|
||||
"todo2": "Уменьшите Макс. параллелизм / Макс. URL/с и добавьте Задержку на воркер (Настройки → Скорость) — частые запросы с одного IP срабатывают на правила по темпу",
|
||||
"todo3": "Соблюдайте robots.txt и указанный в нём Crawl-delay (Настройки → Обходчик)",
|
||||
"todo4": "Если сайт ваш, добавьте краулер в список разрешённых в Cloudflare; иначе сайт намеренно блокирует автоматический доступ"
|
||||
},
|
||||
"401": {
|
||||
"name": "Не авторизован",
|
||||
"explanation": "Ресурс требует аутентификации, а учётные данные не были отправлены (или были неверными).",
|
||||
"cause1": "Страница закрыта аутентификацией HTTP Basic/Bearer",
|
||||
"cause2": "Требуется сессионный cookie, а он не был отправлен",
|
||||
"todo1": "Настройте учётные данные в Настройки → Аутентификация, если они у вас есть",
|
||||
"todo2": "Используйте Настройки → Cookie → «Принимать все», если сайт отдаёт контент только в рамках сессии"
|
||||
},
|
||||
"403": {
|
||||
"name": "Доступ запрещён",
|
||||
"explanation": "Сервер понял запрос, но отказывается его выполнять. При обходе это чаще всего блокировка ботов или правило WAF, а не настоящая проблема с правами доступа.",
|
||||
"cause1": "Правило WAF / защиты от ботов сработало на User-Agent, IP или темп запросов краулера",
|
||||
"cause2": "Каталог или файл действительно недоступен анонимным пользователям",
|
||||
"cause3": "Защита от хотлинкинга / по referer",
|
||||
"todo1": "Попробуйте User-Agent браузера (Настройки → Запросы)",
|
||||
"todo2": "Снизьте скорость обхода (Настройки → Скорость)",
|
||||
"todo3": "Если это происходит на всём сайте, сайт активно блокирует автоматический доступ"
|
||||
},
|
||||
"404": {
|
||||
"name": "Не найдено",
|
||||
"explanation": "На сервере нет ресурса по этому URL.",
|
||||
"cause1": "Битая внутренняя/внешняя ссылка",
|
||||
"cause2": "Страницу удалили без перенаправления",
|
||||
"cause3": "Опечатка в href ссылки",
|
||||
"todo1": "Исправьте или уберите ссылки на страницах, которые ведут сюда, — кто на них ссылается, видно на вкладке Входящие ссылки"
|
||||
},
|
||||
"408": {
|
||||
"name": "Истекло время ожидания запроса",
|
||||
"explanation": "Сервер не дождался завершения запроса.",
|
||||
"cause1": "Медленная сеть между краулером и сервером",
|
||||
"cause2": "На сервере задан очень маленький тайм-аут соединения",
|
||||
"todo1": "Обычно это временно — увеличьте Число попыток (Настройки → Скорость)"
|
||||
},
|
||||
"410": {
|
||||
"name": "Удалено",
|
||||
"explanation": "Ресурс удалён намеренно и навсегда — это более сильный сигнал, чем 404.",
|
||||
"cause1": "Страницу намеренно убрали; сервер сообщает краулерам, что её нужно исключить",
|
||||
"todo1": "Уберите ссылки на этот URL — Google исключит его из индекса быстрее, чем при 404"
|
||||
},
|
||||
"429": {
|
||||
"name": "Слишком много запросов (ограничение скорости)",
|
||||
"explanation": "Сервер ограничивает скорость краулера — слишком много запросов за слишком короткое время с этого IP/User-Agent.",
|
||||
"cause1": "Темп обхода превышает лимит сервера на одного клиента",
|
||||
"cause2": "Виртуальный хостинг с жёстким ограничением",
|
||||
"todo1": "Уменьшите Макс. параллелизм / Макс. URL/с и добавьте Задержку на воркер (Настройки → Скорость)",
|
||||
"todo2": "Соблюдайте заголовок Retry-After — если сервер его прислал, смотрите блок «Из заголовков ответа» ниже",
|
||||
"todo3": "Включите «Соблюдать Crawl-delay из robots.txt» (Настройки → Обходчик)"
|
||||
},
|
||||
"451": {
|
||||
"name": "Недоступно по юридическим причинам",
|
||||
"explanation": "Ресурс заблокирован по юридическим причинам (решение суда, геоблокировка по GDPR, DMCA и т. п.).",
|
||||
"cause1": "Контент с географическими ограничениями",
|
||||
"cause2": "Удаление по требованию закона",
|
||||
"todo1": "Ничего — блокировка намеренная"
|
||||
},
|
||||
"500": {
|
||||
"name": "Внутренняя ошибка сервера",
|
||||
"explanation": "При формировании страницы в серверном приложении произошла необработанная ошибка.",
|
||||
"cause1": "Ошибка в шаблоне страницы / коде бэкенда",
|
||||
"cause2": "База данных недоступна",
|
||||
"cause3": "Неправильная настройка сервера",
|
||||
"todo1": "Повторите после обхода (временные 500 исчезают; постоянные — это настоящая ошибка)",
|
||||
"todo2": "Если 500 появляются под нагрузкой, обход может перегружать базу данных — замедлите обход (Настройки → Скорость)"
|
||||
},
|
||||
"502": {
|
||||
"name": "Ошибка шлюза",
|
||||
"explanation": "Обратный прокси / балансировщик нагрузки перед приложением получил некорректный ответ от вышестоящего сервера приложения.",
|
||||
"cause1": "Вышестоящий сервер приложения упал или вернул мусор",
|
||||
"cause2": "Вышестоящий сервер перегружен — частое явление под нагрузкой обхода",
|
||||
"cause3": "Тайм-аут между прокси и вышестоящим сервером",
|
||||
"todo1": "Снизьте скорость обхода (Настройки → Скорость), если 502 идут пачками",
|
||||
"todo2": "Повторите затронутые URL после обхода"
|
||||
},
|
||||
"503": {
|
||||
"name": "Сервис недоступен",
|
||||
"explanation": "Сервер временно не может обработать запрос — обычно из-за перегрузки или обслуживания. Часто приходит вместе с заголовком Retry-After.",
|
||||
"cause1": "Сервер перегружен — причиной может быть сам обход",
|
||||
"cause2": "Плановое техническое обслуживание",
|
||||
"cause3": "Задержка автомасштабирования (запускаются новые экземпляры)",
|
||||
"todo1": "Уменьшите Макс. параллелизм / Макс. URL/с и добавьте Задержку на воркер (Настройки → Скорость)",
|
||||
"todo2": "Соблюдайте Retry-After (см. блок «Из заголовков ответа» ниже)",
|
||||
"todo3": "Повторите затронутые URL после обхода"
|
||||
},
|
||||
"504": {
|
||||
"name": "Тайм-аут шлюза",
|
||||
"explanation": "Обратный прокси / балансировщик нагрузки не получил вовремя ответ от вышестоящего сервера приложения.",
|
||||
"cause1": "Медленный запрос к базе данных / вызов внешнего API на странице",
|
||||
"cause2": "Вышестоящий сервер перегружен — запросы стоят в очереди",
|
||||
"cause3": "Под нагрузкой обхода: обход заполнил пул воркеров вышестоящего сервера",
|
||||
"todo1": "Снизьте скорость обхода (Настройки → Скорость)",
|
||||
"todo2": "Увеличьте Тайм-аут ответа (Настройки → Обходчик) для действительно медленных страниц",
|
||||
"todo3": "Повторите затронутые URL после обхода"
|
||||
},
|
||||
"508": {
|
||||
"name": "Обнаружен цикл",
|
||||
"explanation": "Сервер обнаружил бесконечный цикл при обработке запроса (часто WebDAV или цикл перенаправлений на стороне сервера).",
|
||||
"cause1": "Цикл перенаправлений на стороне сервера",
|
||||
"cause2": "Рекурсивный include / цикл символических ссылок",
|
||||
"todo1": "Ошибка сервера — краулер ничего не может сделать"
|
||||
},
|
||||
"509": {
|
||||
"name": "Превышен лимит трафика",
|
||||
"explanation": "Нестандартный код (cPanel/Apache), означающий, что аккаунт хостинга исчерпал месячный лимит трафика.",
|
||||
"cause1": "Исчерпана квота трафика на виртуальном хостинге",
|
||||
"todo1": "Краулер ничего не может сделать — хостинг ограничивает весь аккаунт"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "За Cloudflare — cf-ray {{ray}}. Код ответа описывает связь Cloudflare↔origin, а не то, что видит браузер.",
|
||||
"fronted": "Работает через {{front}}.",
|
||||
"origin": "Исходный сервер: {{server}}.",
|
||||
"retryAfterSeconds": "Сервер прислал Retry-After: {{seconds}} с — он прямо просит клиентов подождать это время. Аккуратный обход не должен запрашивать этот URL раньше.",
|
||||
"retryAfter": "Сервер прислал Retry-After: {{value}}.",
|
||||
"cfCache": "Статус кэша Cloudflare: {{status}}.",
|
||||
"xRobots": "X-Robots-Tag: {{value}} — влияет на индексируемость независимо от кода ответа."
|
||||
},
|
||||
"robots": {
|
||||
"title": "Не запрашивался — запрещено robots.txt",
|
||||
"explanation": "Краулер ни разу не отправил запрос к этому URL: его запрещает правило robots.txt для заданного user-agent. Кода ответа нет, потому что ответ вообще не запрашивался, — это не ошибка соединения.",
|
||||
"cause1": "Правило Disallow в robots.txt сайта совпадает с этим путём для user-agent обхода",
|
||||
"cause2": "На URL ссылаются обойдённые страницы, поэтому он учитывается и подсчитывается, но никогда не загружается",
|
||||
"todo1": "Ничего, если исключение намеренное — Google тоже не будет его обходить",
|
||||
"todo2": "Проверьте, должна ли страница индексироваться: запрещённый URL не передаёт ссылочный вес, а его содержимое никто не видит",
|
||||
"todo3": "Откройте Справка → Тестер robots.txt, чтобы увидеть, какое правило срабатывает, или отключите «Соблюдать robots.txt» в Настройки → Обходчик, чтобы всё равно проверить этот URL"
|
||||
},
|
||||
"network": {
|
||||
"title": "Нет HTTP-ответа — ошибка сети / соединения",
|
||||
"explanationDetail": "Запрос так и не получил HTTP-ответа. Краулер записал: «{{detail}}».",
|
||||
"explanation": "Запрос так и не получил HTTP-ответа — DNS-запрос, TCP-соединение, TLS-рукопожатие или чтение ответа завершились ошибкой до получения строки статуса.",
|
||||
"cause1": "DNS не смог разрешить имя хоста (опечатка, неработающий домен или имя, которое разрешается только внутри частной сети)",
|
||||
"cause2": "TCP-соединение отклонено или истекло по тайм-ауту (хост не работает, файрвол, неверный порт)",
|
||||
"cause3": "Сбой TLS-рукопожатия (просроченный/недействительный сертификат, несовместимый протокол)",
|
||||
"cause4": "Соединение сброшено или истёк тайм-аут чтения посреди ответа",
|
||||
"cause5": "Краулер прервал запрос, потому что тот превысил Тайм-аут ответа (Настройки → Обходчик) или Макс. время отклика (Настройки → Дополнительно)",
|
||||
"todo1": "Проверьте, что имя хоста разрешается и сайт открывается в обычном браузере на этом компьютере",
|
||||
"todo2": "Если сайт доступен, но медленный, увеличьте Тайм-аут ответа (Настройки → Обходчик)",
|
||||
"todo3": "Если ошибка бывает только под нагрузкой, уменьшите Макс. параллелизм / Макс. URL/с (Настройки → Скорость) — исчерпание таблицы соединений на origin выглядит для клиента как ошибка сети",
|
||||
"todo4": "Повторите затронутые URL после обхода (временные сбросы соединения исчезают при повторе)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — Информационный",
|
||||
"fallback": "Информационный",
|
||||
"explanation": "Ответ 1xx ({{text}}). Это промежуточные ответы; краулер считает их временными и ждёт окончательного статуса."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "Успешно",
|
||||
"explanation": "Запрос выполнен успешно. Диагностика не нужна — этот раздел даёт подробности только по кодам ошибок и перенаправлений."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "Перенаправление",
|
||||
"explanation301": "Постоянное перенаправление. Поисковые системы передают сигналы ранжирования на целевой URL и обновляют индекс.",
|
||||
"explanation302": "Временное перенаправление. Поисковые системы оставляют в индексе исходный URL. Долговременный 302 — частая SEO-ошибка: обычно здесь нужен 301.",
|
||||
"explanation308": "Постоянное перенаправление (как 301, но метод запроса сохраняется).",
|
||||
"explanation304": "Не изменено — копия в кэше ещё актуальна. Краулер обычно отправляет запросы без условных заголовков, поэтому это необычно.",
|
||||
"explanationOther": "Перенаправление {{code}} ({{text}}).",
|
||||
"location": "Заголовок Location указывает на: {{location}}",
|
||||
"noLocation": "Заголовок Location не получен — необычно для перенаправления.",
|
||||
"todoPermanent": "Если это перенаправление постоянное, замените его на 301/308, чтобы сигналы ранжирования объединились",
|
||||
"todoChain": "Откройте вкладку Коды ответа, чтобы увидеть всю цепочку перенаправлений и её конечную цель"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "Ошибка клиента",
|
||||
"explanation": "Код 4xx означает, что сервер отклонил запрос как ошибку клиента (неверный запрос, нет доступа, не найдено и т. п.). При обходе постоянные 4xx на страницах, на которые есть ссылки, обычно означают битые ссылки; эпизодические 4xx могут быть защитой от ботов.",
|
||||
"cause1": "Битая или некорректная ссылка",
|
||||
"cause2": "Защита от ботов / правило WAF",
|
||||
"cause3": "Метод или содержимое, которые сервер отклоняет",
|
||||
"todo1": "Откройте вкладку Входящие ссылки, чтобы увидеть, что ссылается на этот URL",
|
||||
"todo2": "Если это похоже на блокировку, попробуйте User-Agent браузера и меньшую скорость (Настройки → Запросы / Скорость)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "Ошибка сервера",
|
||||
"explanation": "Код 5xx означает, что сервер не смог выполнить запрос, который принял как корректный. Под нагрузкой обхода это часто следствие перегрузки origin самим обходом, а не настоящая ошибка страницы.",
|
||||
"cause1": "Ошибка или сбой на сервере",
|
||||
"cause2": "Origin перегружен — возможно, самим обходом",
|
||||
"cause3": "Сбой вышестоящего сервера/прокси",
|
||||
"todo1": "Уменьшите Макс. параллелизм / Макс. URL/с и добавьте Задержку на воркер (Настройки → Скорость), если 5xx идут пачками",
|
||||
"todo2": "Увеличьте Число попыток (Настройки → Скорость) и повторно обойдите затронутые URL"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "На этой странице не найдено заголовков (нет элементов",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL'ler",
|
||||
"percentOfTotal": "Toplamın %",
|
||||
"siteSoft404": "Olmayan sayfalar {{status}} döndürüyor (soft 404)",
|
||||
"siteSoft404Hint": "Var olmayan bir sayfa için yapılan istek 404 yerine {{status}} ile yanıtlandı; bu yüzden sitedeki kırık bağlantılar arama motorlarına çalışan sayfalar gibi görünüyor."
|
||||
"siteSoft404Hint": "Var olmayan bir sayfa için yapılan istek 404 yerine {{status}} ile yanıtlandı; bu yüzden sitedeki kırık bağlantılar arama motorlarına çalışan sayfalar gibi görünüyor.",
|
||||
"siteBotBlocked": "Bot koruması taramanın bir kısmını engelledi",
|
||||
"siteBotBlockedHint": "Sitenin bot koruması bu sayfalar yerine bir doğrulama sayfasıyla yanıt verdi. Bu sayfalar taranmadı; kırık bağlantı ve sitemap kontrollerinin dışında tutuldu. Tarama hızını düşürün ya da tarayıcının user agent’ını izin listesine ekleyin, sonra yeniden tarayın."
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "Harici servisleri kendi kimlik bilgilerinle bağla. API anahtarları ve secret'lar işletim sisteminin güvenli depolaması ile şifreli saklanır ve bu makineden asla çıkmaz.",
|
||||
@@ -394,7 +396,54 @@
|
||||
"orphanMetricaVisits_one": "{{formatted}} Metrica ziyareti (giriş)",
|
||||
"orphanMetricaVisits_other": "{{formatted}} Metrica ziyareti (giriş)",
|
||||
"orphanMetricaPageviews_one": "{{formatted}} Metrica görüntülemesi (hit)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} Metrica görüntülemesi (hit)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} Metrica görüntülemesi (hit)",
|
||||
"noResponse": "Yanıt yok",
|
||||
"bucketOrphan": "0 (yetim)",
|
||||
"badge": {
|
||||
"ok": "TAMAM",
|
||||
"non": "DIŞI",
|
||||
"warn": "UYARI",
|
||||
"bad": "KÖTÜ",
|
||||
"err": "HATA",
|
||||
"slow": "YAVAŞ",
|
||||
"net": "AĞ"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} başarılı · %{{rate}} hata",
|
||||
"avgMs": "ort. {{ms}} ms",
|
||||
"distinctIds_one": "{{count}} farklı ID",
|
||||
"distinctIds_other": "{{count}} farklı ID",
|
||||
"pageCount_one": "{{formatted}} sayfa",
|
||||
"pageCount_other": "{{formatted}} sayfa",
|
||||
"imageCount_one": "{{formatted}} görsel",
|
||||
"imageCount_other": "{{formatted}} görsel",
|
||||
"hits_one": "{{formatted}} kez",
|
||||
"hits_other": "{{formatted}} kez",
|
||||
"avgWords": "ort. {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "kaynak: {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "Navigasyon",
|
||||
"header": "Üst bölüm",
|
||||
"content": "İçerik",
|
||||
"sidebar": "Kenar çubuğu",
|
||||
"footer": "Alt bilgi",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "İndekslenebilir",
|
||||
"noindex": "İndekslenemez — noindex",
|
||||
"canonical": "İndekslenemez — canonical edilmiş",
|
||||
"robotsBlocked": "İndekslenemez — robots.txt tarafından engellendi",
|
||||
"redirect": "İndekslenemez — yönlendirme",
|
||||
"clientError": "İndekslenemez — 4xx",
|
||||
"serverError": "İndekslenemez — 5xx",
|
||||
"botBlocked": "Bilinmiyor — bot korumasına takıldı"
|
||||
},
|
||||
"kind": {
|
||||
"image": "Görsel",
|
||||
"font": "Font",
|
||||
"other": "Diğer"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "Sitemap Doğrulayıcı",
|
||||
@@ -1049,7 +1098,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "Yanıt başlıklarından",
|
||||
"likelyCauses": "Olası sebepler",
|
||||
"whatToDo": "Ne yapılmalı"
|
||||
"whatToDo": "Ne yapılmalı",
|
||||
"cloudflareStyle": "Cloudflare tarzı edge kodu",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "Web Sunucusu Bilinmeyen Bir Hata Döndürdü",
|
||||
"explanation": "Cloudflare origin sunucusuna bağlandı ama boş, bozuk ya da beklenmedik bir yanıt aldı (örneğin bağlantı yanıtın ortasında kesildi, origin çok büyük başlıklar döndürdü ya da origin süreci çöktü). 'Origin tarafında bir şeyler ters gitti' anlamına gelen genel bir koddur.",
|
||||
"cause1": "Origin sunucusu isteği işlerken çöktü, yeniden başladı ya da worker/bellek kapasitesi tükendi",
|
||||
"cause2": "Origin, yanıtı tamamlamadan TCP bağlantısını kapattı (çoğunlukla uygulama düzeyinde bir zaman aşımı ya da bellek yetersizliği nedeniyle sonlandırma)",
|
||||
"cause3": "Taramanın istek hızı origin'in süreç havuzunu aştı — yük altında toplu halde görülen 520'ler neredeyse her zaman bunu gösterir",
|
||||
"cause4": "Origin, Cloudflare'in izin verdiğinden büyük yanıt başlıkları döndürdü (nadir; genellikle yanlış yapılandırılmış bir uygulama)",
|
||||
"todo1": "520'ler birçok URL'de toplu halde görülüyorsa Maks Paralellik / Maks URL/sn değerini düşürün ve Worker Başına Gecikme ekleyin (Ayarlar → Hız) — tarama origin'i aşırı yüklüyor",
|
||||
"todo2": "Tarama bittikten sonra etkilenen URL'leri yeniden tarayın (geçici 520'ler tekrar denemede çoğunlukla düzelir — Ayarlar → Hız'da Yeniden Deneme Sayısı'nı artırın)",
|
||||
"todo3": "Yalnızca belirli URL'ler sürekli 520 veriyorsa bu sayfalarda sunucu tarafında bir hata vardır — site sahibine bildirin"
|
||||
},
|
||||
"521": {
|
||||
"name": "Web Sunucusu Kapalı",
|
||||
"explanation": "Cloudflare origin sunucusuyla TCP bağlantısı açamadı — origin bağlantıyı aktif olarak reddetti ya da beklenen portu dinlemiyor.",
|
||||
"cause1": "Origin web sunucusu (nginx/Apache/Node vb.) durdurulmuş ya da çökmüş",
|
||||
"cause2": "Origin'in güvenlik duvarı Cloudflare'in IP aralıklarını engelliyor (yanlış yapılandırma — origin Cloudflare'e izin vermeli)",
|
||||
"cause3": "Origin sunucusu yeni bağlantı kabul edemeyecek kadar aşırı yüklü",
|
||||
"todo1": "Bu bir origin erişilebilirliği sorunu; tarayıcının aşabileceği bir şey değil — site bu URL'ler için fiilen kapalı",
|
||||
"todo2": "Daha sonra yeniden tarayın; devam ederse site bozuktur"
|
||||
},
|
||||
"522": {
|
||||
"name": "Bağlantı Zaman Aşımına Uğradı",
|
||||
"explanation": "Cloudflare origin ile TCP el sıkışmasını başlattı ama süre içinde tamamlanmadı — origin SYN'i kabul etti fakat hiç yanıt vermedi ya da aradaki bir ağ cihazı paketleri düşürdü.",
|
||||
"cause1": "Origin ciddi şekilde aşırı yüklü (TCP kuyruğu dolu)",
|
||||
"cause2": "Origin'in güvenlik duvarı Cloudflare'in paketlerini sessizce düşürüyor (reddetmiyor)",
|
||||
"cause3": "Cloudflare ile origin arasında ağ/yönlendirme sorunu",
|
||||
"cause4": "Tarama yükü altında: taramanın paralel istekleri origin'in bağlantı tablosunu doldurdu",
|
||||
"todo1": "Paralelliği düşürün (Ayarlar → Hız → Maks Paralellik) ve yeniden tarayın — tarama ortasında görülüyorsa olağan sebep bağlantı tablosunun dolmasıdır",
|
||||
"todo2": "Etkilenen URL'leri tarama bittikten sonra yeniden deneyin"
|
||||
},
|
||||
"523": {
|
||||
"name": "Origin Erişilemez",
|
||||
"explanation": "Cloudflare origin sunucusuna hiçbir şekilde ulaşamadı — genellikle Cloudflare'deki origin kaydında bir DNS sorunu vardır ya da origin'in ağı çökmüştür.",
|
||||
"cause1": "Sitenin Cloudflare'deki DNS kaydı yanlış / kullanılmayan bir IP'yi gösteriyor",
|
||||
"cause2": "Origin'in barındırma sağlayıcısında ağ kesintisi var",
|
||||
"cause3": "Cloudflare ile origin arasında BGP / yönlendirme hatası",
|
||||
"todo1": "Tarayıcının yapabileceği bir şey yok — bu bir origin DNS/ağ yapılandırma hatası"
|
||||
},
|
||||
"524": {
|
||||
"name": "Zaman Aşımı Oluştu",
|
||||
"explanation": "Cloudflare origin ile TCP bağlantısını başarıyla kurdu ama origin, Cloudflare'in süresi içinde (varsayılan 100 sn) eksiksiz bir HTTP yanıtı göndermedi. Origin çalışıyor ama bu istekte çok yavaş.",
|
||||
"cause1": "Sayfada yavaş bir veritabanı sorgusu, harici API çağrısı ya da ağır bir hesaplama var",
|
||||
"cause2": "Origin aşırı yüklü — istekler dolmuş bir worker havuzunun arkasında sıra bekliyor",
|
||||
"cause3": "Tarama yükü altında: tarama origin'in istek kuyruğunu doldurdu ve bu URL arkada çok uzun bekledi",
|
||||
"todo1": "Origin'i doyurmamak için Maks Paralellik / Maks URL/sn değerini düşürün ve Worker Başına Gecikme ekleyin (Ayarlar → Hız)",
|
||||
"todo2": "Gerçekten yavaş sayfalara daha fazla süre tanımak istiyorsanız Yanıt Zaman Aşımı değerini artırın (Ayarlar → Crawler)",
|
||||
"todo3": "Etkilenen URL'leri tarama bittikten sonra yeniden deneyin"
|
||||
},
|
||||
"525": {
|
||||
"name": "SSL El Sıkışması Başarısız",
|
||||
"explanation": "Cloudflare ile origin sunucusu arasındaki TLS el sıkışması başarısız oldu. Bu, adres çubuğunda gördüğünüz tarayıcı↔Cloudflare bağlantısı değil, Cloudflare↔origin bağlantısıdır — bu yüzden site normal bir ziyaretçiye sorunsuz görünürken yine de 525 verebilir.",
|
||||
"cause1": "Origin'in TLS sertifikasının süresi dolmuş, kendinden imzalı ya da yanlış ana bilgisayar adı için verilmiş (Cloudflare 'Full (strict)' modundayken)",
|
||||
"cause2": "Origin yalnızca Cloudflare'in reddettiği eski TLS sürümlerini / şifre takımlarını destekliyor",
|
||||
"cause3": "Origin sunucusu aşırı yüklü ve TLS el sıkışmalarını düşürüyor",
|
||||
"todo1": "Origin TLS yapılandırma hatası — tarayıcının değiştirebileceği bir şey değil. Ana bilgisayarın sertifikasını kontrol edin (HTTPS için URL Detayları'ndaki TLS Cert satırları)"
|
||||
},
|
||||
"526": {
|
||||
"name": "Geçersiz SSL Sertifikası",
|
||||
"explanation": "Cloudflare ('Full (strict)' SSL modunda) origin sunucusunun TLS sertifikasını doğrulayamadı.",
|
||||
"cause1": "Origin sertifikasının süresi dolmuş, kendinden imzalı ya da başka bir ana bilgisayar adı için verilmiş",
|
||||
"cause2": "Origin üzerindeki sertifika zinciri eksik (ara sertifikalar yok)",
|
||||
"todo1": "Origin sertifika sorunu — tarayıcının yapabileceği bir şey yok"
|
||||
},
|
||||
"527": {
|
||||
"name": "Railgun Hatası (Cloudflare, kullanımdan kalkmış)",
|
||||
"explanation": "Cloudflare Railgun'da (artık kullanımdan kaldırılmış bir origin hızlandırma ürünü) bağlantı hatası. Fiilen eskimiş bir koddur; görüyorsanız site çok eski bir Cloudflare kurulumu kullanıyor.",
|
||||
"cause1": "Eski Cloudflare Railgun bağlantı hatası",
|
||||
"todo1": "Tarayıcı tarafında yapılabilecek bir şey yok"
|
||||
},
|
||||
"530": {
|
||||
"name": "Cloudflare Hatası (genellikle güvenlik duvarı / WAF engeli — hata 1020)",
|
||||
"explanation": "530, Cloudflare'in \"bir hata oluştu, gövdedeki 1xxx hata koduna bakın\" deme biçimidir. Tarama bağlamında neredeyse her zaman 1020 \"Access Denied\"dır — bir Cloudflare Firewall / WAF / Bot-Fight kuralı isteği engellemiştir. Daha seyrek olarak 1016 (origin DNS hatası) ya da başka 1xxx kodlarıyla birlikte görülür.",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode tarayıcıyı işaretledi — IP, User-Agent, istek hızı ya da eksik tarayıcı parmak izi nedeniyle",
|
||||
"cause2": "Sitede tarayıcının çözemeyeceği bir \"managed challenge\" var",
|
||||
"cause3": "(Nadiren) engel değil, bir origin DNS hatası (1016)",
|
||||
"todo1": "Tarayıcı benzeri bir User-Agent ayarlayın (Ayarlar → İstekler) — varsayılan tarayıcı UA'sı en sık tetikleyicidir",
|
||||
"todo2": "Maks Paralellik / Maks URL/sn değerini düşürün ve Worker Başına Gecikme ekleyin (Ayarlar → Hız) — tek IP'den gelen hızlı istekler hız tabanlı kuralları tetikler",
|
||||
"todo3": "robots.txt'ye ve belirtilmiş Crawl-delay değerine uyun (Ayarlar → Crawler)",
|
||||
"todo4": "Site sizinse tarayıcıyı Cloudflare'de izin listesine ekleyin; değilse bu site otomatik erişimi bilerek engelliyor"
|
||||
},
|
||||
"401": {
|
||||
"name": "Yetkisiz",
|
||||
"explanation": "Kaynak kimlik doğrulama gerektiriyor ve hiç (ya da geçersiz) kimlik bilgisi gönderilmedi.",
|
||||
"cause1": "Sayfa HTTP Basic/Bearer kimlik doğrulamasının arkasında",
|
||||
"cause2": "Bir oturum çerezi gerekiyor ama gönderilmedi",
|
||||
"todo1": "Kimlik bilgileriniz varsa Ayarlar → Kimlik Doğrulama bölümünde yapılandırın",
|
||||
"todo2": "Site içeriği bir oturumun arkasında tutuyorsa Ayarlar → Çerezler → \"Hepsini kabul et\" seçeneğini kullanın"
|
||||
},
|
||||
"403": {
|
||||
"name": "Yasak",
|
||||
"explanation": "Sunucu isteği anladı ama karşılamayı reddediyor. Taramada bu çoğunlukla gerçek bir izin sorunu değil, bir bot engeli ya da WAF kuralıdır.",
|
||||
"cause1": "Tarayıcının User-Agent'ı, IP'si ya da istek hızı bir WAF / bot koruma kuralını tetikledi",
|
||||
"cause2": "Dizin ya da dosya anonim kullanıcılara gerçekten kapalı",
|
||||
"cause3": "Hotlink / referer koruması",
|
||||
"todo1": "Tarayıcı benzeri bir User-Agent deneyin (Ayarlar → İstekler)",
|
||||
"todo2": "Tarama hızını düşürün (Ayarlar → Hız)",
|
||||
"todo3": "Site genelindeyse site otomatik erişimi aktif olarak engelliyor"
|
||||
},
|
||||
"404": {
|
||||
"name": "Bulunamadı",
|
||||
"explanation": "Sunucuda bu URL'de bir kaynak yok.",
|
||||
"cause1": "Kırık iç/dış bağlantı",
|
||||
"cause2": "Sayfa yönlendirme yapılmadan kaldırıldı",
|
||||
"cause3": "Bir bağlantının href değerinde yazım hatası",
|
||||
"todo1": "Bağlantı veren sayfaları düzeltin ya da bağlantıyı kaldırın — buraya kimin bağlantı verdiğini Gelen Linkler sekmesinde görebilirsiniz"
|
||||
},
|
||||
"408": {
|
||||
"name": "İstek Zaman Aşımı",
|
||||
"explanation": "Sunucu isteğin tamamlanmasını beklerken zaman aşımına uğradı.",
|
||||
"cause1": "Tarayıcı ile sunucu arasında yavaş ağ",
|
||||
"cause2": "Sunucu tarafındaki bağlantı zaman aşımı çok düşük ayarlanmış",
|
||||
"todo1": "Genellikle geçicidir — Yeniden Deneme Sayısı'nı artırın (Ayarlar → Hız)"
|
||||
},
|
||||
"410": {
|
||||
"name": "Kaldırıldı",
|
||||
"explanation": "Kaynak bilerek ve kalıcı olarak kaldırıldı — 404'ten daha güçlü bir sinyaldir.",
|
||||
"cause1": "Sayfa bilerek kaldırıldı; sunucu tarayıcılara onu dizinden çıkarmalarını söylüyor",
|
||||
"todo1": "Bu URL'ye verilen bağlantıları kaldırın — Google onu 404'ten daha hızlı dizinden çıkarır"
|
||||
},
|
||||
"429": {
|
||||
"name": "Çok Fazla İstek (hız sınırı)",
|
||||
"explanation": "Sunucu tarayıcıya hız sınırı uyguluyor — bu IP/User-Agent'tan çok kısa sürede çok fazla istek geldi.",
|
||||
"cause1": "Tarama hızı sunucunun istemci başına sınırını aşıyor",
|
||||
"cause2": "Agresif hız sınırlaması yapan paylaşımlı barındırma",
|
||||
"todo1": "Maks Paralellik / Maks URL/sn değerini düşürün ve Worker Başına Gecikme ekleyin (Ayarlar → Hız)",
|
||||
"todo2": "Retry-After başlığına uyun — sunucu gönderdiyse aşağıdaki \"Yanıt başlıklarından\" bölümüne bakın",
|
||||
"todo3": "\"robots.txt Crawl-delay'e Uy\" seçeneğini açın (Ayarlar → Crawler)"
|
||||
},
|
||||
"451": {
|
||||
"name": "Yasal Nedenlerle Erişilemez",
|
||||
"explanation": "Kaynak yasal nedenlerle engellenmiş (mahkeme kararı, GDPR coğrafi engeli, DMCA vb.).",
|
||||
"cause1": "Coğrafi olarak kısıtlanmış içerik",
|
||||
"cause2": "Yasal zorunluluk nedeniyle kaldırma",
|
||||
"todo1": "Yapılacak bir şey yok — engel kasıtlı"
|
||||
},
|
||||
"500": {
|
||||
"name": "Sunucu İç Hatası",
|
||||
"explanation": "Sayfa oluşturulurken sunucu tarafındaki uygulamada yakalanmamış bir hata oluştu.",
|
||||
"cause1": "Sayfa şablonunda / arka uç kodunda hata",
|
||||
"cause2": "Veritabanı erişilemez",
|
||||
"cause3": "Sunucuda yanlış yapılandırma",
|
||||
"todo1": "Tarama bittikten sonra yeniden deneyin (geçici 500'ler düzelir; kalıcı olanlar gerçek bir hatadır)",
|
||||
"todo2": "500'ler yük altında görülüyorsa tarama bir veritabanını aşırı yüklüyor olabilir — taramayı yavaşlatın (Ayarlar → Hız)"
|
||||
},
|
||||
"502": {
|
||||
"name": "Hatalı Ağ Geçidi",
|
||||
"explanation": "Uygulamanın önündeki ters proxy / yük dengeleyici, arkadaki uygulama sunucusundan geçersiz bir yanıt aldı.",
|
||||
"cause1": "Arkadaki uygulama sunucusu çöktü ya da anlamsız veri döndürdü",
|
||||
"cause2": "Arkadaki sunucu aşırı yüklü — tarama yükü altında yaygındır",
|
||||
"cause3": "Proxy↔arka sunucu zaman aşımı",
|
||||
"todo1": "502'ler toplu halde görülüyorsa tarama hızını düşürün (Ayarlar → Hız)",
|
||||
"todo2": "Etkilenen URL'leri tarama bittikten sonra yeniden deneyin"
|
||||
},
|
||||
"503": {
|
||||
"name": "Hizmet Kullanılamıyor",
|
||||
"explanation": "Sunucu isteği geçici olarak karşılayamıyor — genellikle aşırı yük ya da bakım nedeniyle. Çoğunlukla bir Retry-After başlığıyla gelir.",
|
||||
"cause1": "Sunucu aşırı yüklü — bunu taramanın kendisi de yapabilir",
|
||||
"cause2": "Planlı bakım zamanı",
|
||||
"cause3": "Otomatik ölçeklendirme gecikmesi (yeni sunucular açılıyor)",
|
||||
"todo1": "Maks Paralellik / Maks URL/sn değerini düşürün ve Worker Başına Gecikme ekleyin (Ayarlar → Hız)",
|
||||
"todo2": "Retry-After değerine uyun (aşağıdaki \"Yanıt başlıklarından\" bölümüne bakın)",
|
||||
"todo3": "Etkilenen URL'leri tarama bittikten sonra yeniden deneyin"
|
||||
},
|
||||
"504": {
|
||||
"name": "Ağ Geçidi Zaman Aşımı",
|
||||
"explanation": "Ters proxy / yük dengeleyici, arkadaki uygulama sunucusundan zamanında yanıt alamadı.",
|
||||
"cause1": "Sayfada yavaş veritabanı sorgusu / harici API çağrısı",
|
||||
"cause2": "Arkadaki sunucu aşırı yüklü — istekler sıra bekliyor",
|
||||
"cause3": "Tarama yükü altında: tarama arkadaki worker havuzunu doldurdu",
|
||||
"todo1": "Tarama hızını düşürün (Ayarlar → Hız)",
|
||||
"todo2": "Gerçekten yavaş sayfalar için Yanıt Zaman Aşımı değerini artırın (Ayarlar → Crawler)",
|
||||
"todo3": "Etkilenen URL'leri tarama bittikten sonra yeniden deneyin"
|
||||
},
|
||||
"508": {
|
||||
"name": "Döngü Algılandı",
|
||||
"explanation": "Sunucu isteği işlerken sonsuz bir döngü algıladı (çoğunlukla WebDAV ya da sunucu tarafında bir yönlendirme döngüsü).",
|
||||
"cause1": "Sunucu tarafında yönlendirme döngüsü",
|
||||
"cause2": "Özyinelemeli include / sembolik bağlantı döngüsü",
|
||||
"todo1": "Sunucu hatası — tarayıcının yapabileceği bir şey yok"
|
||||
},
|
||||
"509": {
|
||||
"name": "Bant Genişliği Sınırı Aşıldı",
|
||||
"explanation": "Barındırma hesabının aylık bant genişliği kotasını doldurduğunu belirten standart dışı bir kod (cPanel/Apache).",
|
||||
"cause1": "Paylaşımlı barındırmanın bant genişliği kotası doldu",
|
||||
"todo1": "Tarayıcının yapabileceği bir şey yok — sağlayıcı tüm hesabı kısıtlıyor"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "Cloudflare arkasında — cf-ray {{ray}}. Durum kodu, bir web tarayıcısının gördüğünü değil Cloudflare↔origin bağlantısını anlatıyor.",
|
||||
"fronted": "Önünde {{front}} var.",
|
||||
"origin": "Origin sunucusu: {{server}}.",
|
||||
"retryAfterSeconds": "Sunucu Retry-After: {{seconds}} sn gönderdi — istemcilerden bu süre boyunca açıkça beklemelerini istiyor. Özenli bir tarama bu URL'yi daha erken yeniden istememeli.",
|
||||
"retryAfter": "Sunucu Retry-After: {{value}} gönderdi.",
|
||||
"cfCache": "Cloudflare önbellek durumu: {{status}}.",
|
||||
"xRobots": "X-Robots-Tag: {{value}} — durum kodundan bağımsız olarak indekslenebilirliği etkiler."
|
||||
},
|
||||
"robots": {
|
||||
"title": "İstek Atılmadı — robots.txt İzin Vermiyor",
|
||||
"explanation": "Tarayıcı bu URL için hiç istek göndermedi: yapılandırılmış user-agent için bir robots.txt kuralı buna izin vermiyor. Hiç yanıt istenmediği için durum kodu yok — bu bir bağlantı hatası değil.",
|
||||
"cause1": "Sitenin robots.txt dosyasındaki bir Disallow kuralı tarama user-agent'ı için bu yolla eşleşiyor",
|
||||
"cause2": "URL taranan sayfalardan bağlantı aldığı için kaydedilip sayılıyor ama hiç çekilmiyor",
|
||||
"todo1": "Hariç tutma kasıtlıysa yapılacak bir şey yok — Google da taramaz",
|
||||
"todo2": "Sayfanın indekslenebilir olması gerekip gerekmediğini kontrol edin: engellenen bir URL bağlantı değerini aktaramaz ve içeriği hiç görülmez",
|
||||
"todo3": "Hangi kuralın eşleştiğini görmek için Yardım → Robots.txt Test Aracı'nı kullanın ya da yine de denetlemek için Ayarlar → Crawler'da \"robots.txt'ye Uy\" seçeneğini kapatın"
|
||||
},
|
||||
"network": {
|
||||
"title": "HTTP Yanıtı Yok — Ağ / Bağlantı Hatası",
|
||||
"explanationDetail": "İstek hiçbir HTTP yanıtı almadı. Tarayıcının kaydettiği: \"{{detail}}\".",
|
||||
"explanation": "İstek hiçbir HTTP yanıtı almadı — herhangi bir durum satırı gelmeden önce DNS sorgusu, TCP bağlantısı, TLS el sıkışması ya da yanıtın okunması başarısız oldu.",
|
||||
"cause1": "DNS ana bilgisayar adını çözemedi (yazım hatası, ölü alan adı ya da yalnızca özel bir ağ içinden çözülebilen bir ad)",
|
||||
"cause2": "TCP bağlantısı reddedildi ya da zaman aşımına uğradı (sunucu kapalı, güvenlik duvarı, yanlış port)",
|
||||
"cause3": "TLS el sıkışması başarısız oldu (süresi dolmuş/geçersiz sertifika, protokol uyuşmazlığı)",
|
||||
"cause4": "Bağlantı sıfırlandı ya da yanıtın ortasında okuma zaman aşımına uğradı",
|
||||
"cause5": "İstek, Yanıt Zaman Aşımı (Ayarlar → Crawler) ya da Maks yanıt süresi (Ayarlar → Gelişmiş) aşıldığı için tarayıcı tarafından iptal edildi",
|
||||
"todo1": "Ana bilgisayar adının çözüldüğünü ve sitenin bu bilgisayardaki normal bir web tarayıcısından erişilebilir olduğunu kontrol edin",
|
||||
"todo2": "Erişilebilir ama yavaşsa Yanıt Zaman Aşımı değerini artırın (Ayarlar → Crawler)",
|
||||
"todo3": "Yalnızca yük altında başarısız oluyorsa Maks Paralellik / Maks URL/sn değerini düşürün (Ayarlar → Hız) — origin'de bağlantı tablosunun dolması istemciye ağ hatası gibi görünür",
|
||||
"todo4": "Etkilenen URL'leri tarama bittikten sonra yeniden deneyin (geçici sıfırlamalar tekrar denemede düzelir)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — Bilgilendirme",
|
||||
"fallback": "Bilgilendirme",
|
||||
"explanation": "Bir 1xx yanıtı ({{text}}). Bunlar ara yanıtlardır; tarayıcı bunları geçici sayar ve nihai durumu bekler."
|
||||
},
|
||||
"success": {
|
||||
"fallback": "Başarılı",
|
||||
"explanation": "İstek başarılı oldu. Teşhis gerekmiyor — bu bölüm yalnızca hata ve yönlendirme kodları için ek ayrıntı içerir."
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "Yönlendirme",
|
||||
"explanation301": "Kalıcı yönlendirme. Arama motorları sıralama sinyallerini hedefe aktarır ve dizinlerini yeni URL ile günceller.",
|
||||
"explanation302": "Geçici yönlendirme. Arama motorları orijinal URL'yi dizinde tutar. Uzun süre kullanılan bir 302 yaygın bir SEO hatasıdır — genellikle 301 olmalıdır.",
|
||||
"explanation308": "Kalıcı yönlendirme (301 gibi, ama istek yöntemi korunur).",
|
||||
"explanation304": "Değiştirilmedi — önbellekteki kopya hâlâ güncel. Tarayıcı normalde koşullu başlıklar olmadan istek gönderir, bu yüzden bu alışılmadık bir durum.",
|
||||
"explanationOther": "Bir {{code}} yönlendirmesi ({{text}}).",
|
||||
"location": "Location başlığı şunu gösteriyor: {{location}}",
|
||||
"noLocation": "Location başlığı yakalanmadı — bir yönlendirme için alışılmadık.",
|
||||
"todoPermanent": "Bu yönlendirme kalıcıysa sıralama sinyallerinin birleşmesi için 301/308 yapın",
|
||||
"todoChain": "Tam yönlendirme zincirini ve son hedefini görmek için Yanıt Kodları sekmesine bakın"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "İstemci Hatası",
|
||||
"explanation": "4xx kodu, sunucunun isteği istemcinin hatası olarak reddettiği anlamına gelir (hatalı istek, izin yok, bulunamadı vb.). Taramada bağlantı verilen sayfalardaki kalıcı 4xx'ler genellikle kırık bağlantı demektir; ara sıra görülen 4xx'ler bot koruması olabilir.",
|
||||
"cause1": "Kırık ya da bozuk bağlantı",
|
||||
"cause2": "Bot koruması / WAF kuralı",
|
||||
"cause3": "Sunucunun kabul etmediği yöntem ya da içerik",
|
||||
"todo1": "Bu URL'ye neyin bağlantı verdiğini görmek için Gelen Linkler sekmesine bakın",
|
||||
"todo2": "Bir engel gibi görünüyorsa tarayıcı benzeri bir User-Agent ve daha yavaş bir hız deneyin (Ayarlar → İstekler / Hız)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "Sunucu Hatası",
|
||||
"explanation": "5xx kodu, sunucunun geçerli kabul ettiği bir isteği yerine getiremediği anlamına gelir. Tarama yükü altında bunlar çoğunlukla gerçek bir sayfa hatası değil, taramanın origin'i aşırı yüklemesidir.",
|
||||
"cause1": "Sunucu tarafında hata ya da çökme",
|
||||
"cause2": "Origin aşırı yüklü — muhtemelen taramanın kendisi yüzünden",
|
||||
"cause3": "Arka sunucu/proxy hatası",
|
||||
"todo1": "5xx'ler toplu halde görülüyorsa Maks Paralellik / Maks URL/sn değerini düşürün ve Worker Başına Gecikme ekleyin (Ayarlar → Hız)",
|
||||
"todo2": "Yeniden Deneme Sayısı'nı artırın (Ayarlar → Hız) ve etkilenen URL'leri yeniden tarayın"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "Bu sayfada tespit edilen başlık yok (",
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
"urls": "URL",
|
||||
"percentOfTotal": "占总数百分比",
|
||||
"siteSoft404": "不存在的页面返回 {{status}}(soft 404)",
|
||||
"siteSoft404Hint": "请求一个不存在的页面时得到的是 {{status}} 而不是 404,因此该站点上的失效链接在搜索引擎看来就像正常页面。"
|
||||
"siteSoft404Hint": "请求一个不存在的页面时得到的是 {{status}} 而不是 404,因此该站点上的失效链接在搜索引擎看来就像正常页面。",
|
||||
"siteBotBlocked": "机器人防护拦截了部分抓取",
|
||||
"siteBotBlockedHint": "站点的机器人防护以验证页面代替了这些页面的响应。它们未被抓取,也不计入失效链接和站点地图检查。请降低抓取速度或将抓取器的 User-Agent 加入白名单,然后重新抓取。"
|
||||
},
|
||||
"integrations": {
|
||||
"intro": "使用你自己的凭据连接外部服务。API 密钥和机密会通过操作系统的安全存储在本地加密保存,绝不会离开这台机器。",
|
||||
@@ -382,7 +384,50 @@
|
||||
"orphanGscImpressions_other": "{{formatted}} 次展示",
|
||||
"orphanGa4Sessions_other": "{{formatted}} 次 GA4 会话",
|
||||
"orphanMetricaVisits_other": "{{formatted}} 次 Metrica 访问(着陆页)",
|
||||
"orphanMetricaPageviews_other": "{{formatted}} 次 Metrica 页面浏览(命中)"
|
||||
"orphanMetricaPageviews_other": "{{formatted}} 次 Metrica 页面浏览(命中)",
|
||||
"noResponse": "无响应",
|
||||
"bucketOrphan": "0(孤立)",
|
||||
"badge": {
|
||||
"ok": "正常",
|
||||
"non": "否",
|
||||
"warn": "警告",
|
||||
"bad": "差",
|
||||
"err": "错误",
|
||||
"slow": "慢",
|
||||
"net": "网络"
|
||||
},
|
||||
"domainHealth": "{{ok}}/{{total}} 正常 · {{rate}}% 错误",
|
||||
"avgMs": "平均 {{ms}} 毫秒",
|
||||
"distinctIds_other": "{{count}} 个不同 ID",
|
||||
"pageCount_other": "{{formatted}} 个页面",
|
||||
"imageCount_other": "{{formatted}} 张图片",
|
||||
"hits_other": "{{formatted}} 次",
|
||||
"avgWords": "平均 {{formatted}}",
|
||||
"lastmod": "lastmod {{date}}",
|
||||
"fromSitemap": "来自 {{sitemap}}",
|
||||
"position": {
|
||||
"navigation": "导航",
|
||||
"header": "页眉",
|
||||
"content": "正文",
|
||||
"sidebar": "侧边栏",
|
||||
"footer": "页脚",
|
||||
"aside": "Aside"
|
||||
},
|
||||
"indexability": {
|
||||
"indexable": "可索引",
|
||||
"noindex": "不可索引——noindex",
|
||||
"canonical": "不可索引——已规范化",
|
||||
"robotsBlocked": "不可索引——被 robots.txt 拦截",
|
||||
"redirect": "不可索引——重定向",
|
||||
"clientError": "不可索引——4xx",
|
||||
"serverError": "不可索引——5xx",
|
||||
"botBlocked": "未知——被机器人防护拦截"
|
||||
},
|
||||
"kind": {
|
||||
"image": "图片",
|
||||
"font": "字体",
|
||||
"other": "其他"
|
||||
}
|
||||
},
|
||||
"sitemap": {
|
||||
"title": "站点地图校验器",
|
||||
@@ -1028,7 +1073,267 @@
|
||||
"diag": {
|
||||
"fromHeaders": "来自响应头",
|
||||
"likelyCauses": "可能的原因",
|
||||
"whatToDo": "该怎么做"
|
||||
"whatToDo": "该怎么做",
|
||||
"cloudflareStyle": "Cloudflare 式边缘代码",
|
||||
"code": {
|
||||
"520": {
|
||||
"name": "Web 服务器返回了未知错误",
|
||||
"explanation": "Cloudflare 已连接到源站服务器,但收到的是空的、格式错误或意外的响应(例如响应中途连接被重置、源站返回的响应头过大,或源站进程崩溃)。这是表示“源站出了问题”的通用代码。",
|
||||
"cause1": "源站服务器在处理请求时崩溃、重启或耗尽了工作进程/内存",
|
||||
"cause2": "源站在发送完整响应之前关闭了 TCP 连接(通常是应用层超时或因内存不足被终止)",
|
||||
"cause3": "抓取的请求速率压垮了源站的进程池——在负载下成批出现的 520 几乎都是这个原因",
|
||||
"cause4": "源站返回的响应头超过了 Cloudflare 允许的大小(少见;通常是应用配置错误)",
|
||||
"todo1": "如果 520 在大量 URL 上成批出现,请降低最大并发数 / 最大 URL/秒并添加每个工作线程的延迟(设置 → 速度)——抓取正在使源站过载",
|
||||
"todo2": "抓取完成后重新抓取受影响的 URL(暂时性的 520 重试后通常会消失——在设置 → 速度中提高重试次数)",
|
||||
"todo3": "如果只有特定 URL 持续返回 520,说明这些页面存在服务器端缺陷——请告知网站所有者"
|
||||
},
|
||||
"521": {
|
||||
"name": "Web 服务器已宕机",
|
||||
"explanation": "Cloudflare 无法与源站服务器建立 TCP 连接——源站主动拒绝了连接,或未在预期端口上监听。",
|
||||
"cause1": "源站 Web 服务器(nginx/Apache/Node 等)已停止或崩溃",
|
||||
"cause2": "源站防火墙屏蔽了 Cloudflare 的 IP 段(配置错误——源站应放行 Cloudflare)",
|
||||
"cause3": "源站服务器过载,已无法接受新连接",
|
||||
"todo1": "这是源站可用性问题,抓取器无法绕过——这些 URL 对应的网站实际上已宕机",
|
||||
"todo2": "稍后重新抓取;如果问题持续,说明网站已损坏"
|
||||
},
|
||||
"522": {
|
||||
"name": "连接超时",
|
||||
"explanation": "Cloudflare 与源站开始了 TCP 握手,但未在超时时间内完成——源站接受了 SYN 却始终没有回复,或者中间的网络设备丢弃了数据包。",
|
||||
"cause1": "源站严重过载(TCP 积压队列已满)",
|
||||
"cause2": "源站防火墙静默丢弃(而非拒绝)Cloudflare 的数据包",
|
||||
"cause3": "Cloudflare 与源站之间存在网络/路由问题",
|
||||
"cause4": "抓取负载下:抓取的并行请求耗尽了源站的连接表",
|
||||
"todo1": "降低并发(设置 → 速度 → 最大并发数)后重新抓取——在抓取过程中出现时,通常是连接表耗尽所致",
|
||||
"todo2": "抓取结束后重试受影响的 URL"
|
||||
},
|
||||
"523": {
|
||||
"name": "源站无法访问",
|
||||
"explanation": "Cloudflare 完全无法路由到源站服务器——通常是 Cloudflare 中源站记录的 DNS 问题,或者源站网络已中断。",
|
||||
"cause1": "网站在 Cloudflare 中的 DNS 记录指向了错误或已失效的 IP",
|
||||
"cause2": "源站的托管服务商出现网络故障",
|
||||
"cause3": "Cloudflare 与源站之间的 BGP / 路由故障",
|
||||
"todo1": "抓取器无能为力——这是源站 DNS/网络配置错误"
|
||||
},
|
||||
"524": {
|
||||
"name": "发生超时",
|
||||
"explanation": "Cloudflare 已成功与源站建立 TCP 连接,但源站未在 Cloudflare 的超时时间(默认 100 秒)内发送完整的 HTTP 响应。源站仍在运行,只是处理这个请求太慢。",
|
||||
"cause1": "页面上有缓慢的数据库查询、外部 API 调用或繁重计算",
|
||||
"cause2": "源站过载——请求在已饱和的工作进程池后面排队",
|
||||
"cause3": "抓取负载下:抓取填满了源站的请求队列,这个 URL 等待时间过长",
|
||||
"todo1": "降低最大并发数 / 最大 URL/秒并添加每个工作线程的延迟(设置 → 速度),避免使源站饱和",
|
||||
"todo2": "如果想给确实很慢的页面更多时间,请增大响应超时(设置 → 抓取器)",
|
||||
"todo3": "抓取结束后重试受影响的 URL"
|
||||
},
|
||||
"525": {
|
||||
"name": "SSL 握手失败",
|
||||
"explanation": "Cloudflare 与源站服务器之间的 TLS 握手失败。这是 Cloudflare↔源站这一段,而不是你在地址栏中看到的浏览器↔Cloudflare 那一段——因此普通访客看到的网站可能一切正常,却仍然返回 525。",
|
||||
"cause1": "源站的 TLS 证书已过期、为自签名证书或签发给了其他主机名(Cloudflare 处于 'Full (strict)' 模式时)",
|
||||
"cause2": "源站只支持被 Cloudflare 拒绝的旧版 TLS / 加密套件",
|
||||
"cause3": "源站服务器过载,正在丢弃 TLS 握手",
|
||||
"todo1": "源站 TLS 配置错误——抓取器无法更改。请检查该主机的证书(对于 HTTPS,见 URL 详情中的 TLS Cert 行)"
|
||||
},
|
||||
"526": {
|
||||
"name": "SSL 证书无效",
|
||||
"explanation": "Cloudflare(处于 'Full (strict)' SSL 模式)无法验证源站服务器的 TLS 证书。",
|
||||
"cause1": "源站证书已过期、为自签名证书或签发给了其他主机名",
|
||||
"cause2": "源站的证书链不完整(缺少中间证书)",
|
||||
"todo1": "源站证书问题——抓取器无能为力"
|
||||
},
|
||||
"527": {
|
||||
"name": "Railgun 错误(Cloudflare,已弃用)",
|
||||
"explanation": "Cloudflare Railgun(一款已停用的源站加速产品)中的连接错误。实际上已经过时;如果看到它,说明网站使用的是非常旧的 Cloudflare 配置。",
|
||||
"cause1": "旧版 Cloudflare Railgun 连接失败",
|
||||
"todo1": "抓取器这一侧无事可做"
|
||||
},
|
||||
"530": {
|
||||
"name": "Cloudflare 错误(通常是防火墙 / WAF 拦截——错误 1020)",
|
||||
"explanation": "530 是 Cloudflare 表示“发生了错误,请查看响应正文中的 1xxx 错误代码”的方式。在抓取场景中几乎总是 1020“Access Denied”——某条 Cloudflare Firewall / WAF / Bot-Fight 规则拦截了请求。较少见的情况下会伴随 1016(源站 DNS 错误)或其他 1xxx 代码。",
|
||||
"cause1": "Cloudflare WAF / Firewall Rules / Bot Fight Mode 标记了抓取器——依据 IP、User-Agent、请求速率或缺少浏览器指纹",
|
||||
"cause2": "网站启用了抓取器无法通过的“managed challenge”",
|
||||
"cause3": "(少见)源站 DNS 错误(1016),而不是拦截",
|
||||
"todo1": "设置类似浏览器的 User-Agent(设置 → 请求)——默认的抓取器 UA 是头号触发因素",
|
||||
"todo2": "降低最大并发数 / 最大 URL/秒并添加每个工作线程的延迟(设置 → 速度)——来自单个 IP 的快速请求会触发基于速率的规则",
|
||||
"todo3": "遵守 robots.txt 及其中声明的 Crawl-delay(设置 → 抓取器)",
|
||||
"todo4": "如果网站是你的,请在 Cloudflare 中将抓取器加入白名单;否则说明该网站在有意阻止自动访问"
|
||||
},
|
||||
"401": {
|
||||
"name": "未授权",
|
||||
"explanation": "该资源需要身份验证,但请求未发送凭据(或凭据无效)。",
|
||||
"cause1": "页面受 HTTP Basic/Bearer 身份验证保护",
|
||||
"cause2": "需要会话 Cookie,但未发送",
|
||||
"todo1": "如果有凭据,请在设置 → 认证中配置",
|
||||
"todo2": "如果网站把内容放在会话之后,请使用设置 → Cookie →“全部接受”"
|
||||
},
|
||||
"403": {
|
||||
"name": "禁止访问",
|
||||
"explanation": "服务器理解了请求但拒绝提供。在抓取中,这通常是机器人拦截或 WAF 规则,而不是真正的权限问题。",
|
||||
"cause1": "抓取器的 User-Agent、IP 或请求速率触发了 WAF / 机器人防护规则",
|
||||
"cause2": "目录或文件确实不对匿名用户开放",
|
||||
"cause3": "防盗链 / Referer 保护",
|
||||
"todo1": "尝试类似浏览器的 User-Agent(设置 → 请求)",
|
||||
"todo2": "降低抓取速度(设置 → 速度)",
|
||||
"todo3": "如果整个网站都这样,说明网站在主动阻止自动访问"
|
||||
},
|
||||
"404": {
|
||||
"name": "未找到",
|
||||
"explanation": "服务器在此 URL 上没有任何资源。",
|
||||
"cause1": "内部/外部链接失效",
|
||||
"cause2": "页面被删除且未设置重定向",
|
||||
"cause3": "链接 href 中有拼写错误",
|
||||
"todo1": "修复或移除指向此处的页面中的链接——在入链标签页中可以看到哪些页面链接到这里"
|
||||
},
|
||||
"408": {
|
||||
"name": "请求超时",
|
||||
"explanation": "服务器在等待请求完成时超时。",
|
||||
"cause1": "抓取器与服务器之间网络缓慢",
|
||||
"cause2": "服务器端连接超时设置得过低",
|
||||
"todo1": "通常是暂时性的——提高重试次数(设置 → 速度)"
|
||||
},
|
||||
"410": {
|
||||
"name": "已永久删除",
|
||||
"explanation": "该资源已被有意且永久地删除——比 404 更明确。",
|
||||
"cause1": "页面被有意下线;服务器在告诉抓取器将其移除",
|
||||
"todo1": "移除指向此 URL 的链接——Google 会比 404 更快地将其从索引中移除"
|
||||
},
|
||||
"429": {
|
||||
"name": "请求过多(限速)",
|
||||
"explanation": "服务器正在对抓取器限速——该 IP/User-Agent 在过短时间内发出了过多请求。",
|
||||
"cause1": "抓取速率超过了服务器对单个客户端的限制",
|
||||
"cause2": "共享主机的限流非常严格",
|
||||
"todo1": "降低最大并发数 / 最大 URL/秒并添加每个工作线程的延迟(设置 → 速度)",
|
||||
"todo2": "遵守 Retry-After 响应头——如果服务器发送了该响应头,请查看下方的“来自响应头”",
|
||||
"todo3": "开启“遵守 robots.txt 的 Crawl-delay”(设置 → 抓取器)"
|
||||
},
|
||||
"451": {
|
||||
"name": "因法律原因不可用",
|
||||
"explanation": "该资源因法律原因被屏蔽(法院命令、GDPR 地域屏蔽、DMCA 等)。",
|
||||
"cause1": "受地域限制的内容",
|
||||
"cause2": "依法要求的下架",
|
||||
"todo1": "无需处理——屏蔽是有意为之"
|
||||
},
|
||||
"500": {
|
||||
"name": "服务器内部错误",
|
||||
"explanation": "服务器端应用在生成页面时发生了未处理的错误。",
|
||||
"cause1": "页面模板 / 后端代码存在缺陷",
|
||||
"cause2": "数据库不可用",
|
||||
"cause3": "服务器配置错误",
|
||||
"todo1": "抓取结束后重试(暂时性的 500 会消失;持续出现的才是真正的缺陷)",
|
||||
"todo2": "如果 500 在负载下出现,可能是抓取压垮了数据库——请放慢抓取(设置 → 速度)"
|
||||
},
|
||||
"502": {
|
||||
"name": "网关错误",
|
||||
"explanation": "应用前面的反向代理 / 负载均衡器从上游应用服务器收到了无效响应。",
|
||||
"cause1": "上游应用服务器崩溃或返回了无效数据",
|
||||
"cause2": "上游过载——在抓取负载下很常见",
|
||||
"cause3": "代理↔上游超时",
|
||||
"todo1": "如果 502 成批出现,请降低抓取速度(设置 → 速度)",
|
||||
"todo2": "抓取结束后重试受影响的 URL"
|
||||
},
|
||||
"503": {
|
||||
"name": "服务不可用",
|
||||
"explanation": "服务器暂时无法处理请求——通常是由于过载或维护。往往会附带 Retry-After 响应头。",
|
||||
"cause1": "服务器过载——抓取本身就可能导致",
|
||||
"cause2": "计划内的维护时段",
|
||||
"cause3": "自动扩容延迟(新实例正在启动)",
|
||||
"todo1": "降低最大并发数 / 最大 URL/秒并添加每个工作线程的延迟(设置 → 速度)",
|
||||
"todo2": "遵守 Retry-After(见下方的“来自响应头”)",
|
||||
"todo3": "抓取结束后重试受影响的 URL"
|
||||
},
|
||||
"504": {
|
||||
"name": "网关超时",
|
||||
"explanation": "反向代理 / 负载均衡器未能及时从上游应用服务器获得响应。",
|
||||
"cause1": "页面上有缓慢的数据库查询 / 外部 API 调用",
|
||||
"cause2": "上游过载——请求在排队",
|
||||
"cause3": "抓取负载下:抓取使上游工作进程池饱和",
|
||||
"todo1": "降低抓取速度(设置 → 速度)",
|
||||
"todo2": "针对确实很慢的页面,增大响应超时(设置 → 抓取器)",
|
||||
"todo3": "抓取结束后重试受影响的 URL"
|
||||
},
|
||||
"508": {
|
||||
"name": "检测到循环",
|
||||
"explanation": "服务器在处理请求时检测到无限循环(通常是 WebDAV 或服务器端的重定向循环)。",
|
||||
"cause1": "服务器端重定向循环",
|
||||
"cause2": "递归 include / 符号链接循环",
|
||||
"todo1": "服务器缺陷——抓取器无能为力"
|
||||
},
|
||||
"509": {
|
||||
"name": "超出带宽限制",
|
||||
"explanation": "一个非标准代码(cPanel/Apache),表示托管账户已用完每月的带宽配额。",
|
||||
"cause1": "共享主机的带宽配额已用尽",
|
||||
"todo1": "抓取器无能为力——托管服务商正在限制整个账户"
|
||||
}
|
||||
},
|
||||
"signal": {
|
||||
"cloudflare": "位于 Cloudflare 之后——cf-ray {{ray}}。状态码描述的是 Cloudflare↔源站之间的连接,而不是浏览器看到的结果。",
|
||||
"fronted": "前端为 {{front}}。",
|
||||
"origin": "源站服务器:{{server}}。",
|
||||
"retryAfterSeconds": "服务器发送了 Retry-After: {{seconds}} 秒——它明确要求客户端在这段时间内暂停。礼貌的抓取不应在此之前再次请求该 URL。",
|
||||
"retryAfter": "服务器发送了 Retry-After: {{value}}。",
|
||||
"cfCache": "Cloudflare 缓存状态:{{status}}。",
|
||||
"xRobots": "X-Robots-Tag: {{value}}——无论状态码如何都会影响可索引性。"
|
||||
},
|
||||
"robots": {
|
||||
"title": "未请求——被 robots.txt 禁止",
|
||||
"explanation": "抓取器从未向此 URL 发送请求:针对所配置 user-agent 的 robots.txt 规则禁止访问它。由于从未请求响应,所以没有状态码——这不是连接失败。",
|
||||
"cause1": "网站 robots.txt 中的 Disallow 规则对抓取所用的 user-agent 匹配了此路径",
|
||||
"cause2": "已抓取的页面链接到此 URL,因此它被记录和计数,但从未被获取",
|
||||
"todo1": "如果是有意排除,则无需处理——Google 也不会抓取它",
|
||||
"todo2": "检查该页面是否应当可被索引:被禁止的 URL 无法传递链接权重,其内容也永远不会被看到",
|
||||
"todo3": "使用帮助 → robots.txt 测试器查看匹配的是哪条规则,或在设置 → 抓取器中关闭“遵守 robots.txt”以照样审计它"
|
||||
},
|
||||
"network": {
|
||||
"title": "无 HTTP 响应——网络 / 连接错误",
|
||||
"explanationDetail": "请求始终没有收到 HTTP 响应。抓取器记录的信息:“{{detail}}”。",
|
||||
"explanation": "请求始终没有收到 HTTP 响应——在任何状态行到达之前,DNS 解析、TCP 连接、TLS 握手或响应读取就已失败。",
|
||||
"cause1": "DNS 无法解析主机名(拼写错误、域名已失效,或该名称只能在私有网络内解析)",
|
||||
"cause2": "TCP 连接被拒绝或超时(主机宕机、防火墙、端口错误)",
|
||||
"cause3": "TLS 握手失败(证书过期/无效、协议不匹配)",
|
||||
"cause4": "连接被重置,或响应中途读取超时",
|
||||
"cause5": "请求超过了响应超时(设置 → 抓取器)或最大响应时间(设置 → 高级),被抓取器中止",
|
||||
"todo1": "检查主机名能否解析,以及在这台电脑上用普通浏览器能否访问该网站",
|
||||
"todo2": "如果能访问但很慢,请增大响应超时(设置 → 抓取器)",
|
||||
"todo3": "如果只在负载下失败,请降低最大并发数 / 最大 URL/秒(设置 → 速度)——源站连接表耗尽在客户端看来就像网络错误",
|
||||
"todo4": "抓取结束后重试受影响的 URL(暂时性的连接重置重试后会恢复)"
|
||||
},
|
||||
"informational": {
|
||||
"title": "{{code}} — 信息性",
|
||||
"fallback": "信息性",
|
||||
"explanation": "1xx 响应({{text}})。这些是临时响应;抓取器将其视为暂时状态并等待最终状态。"
|
||||
},
|
||||
"success": {
|
||||
"fallback": "成功",
|
||||
"explanation": "请求成功。无需诊断——本部分只为错误码和重定向码提供额外说明。"
|
||||
},
|
||||
"redirect": {
|
||||
"fallback": "重定向",
|
||||
"explanation301": "永久重定向。搜索引擎会把排名信号转移到目标页面,并在索引中更新为新 URL。",
|
||||
"explanation302": "临时重定向。搜索引擎会继续在索引中保留原始 URL。长期使用 302 是常见的 SEO 错误——通常应改为 301。",
|
||||
"explanation308": "永久重定向(与 301 类似,但会保留请求方法)。",
|
||||
"explanation304": "未修改——缓存副本仍然有效。抓取器通常不带条件请求头发送请求,因此这种情况并不常见。",
|
||||
"explanationOther": "一个 {{code}} 重定向({{text}})。",
|
||||
"location": "Location 响应头指向:{{location}}",
|
||||
"noLocation": "未捕获到 Location 响应头——对于重定向来说并不常见。",
|
||||
"todoPermanent": "如果这是永久重定向,请改为 301/308,以便合并排名信号",
|
||||
"todoChain": "查看响应代码标签页,了解完整的重定向链及其最终目标"
|
||||
},
|
||||
"clientError": {
|
||||
"fallback": "客户端错误",
|
||||
"explanation": "4xx 代码表示服务器把请求当作客户端的错误而拒绝(请求错误、不允许、未找到等)。在抓取中,被链接页面上持续出现的 4xx 通常意味着链接失效;偶发的 4xx 可能是机器人防护所致。",
|
||||
"cause1": "链接失效或格式错误",
|
||||
"cause2": "机器人防护 / WAF 规则",
|
||||
"cause3": "服务器拒绝的请求方法或内容",
|
||||
"todo1": "查看入链标签页,了解哪些页面链接到此 URL",
|
||||
"todo2": "如果看起来像是拦截,请尝试类似浏览器的 User-Agent 和更低的速率(设置 → 请求 / 速度)"
|
||||
},
|
||||
"serverError": {
|
||||
"fallback": "服务器错误",
|
||||
"explanation": "5xx 代码表示服务器无法完成一个它认为有效的请求。在抓取负载下,这往往是抓取压垮了源站,而不是页面本身真的有缺陷。",
|
||||
"cause1": "服务器端错误或崩溃",
|
||||
"cause2": "源站过载——可能正是抓取本身造成的",
|
||||
"cause3": "上游/代理故障",
|
||||
"todo1": "如果 5xx 成批出现,请降低最大并发数 / 最大 URL/秒并添加每个工作线程的延迟(设置 → 速度)",
|
||||
"todo2": "提高重试次数(设置 → 速度)并重新抓取受影响的 URL"
|
||||
}
|
||||
},
|
||||
"outline": {
|
||||
"emptyPrefix": "此页面未检测到标题(没有任何",
|
||||
|
||||
@@ -136,6 +136,7 @@ export const TAB_QUICK_FILTERS: Partial<Record<TabKey, TabQuickFilter[]>> = {
|
||||
'response-codes': [
|
||||
{ label: 'All', category: 'all' },
|
||||
{ label: 'Blocked by Robots', category: 'status:blocked-robots' },
|
||||
{ label: 'Blocked by Bot Protection', category: 'status:bot-blocked' },
|
||||
{ label: 'No Response', category: 'status:no-response' },
|
||||
{ label: 'Success (2xx)', category: 'status:2xx' },
|
||||
{ label: 'Redirection (3xx)', category: 'status:3xx' },
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
* Pure / sync / renderer-only — everything it needs is already in the
|
||||
* `CrawlUrlRow` (statusCode, statusText) plus the captured response
|
||||
* headers, so there's no IPC.
|
||||
*
|
||||
* Every sentence lives in `DIAGNOSIS_EN` and is looked up under `diag.`
|
||||
* in the locale files; `tests/status-diagnosis-i18n.test.ts` keeps
|
||||
* `en.json` identical to it. The caller passes its `t` — without one the
|
||||
* English text is returned (with `{{placeholders}}` filled in).
|
||||
*/
|
||||
|
||||
export interface StatusDiagnosis {
|
||||
@@ -33,6 +38,352 @@ export interface StatusDiagnosis {
|
||||
whatToDo: string[];
|
||||
}
|
||||
|
||||
/** i18next's `t`, or anything shaped like it. */
|
||||
export type DiagnosisTranslate = (key: string, options: Record<string, unknown>) => string;
|
||||
|
||||
/**
|
||||
* Every string of the diagnosis banner, in English. Keys are relative to
|
||||
* the `diag.` namespace. Settings are named exactly as the Settings dialog
|
||||
* labels them, so the user can find what a step points at.
|
||||
*/
|
||||
export const DIAGNOSIS_EN = {
|
||||
// ── Cloudflare's 5xx extension codes (520-530). Not origin errors in the
|
||||
// usual sense — they describe a failure between Cloudflare and the
|
||||
// customer's origin server, OR (530) a Cloudflare-side block.
|
||||
// `statusText` for these is usually the unhelpful generic "Server
|
||||
// Error", which is exactly why this table exists.
|
||||
'cloudflareStyle': 'Cloudflare-style edge code',
|
||||
'code.520.name': 'Web Server Returned an Unknown Error',
|
||||
'code.520.explanation':
|
||||
"Cloudflare connected to the origin server but got back an empty, malformed, or unexpected response (e.g. the connection was reset mid-response, the origin returned headers that are too large, or the origin process crashed). It is a catch-all 'something went wrong at the origin' code.",
|
||||
'code.520.cause1': 'Origin server crashed, restarted, or ran out of workers/memory while serving the request',
|
||||
'code.520.cause2':
|
||||
'Origin closed the TCP connection before sending a complete response (often an app-level timeout or OOM kill)',
|
||||
'code.520.cause3':
|
||||
"The crawl's request rate overwhelmed the origin's process pool — 520s that appear in bursts under load almost always mean this",
|
||||
'code.520.cause4':
|
||||
'Origin returned response headers larger than Cloudflare allows (rare; usually a misconfigured app)',
|
||||
'code.520.todo1':
|
||||
'If 520s appear in bursts across many URLs, lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed) — the origin is being overloaded by the crawl',
|
||||
'code.520.todo2':
|
||||
'Re-crawl the affected URLs after the crawl finishes (transient 520s often clear on retry — raise Retry Attempts in Settings → Speed)',
|
||||
'code.520.todo3':
|
||||
'If only specific URLs 520 consistently, those pages have a server-side bug — flag them to the site owner',
|
||||
|
||||
'code.521.name': 'Web Server Is Down',
|
||||
'code.521.explanation':
|
||||
'Cloudflare could not open a TCP connection to the origin server — the origin actively refused the connection or is not listening on the expected port.',
|
||||
'code.521.cause1': 'Origin web server (nginx/Apache/Node/etc.) is stopped or has crashed',
|
||||
'code.521.cause2':
|
||||
"Origin's firewall is blocking Cloudflare's IP ranges (a misconfiguration — the origin should allow Cloudflare)",
|
||||
'code.521.cause3': 'Origin server is overloaded to the point of not accepting new connections',
|
||||
'code.521.todo1':
|
||||
'This is an origin-availability problem, not something the crawler can work around — the site is effectively down for these URLs',
|
||||
'code.521.todo2': 'Re-crawl later; if it persists, the site is broken',
|
||||
|
||||
'code.522.name': 'Connection Timed Out',
|
||||
'code.522.explanation':
|
||||
'Cloudflare started a TCP handshake with the origin but it never completed within the timeout — the origin accepted the SYN but never replied, or a network device between them dropped the packets.',
|
||||
'code.522.cause1': 'Origin is severely overloaded (TCP backlog full)',
|
||||
'code.522.cause2': "Origin's firewall is silently dropping (not rejecting) Cloudflare's packets",
|
||||
'code.522.cause3': 'Network/routing issue between Cloudflare and the origin',
|
||||
'code.522.cause4': "Under crawl load: the origin's connection table is exhausted by the crawl's parallel requests",
|
||||
'code.522.todo1':
|
||||
'Lower Max Concurrency (Settings → Speed) and re-crawl — connection-table exhaustion is the usual cause when this shows up mid-crawl',
|
||||
'code.522.todo2': 'Retry the affected URLs after the crawl',
|
||||
|
||||
'code.523.name': 'Origin Is Unreachable',
|
||||
'code.523.explanation':
|
||||
"Cloudflare could not route to the origin server at all — typically a DNS problem in Cloudflare's origin record, or the origin's network is down.",
|
||||
'code.523.cause1': "The site's DNS record in Cloudflare points to a wrong / dead IP",
|
||||
'code.523.cause2': "Origin's hosting provider has a network outage",
|
||||
'code.523.cause3': 'BGP / routing failure between Cloudflare and the origin',
|
||||
'code.523.todo1': 'Nothing the crawler can do — this is an origin DNS/network misconfiguration',
|
||||
|
||||
'code.524.name': 'A Timeout Occurred',
|
||||
'code.524.explanation':
|
||||
"Cloudflare made a TCP connection to the origin successfully, but the origin did not send a complete HTTP response within Cloudflare's timeout (100 s by default). The origin is alive but too slow on this request.",
|
||||
'code.524.cause1': 'A slow database query, external API call, or heavy computation on the page',
|
||||
'code.524.cause2': 'Origin is overloaded — requests are queuing behind a saturated worker pool',
|
||||
'code.524.cause3':
|
||||
"Under crawl load: the crawl filled the origin's request queue and this URL waited too long behind it",
|
||||
'code.524.todo1':
|
||||
"Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed) so the origin isn't saturated",
|
||||
'code.524.todo2':
|
||||
'Increase Response Timeout (Settings → Crawler) if you want to give genuinely slow pages more time',
|
||||
'code.524.todo3': 'Retry affected URLs after the crawl',
|
||||
|
||||
'code.525.name': 'SSL Handshake Failed',
|
||||
'code.525.explanation':
|
||||
'The TLS handshake between Cloudflare and the origin server failed. This is the Cloudflare↔origin leg, not the browser↔Cloudflare leg you see in the address bar — so the site can look fine to a normal visitor and still 525.',
|
||||
'code.525.cause1':
|
||||
"Origin's TLS certificate is expired, self-signed, or for the wrong hostname (with Cloudflare in 'Full (strict)' mode)",
|
||||
'code.525.cause2': 'Origin only supports old TLS versions / cipher suites that Cloudflare rejects',
|
||||
'code.525.cause3': 'Origin server is overloaded and dropping TLS handshakes',
|
||||
'code.525.todo1':
|
||||
"Origin TLS misconfiguration — not something the crawler can change. Check the host's certificate (the TLS Cert rows in URL Details, for HTTPS)",
|
||||
|
||||
'code.526.name': 'Invalid SSL Certificate',
|
||||
'code.526.explanation':
|
||||
"Cloudflare (in 'Full (strict)' SSL mode) could not validate the origin server's TLS certificate.",
|
||||
'code.526.cause1': 'Origin certificate is expired, self-signed, or issued for a different hostname',
|
||||
'code.526.cause2': 'Certificate chain on the origin is incomplete (missing intermediates)',
|
||||
'code.526.todo1': 'Origin certificate problem — nothing the crawler can do',
|
||||
|
||||
'code.527.name': 'Railgun Error (Cloudflare, deprecated)',
|
||||
'code.527.explanation':
|
||||
'A connection error in Cloudflare Railgun (a now-retired origin-acceleration product). Effectively obsolete; if you see it the site is on a very old Cloudflare setup.',
|
||||
'code.527.cause1': 'Legacy Cloudflare Railgun connection failure',
|
||||
'code.527.todo1': 'Nothing actionable from the crawler side',
|
||||
|
||||
'code.530.name': 'Cloudflare Error (usually a firewall / WAF block — error 1020)',
|
||||
'code.530.explanation':
|
||||
'A 530 is Cloudflare\'s way of saying "an error happened, see the error 1xxx code in the body". In a crawl context it is almost always 1020 "Access Denied" — a Cloudflare Firewall / WAF / Bot-Fight rule blocked the request. Less commonly it pairs with 1016 (origin DNS error) or other 1xxx codes.',
|
||||
'code.530.cause1':
|
||||
'Cloudflare WAF / Firewall Rules / Bot Fight Mode flagged the crawler — by IP, by User-Agent, by request rate, or by missing browser fingerprints',
|
||||
'code.530.cause2': 'The site has a "managed challenge" the crawler can\'t solve',
|
||||
'code.530.cause3': '(Rarely) an origin DNS error (1016) rather than a block',
|
||||
'code.530.todo1': 'Set a browser-like User-Agent (Settings → Requests) — the default crawler UA is the #1 trigger',
|
||||
'code.530.todo2':
|
||||
'Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed) — rapid requests from one IP trip rate-based rules',
|
||||
'code.530.todo3': 'Respect robots.txt and any declared Crawl-delay (Settings → Crawler)',
|
||||
'code.530.todo4':
|
||||
'If the site owner is you, allowlist the crawler in Cloudflare; otherwise this site is intentionally blocking automated access',
|
||||
|
||||
// ── Other codes worth a richer explanation than `statusText`.
|
||||
'code.401.name': 'Unauthorized',
|
||||
'code.401.explanation': 'The resource requires authentication and none (or invalid) credentials were sent.',
|
||||
'code.401.cause1': 'Page is behind HTTP Basic/Bearer auth',
|
||||
'code.401.cause2': 'A session cookie is required and none was sent',
|
||||
'code.401.todo1': 'Configure credentials in Settings → Authentication if you have them',
|
||||
'code.401.todo2': 'Use Settings → Cookies → "Accept all" if the site gates content behind a session',
|
||||
|
||||
'code.403.name': 'Forbidden',
|
||||
'code.403.explanation':
|
||||
'The server understood the request but refuses to serve it. In a crawl this is most often a bot block or a WAF rule rather than a genuine permission issue.',
|
||||
'code.403.cause1': 'WAF / bot-protection rule triggered by the crawler User-Agent, IP, or request rate',
|
||||
'code.403.cause2': 'Directory or file genuinely not accessible to anonymous users',
|
||||
'code.403.cause3': 'Hotlink / referer protection',
|
||||
'code.403.todo1': 'Try a browser-like User-Agent (Settings → Requests)',
|
||||
'code.403.todo2': 'Lower the crawl rate (Settings → Speed)',
|
||||
'code.403.todo3': "If it's site-wide, the site is actively blocking automated access",
|
||||
|
||||
'code.404.name': 'Not Found',
|
||||
'code.404.explanation': 'The server has no resource at this URL.',
|
||||
'code.404.cause1': 'Broken internal/external link',
|
||||
'code.404.cause2': 'Page was removed without a redirect',
|
||||
'code.404.cause3': 'Typo in a link href',
|
||||
'code.404.todo1': 'Fix or remove the linking pages — see the Inlinks tab for who links here',
|
||||
|
||||
'code.408.name': 'Request Timeout',
|
||||
'code.408.explanation': 'The server timed out waiting for the request to complete.',
|
||||
'code.408.cause1': 'Slow network between crawler and server',
|
||||
'code.408.cause2': 'Server-side connection timeout set very low',
|
||||
'code.408.todo1': 'Usually transient — raise Retry Attempts (Settings → Speed)',
|
||||
|
||||
'code.410.name': 'Gone',
|
||||
'code.410.explanation': 'The resource was intentionally and permanently removed — stronger than a 404.',
|
||||
'code.410.cause1': 'Page deliberately retired; the server is telling crawlers to drop it',
|
||||
'code.410.todo1': 'Remove links to this URL — Google will de-index it faster than a 404',
|
||||
|
||||
'code.429.name': 'Too Many Requests (rate limited)',
|
||||
'code.429.explanation':
|
||||
'The server is rate-limiting the crawler — too many requests in too short a window from this IP/User-Agent.',
|
||||
'code.429.cause1': "Crawl rate exceeds the server's per-client limit",
|
||||
'code.429.cause2': 'Shared hosting with aggressive throttling',
|
||||
'code.429.todo1': 'Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed)',
|
||||
'code.429.todo2': 'Honour the Retry-After header — see "From the response headers" below if the server sent one',
|
||||
'code.429.todo3': 'Turn on "Respect robots.txt Crawl-delay" (Settings → Crawler)',
|
||||
|
||||
'code.451.name': 'Unavailable for Legal Reasons',
|
||||
'code.451.explanation': 'The resource is blocked for legal reasons (court order, GDPR geoblock, DMCA, etc.).',
|
||||
'code.451.cause1': 'Geo-restricted content',
|
||||
'code.451.cause2': 'Legally-mandated takedown',
|
||||
'code.451.todo1': 'Nothing — the block is intentional',
|
||||
|
||||
'code.500.name': 'Internal Server Error',
|
||||
'code.500.explanation': 'An unhandled error occurred in the server-side application while building the page.',
|
||||
'code.500.cause1': 'Bug in the page template / backend code',
|
||||
'code.500.cause2': 'Database unavailable',
|
||||
'code.500.cause3': 'Misconfiguration on the server',
|
||||
'code.500.todo1': 'Retry after the crawl (transient 500s clear; persistent ones are a real bug)',
|
||||
'code.500.todo2':
|
||||
'If 500s appear under load, the crawl may be overwhelming a database — slow the crawl (Settings → Speed)',
|
||||
|
||||
'code.502.name': 'Bad Gateway',
|
||||
'code.502.explanation':
|
||||
'A reverse proxy / load balancer in front of the app got an invalid response from the upstream application server.',
|
||||
'code.502.cause1': 'Upstream app server crashed or returned garbage',
|
||||
'code.502.cause2': 'Upstream is overloaded — common under crawl load',
|
||||
'code.502.cause3': 'Proxy↔upstream timeout',
|
||||
'code.502.todo1': 'Lower the crawl rate (Settings → Speed) if 502s appear in bursts',
|
||||
'code.502.todo2': 'Retry affected URLs after the crawl',
|
||||
|
||||
'code.503.name': 'Service Unavailable',
|
||||
'code.503.explanation':
|
||||
'The server is temporarily unable to handle the request — usually overload or maintenance. Often comes with a Retry-After header.',
|
||||
'code.503.cause1': 'Server overloaded — the crawl itself can cause this',
|
||||
'code.503.cause2': 'Scheduled maintenance window',
|
||||
'code.503.cause3': 'Auto-scaling lag (new instances spinning up)',
|
||||
'code.503.todo1': 'Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed)',
|
||||
'code.503.todo2': 'Honour Retry-After (see "From the response headers" below)',
|
||||
'code.503.todo3': 'Retry affected URLs after the crawl',
|
||||
|
||||
'code.504.name': 'Gateway Timeout',
|
||||
'code.504.explanation': "A reverse proxy / load balancer didn't get a response from the upstream app server in time.",
|
||||
'code.504.cause1': 'Slow database query / external API call on the page',
|
||||
'code.504.cause2': 'Upstream overloaded — requests queueing',
|
||||
'code.504.cause3': 'Under crawl load: the crawl saturated the upstream worker pool',
|
||||
'code.504.todo1': 'Lower the crawl rate (Settings → Speed)',
|
||||
'code.504.todo2': 'Increase Response Timeout (Settings → Crawler) for genuinely slow pages',
|
||||
'code.504.todo3': 'Retry affected URLs after the crawl',
|
||||
|
||||
'code.508.name': 'Loop Detected',
|
||||
'code.508.explanation':
|
||||
'The server detected an infinite loop while processing the request (often WebDAV or a redirect loop server-side).',
|
||||
'code.508.cause1': 'Server-side redirect loop',
|
||||
'code.508.cause2': 'Recursive include / symlink loop',
|
||||
'code.508.todo1': 'Server bug — nothing the crawler can do',
|
||||
|
||||
'code.509.name': 'Bandwidth Limit Exceeded',
|
||||
'code.509.explanation':
|
||||
'A non-standard code (cPanel/Apache) meaning the hosting account hit its monthly bandwidth cap.',
|
||||
'code.509.cause1': 'Shared hosting bandwidth quota exhausted',
|
||||
'code.509.todo1': 'Nothing the crawler can do — the host is throttling the whole account',
|
||||
|
||||
// ── Signals read off the response headers.
|
||||
'signal.cloudflare':
|
||||
'Behind Cloudflare — cf-ray {{ray}}. The status code describes the Cloudflare↔origin link, not what a browser sees.',
|
||||
'signal.fronted': 'Fronted by {{front}}.',
|
||||
'signal.origin': 'Origin server: {{server}}.',
|
||||
'signal.retryAfterSeconds':
|
||||
'Server sent Retry-After: {{seconds}} s — it is explicitly asking clients to back off for that long. A polite crawl should not re-request this URL sooner.',
|
||||
'signal.retryAfter': 'Server sent Retry-After: {{value}}.',
|
||||
'signal.cfCache': 'Cloudflare cache status: {{status}}.',
|
||||
'signal.xRobots': 'X-Robots-Tag: {{value}} — affects indexability regardless of the status.',
|
||||
|
||||
// ── Deliberately not requested (robots.txt).
|
||||
'robots.title': 'Not Requested — Disallowed by robots.txt',
|
||||
'robots.explanation':
|
||||
'The crawler never sent a request for this URL: a robots.txt rule for the configured user-agent disallows it. There is no status code because no response was ever asked for — this is not a connection failure.',
|
||||
'robots.cause1': 'A Disallow rule in the site robots.txt matches this path for the crawl user-agent',
|
||||
'robots.cause2': 'The URL is linked from crawled pages, so it is recorded and counted, but never fetched',
|
||||
'robots.todo1': 'Nothing, if the exclusion is intended — Google will not crawl it either',
|
||||
'robots.todo2':
|
||||
'Check whether the page should be indexable: a disallowed URL cannot pass on link equity and its content is never seen',
|
||||
'robots.todo3':
|
||||
'Use Help → Robots.txt Tester to see which rule matches, or turn off "Respect robots.txt" in Settings → Crawler to audit it anyway',
|
||||
|
||||
// ── Network error (no response).
|
||||
'network.title': 'No HTTP Response — Network / Connection Error',
|
||||
'network.explanationDetail':
|
||||
'The request never received an HTTP response. The crawler recorded: "{{detail}}".',
|
||||
'network.explanation':
|
||||
'The request never received an HTTP response — DNS lookup, TCP connection, TLS handshake, or the response read failed before any status line arrived.',
|
||||
'network.cause1':
|
||||
'DNS could not resolve the hostname (typo, dead domain, or a name only resolvable from inside a private network)',
|
||||
'network.cause2': 'TCP connection refused or timed out (host down, firewall, wrong port)',
|
||||
'network.cause3': 'TLS handshake failed (expired/invalid certificate, protocol mismatch)',
|
||||
'network.cause4': 'Connection reset or read timed out mid-response',
|
||||
'network.cause5':
|
||||
'Request aborted by the crawler because it exceeded Response Timeout (Settings → Crawler) or Max response time (Settings → Advanced)',
|
||||
'network.todo1': 'Check the hostname resolves and the site is reachable from a normal browser on this machine',
|
||||
'network.todo2': 'If it is reachable but slow, raise Response Timeout (Settings → Crawler)',
|
||||
'network.todo3':
|
||||
'If it only fails under load, lower Max Concurrency / Max URL/s (Settings → Speed) — connection-table exhaustion on the origin looks like a network error to the client',
|
||||
'network.todo4': 'Retry the affected URLs after the crawl (transient resets clear on retry)',
|
||||
|
||||
// ── 1xx / 2xx / 3xx — not errors.
|
||||
'informational.title': '{{code}} — Informational',
|
||||
'informational.fallback': 'Informational',
|
||||
'informational.explanation':
|
||||
'A 1xx response ({{text}}). These are interim responses; the crawler treats them as transient and waits for the final status.',
|
||||
'success.fallback': 'Success',
|
||||
'success.explanation':
|
||||
'The request succeeded. No diagnosis needed — this section only has extra detail for error and redirect codes.',
|
||||
'redirect.fallback': 'Redirect',
|
||||
'redirect.explanation301':
|
||||
'Permanent redirect. Search engines transfer ranking signals to the target and update their index to the new URL.',
|
||||
'redirect.explanation302':
|
||||
'Temporary redirect. Search engines keep the original URL indexed. A 302 used long-term is a common SEO mistake — it should usually be a 301.',
|
||||
'redirect.explanation308': 'Permanent redirect (like 301 but the request method is preserved).',
|
||||
'redirect.explanation304':
|
||||
'Not Modified — the cached copy is still fresh. The crawler normally requests without conditional headers, so this is unusual.',
|
||||
'redirect.explanationOther': 'A {{code}} redirect ({{text}}).',
|
||||
'redirect.location': 'Location header points to: {{location}}',
|
||||
'redirect.noLocation': 'No Location header was captured — unusual for a redirect.',
|
||||
'redirect.todoPermanent':
|
||||
'If this redirect is permanent, change it to a 301/308 so ranking signals consolidate',
|
||||
'redirect.todoChain': 'Check the Response Codes tab to see the full redirect chain and its final target',
|
||||
|
||||
// ── 4xx / 5xx with no specific entry.
|
||||
'clientError.fallback': 'Client Error',
|
||||
'clientError.explanation':
|
||||
"A 4xx code means the server rejected the request as the client's fault (bad request, not allowed, not found, etc.). In a crawl, persistent 4xx on linked pages usually means broken links; sporadic 4xx can be bot-protection.",
|
||||
'clientError.cause1': 'Broken or malformed link',
|
||||
'clientError.cause2': 'Bot-protection / WAF rule',
|
||||
'clientError.cause3': 'Method or content the server refuses',
|
||||
'clientError.todo1': 'Check the Inlinks tab to see what links to this URL',
|
||||
'clientError.todo2':
|
||||
'If it looks like a block, try a browser-like User-Agent and a slower rate (Settings → Requests / Speed)',
|
||||
'serverError.fallback': 'Server Error',
|
||||
'serverError.explanation':
|
||||
'A 5xx code means the server failed to fulfil a request it accepted as valid. Under crawl load these are frequently the crawl overwhelming the origin rather than a genuine page bug.',
|
||||
'serverError.cause1': 'Server-side error or crash',
|
||||
'serverError.cause2': 'Origin overloaded — possibly by the crawl itself',
|
||||
'serverError.cause3': 'Upstream/proxy failure',
|
||||
'serverError.todo1':
|
||||
'Lower Max Concurrency / Max URL/s and add a Per-Worker Delay (Settings → Speed) if 5xx appear in bursts',
|
||||
'serverError.todo2': 'Raise Retry Attempts (Settings → Speed) and re-crawl affected URLs',
|
||||
} as const;
|
||||
|
||||
export type DiagnosisKey = keyof typeof DIAGNOSIS_EN;
|
||||
|
||||
/** Severity and bullet counts per code; the text is under `code.<n>.*` in `DIAGNOSIS_EN`. */
|
||||
interface CodeInfo {
|
||||
severity: StatusDiagnosis['severity'];
|
||||
causes: number;
|
||||
todos: number;
|
||||
/** A Cloudflare edge code — the title names it as such. */
|
||||
cloudflare?: true;
|
||||
}
|
||||
|
||||
const CODES: Record<number, CodeInfo> = {
|
||||
520: { severity: 'error', causes: 4, todos: 3, cloudflare: true },
|
||||
521: { severity: 'error', causes: 3, todos: 2, cloudflare: true },
|
||||
522: { severity: 'error', causes: 4, todos: 2, cloudflare: true },
|
||||
523: { severity: 'error', causes: 3, todos: 1, cloudflare: true },
|
||||
524: { severity: 'error', causes: 3, todos: 3, cloudflare: true },
|
||||
525: { severity: 'error', causes: 3, todos: 1, cloudflare: true },
|
||||
526: { severity: 'error', causes: 2, todos: 1, cloudflare: true },
|
||||
527: { severity: 'error', causes: 1, todos: 1 },
|
||||
530: { severity: 'error', causes: 3, todos: 4 },
|
||||
401: { severity: 'warning', causes: 2, todos: 2 },
|
||||
403: { severity: 'warning', causes: 3, todos: 3 },
|
||||
404: { severity: 'warning', causes: 3, todos: 1 },
|
||||
408: { severity: 'warning', causes: 2, todos: 1 },
|
||||
410: { severity: 'info', causes: 1, todos: 1 },
|
||||
429: { severity: 'warning', causes: 2, todos: 3 },
|
||||
451: { severity: 'info', causes: 2, todos: 1 },
|
||||
500: { severity: 'error', causes: 3, todos: 2 },
|
||||
502: { severity: 'error', causes: 3, todos: 2 },
|
||||
503: { severity: 'error', causes: 3, todos: 3 },
|
||||
504: { severity: 'error', causes: 3, todos: 3 },
|
||||
508: { severity: 'error', causes: 2, todos: 1 },
|
||||
509: { severity: 'error', causes: 1, todos: 1 },
|
||||
};
|
||||
|
||||
/** Every key the code tables reference — for the i18n test. */
|
||||
export function diagnosisCodeKeys(): string[] {
|
||||
const keys: string[] = [];
|
||||
for (const [code, info] of Object.entries(CODES)) {
|
||||
keys.push(`code.${code}.name`, `code.${code}.explanation`);
|
||||
for (let i = 1; i <= info.causes; i++) keys.push(`code.${code}.cause${i}`);
|
||||
for (let i = 1; i <= info.todos; i++) keys.push(`code.${code}.todo${i}`);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
type HeaderList = ReadonlyArray<{ name: string; value: string }>;
|
||||
|
||||
function headerMap(headers: HeaderList): Map<string, string> {
|
||||
@@ -51,7 +402,7 @@ function detectFront(h: Map<string, string>): string | null {
|
||||
if (server.includes('vercel') || h.has('x-vercel-id')) return 'Vercel';
|
||||
if (server.includes('netlify')) return 'Netlify';
|
||||
if (h.has('x-sucuri-id') || h.has('x-sucuri-cache')) return 'Sucuri WAF';
|
||||
if (server.includes('varnish') || h.has('x-varnish')) return 'Varnish cache';
|
||||
if (server.includes('varnish') || h.has('x-varnish')) return 'Varnish';
|
||||
if (server.includes('nginx')) return 'nginx';
|
||||
if (server.includes('apache')) return 'Apache';
|
||||
if (server.includes('litespeed')) return 'LiteSpeed';
|
||||
@@ -59,293 +410,6 @@ function detectFront(h: Map<string, string>): string | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
interface CodeInfo {
|
||||
title: string;
|
||||
severity: StatusDiagnosis['severity'];
|
||||
explanation: string;
|
||||
causes: string[];
|
||||
whatToDo: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloudflare's 5xx extension codes (520-530). These are NOT origin
|
||||
* errors in the usual sense — they describe a failure between Cloudflare
|
||||
* and the customer's origin server, OR (530) a Cloudflare-side block.
|
||||
* `statusText` for these is usually the unhelpful generic "Server Error",
|
||||
* which is exactly why this table exists.
|
||||
*/
|
||||
const CLOUDFLARE_CODES: Record<number, CodeInfo> = {
|
||||
520: {
|
||||
title: '520 — Web Server Returned an Unknown Error (Cloudflare)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
"Cloudflare connected to the origin server but got back an empty, malformed, or unexpected response (e.g. the connection was reset mid-response, the origin returned headers that are too large, or the origin process crashed). It is a catch-all 'something went wrong at the origin' code.",
|
||||
causes: [
|
||||
'Origin server crashed, restarted, or ran out of workers/memory while serving the request',
|
||||
'Origin closed the TCP connection before sending a complete response (often an app-level timeout or OOM kill)',
|
||||
"The crawl's request rate overwhelmed the origin's process pool — 520s that appear in bursts under load almost always mean this",
|
||||
'Origin returned response headers larger than Cloudflare allows (rare; usually a misconfigured app)',
|
||||
],
|
||||
whatToDo: [
|
||||
'If 520s appear in bursts across many URLs, lower Max Threads / Max URL/s (Settings → Speed) and add a Crawl Delay — the origin is being overloaded by the crawl',
|
||||
'Re-crawl the affected URLs after the crawl finishes (transient 520s often clear on retry — bump Retry Attempts in Settings → Speed)',
|
||||
'If only specific URLs 520 consistently, those pages have a server-side bug — flag them to the site owner',
|
||||
],
|
||||
},
|
||||
521: {
|
||||
title: '521 — Web Server Is Down (Cloudflare)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
'Cloudflare could not open a TCP connection to the origin server — the origin actively refused the connection or is not listening on the expected port.',
|
||||
causes: [
|
||||
'Origin web server (nginx/Apache/Node/etc.) is stopped or has crashed',
|
||||
"Origin's firewall is blocking Cloudflare's IP ranges (a misconfiguration — the origin should allow Cloudflare)",
|
||||
'Origin server is overloaded to the point of not accepting new connections',
|
||||
],
|
||||
whatToDo: [
|
||||
'This is an origin-availability problem, not something the crawler can work around — the site is effectively down for these URLs',
|
||||
'Re-crawl later; if it persists, the site is broken',
|
||||
],
|
||||
},
|
||||
522: {
|
||||
title: '522 — Connection Timed Out (Cloudflare)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
'Cloudflare started a TCP handshake with the origin but it never completed within the timeout — the origin accepted the SYN but never replied, or a network device between them dropped the packets.',
|
||||
causes: [
|
||||
'Origin is severely overloaded (TCP backlog full)',
|
||||
"Origin's firewall is silently dropping (not rejecting) Cloudflare's packets",
|
||||
'Network/routing issue between Cloudflare and the origin',
|
||||
"Under crawl load: the origin's connection table is exhausted by the crawl's parallel requests",
|
||||
],
|
||||
whatToDo: [
|
||||
'Lower concurrency (Settings → Speed → Max Threads) and re-crawl — connection-table exhaustion is the usual cause when this shows up mid-crawl',
|
||||
'Retry the affected URLs after the crawl',
|
||||
],
|
||||
},
|
||||
523: {
|
||||
title: '523 — Origin Is Unreachable (Cloudflare)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
"Cloudflare could not route to the origin server at all — typically a DNS problem in Cloudflare's origin record, or the origin's network is down.",
|
||||
causes: [
|
||||
"The site's DNS record in Cloudflare points to a wrong / dead IP",
|
||||
"Origin's hosting provider has a network outage",
|
||||
'BGP / routing failure between Cloudflare and the origin',
|
||||
],
|
||||
whatToDo: [
|
||||
'Nothing the crawler can do — this is an origin DNS/network misconfiguration',
|
||||
],
|
||||
},
|
||||
524: {
|
||||
title: '524 — A Timeout Occurred (Cloudflare)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
'Cloudflare made a TCP connection to the origin successfully, but the origin did not send a complete HTTP response within Cloudflare\'s timeout (100 s by default). The origin is alive but too slow on this request.',
|
||||
causes: [
|
||||
'A slow database query, external API call, or heavy computation on the page',
|
||||
'Origin is overloaded — requests are queuing behind a saturated worker pool',
|
||||
"Under crawl load: the crawl filled the origin's request queue and this URL waited too long behind it",
|
||||
],
|
||||
whatToDo: [
|
||||
'Lower Max Threads / Max URL/s and add a Crawl Delay (Settings → Speed) so the origin isn\'t saturated',
|
||||
'Increase Response Timeout (Settings → Crawler) if you want to give genuinely slow pages more time',
|
||||
'Retry affected URLs after the crawl',
|
||||
],
|
||||
},
|
||||
525: {
|
||||
title: '525 — SSL Handshake Failed (Cloudflare)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
"The TLS handshake between Cloudflare and the origin server failed. This is the Cloudflare↔origin leg, not the browser↔Cloudflare leg you see in the address bar — so the site can look fine to a normal visitor and still 525.",
|
||||
causes: [
|
||||
"Origin's TLS certificate is expired, self-signed, or for the wrong hostname (with Cloudflare in 'Full (strict)' mode)",
|
||||
'Origin only supports old TLS versions / cipher suites that Cloudflare rejects',
|
||||
'Origin server is overloaded and dropping TLS handshakes',
|
||||
],
|
||||
whatToDo: [
|
||||
"Origin TLS misconfiguration — not something the crawler can change. Check the host's certificate (URL Details → TLS Cert if HTTPS)",
|
||||
],
|
||||
},
|
||||
526: {
|
||||
title: '526 — Invalid SSL Certificate (Cloudflare)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
"Cloudflare (in 'Full (strict)' SSL mode) could not validate the origin server's TLS certificate.",
|
||||
causes: [
|
||||
"Origin certificate is expired, self-signed, or issued for a different hostname",
|
||||
'Certificate chain on the origin is incomplete (missing intermediates)',
|
||||
],
|
||||
whatToDo: ['Origin certificate problem — nothing the crawler can do'],
|
||||
},
|
||||
527: {
|
||||
title: '527 — Railgun Error (Cloudflare, deprecated)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
'A connection error in Cloudflare Railgun (a now-retired origin-acceleration product). Effectively obsolete; if you see it the site is on a very old Cloudflare setup.',
|
||||
causes: ['Legacy Cloudflare Railgun connection failure'],
|
||||
whatToDo: ['Nothing actionable from the crawler side'],
|
||||
},
|
||||
530: {
|
||||
title: '530 — Cloudflare Error (usually a firewall / WAF block — error 1020)',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
'A 530 is Cloudflare\'s way of saying "an error happened, see the error 1xxx code in the body". In a crawl context it is almost always 1020 "Access Denied" — a Cloudflare Firewall / WAF / Bot-Fight rule blocked the request. Less commonly it pairs with 1016 (origin DNS error) or other 1xxx codes.',
|
||||
causes: [
|
||||
"Cloudflare WAF / Firewall Rules / Bot Fight Mode flagged the crawler — by IP, by User-Agent, by request rate, or by missing browser fingerprints",
|
||||
'The site has a "managed challenge" the crawler can\'t solve',
|
||||
'(Rarely) an origin DNS error (1016) rather than a block',
|
||||
],
|
||||
whatToDo: [
|
||||
'Set a browser-like User-Agent (Settings → Requests) — the default crawler UA is the #1 trigger',
|
||||
'Drop Max Threads / Max URL/s and add a Crawl Delay (Settings → Speed) — rapid requests from one IP trip rate-based rules',
|
||||
'Respect robots.txt and any declared Crawl-Delay (Settings → Crawler)',
|
||||
"If the site owner is you, allowlist the crawler in Cloudflare; otherwise this site is intentionally blocking automated access",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
/** Non-Cloudflare codes worth a richer explanation than `statusText`. */
|
||||
const GENERIC_CODES: Record<number, CodeInfo> = {
|
||||
401: {
|
||||
title: '401 — Unauthorized',
|
||||
severity: 'warning',
|
||||
explanation: 'The resource requires authentication and none (or invalid) credentials were sent.',
|
||||
causes: ['Page is behind HTTP Basic/Bearer auth', 'A session cookie is required and none was sent'],
|
||||
whatToDo: [
|
||||
'Configure credentials in Settings → Authentication if you have them',
|
||||
'Use Settings → Cookies → "Accept all" if the site gates content behind a session',
|
||||
],
|
||||
},
|
||||
403: {
|
||||
title: '403 — Forbidden',
|
||||
severity: 'warning',
|
||||
explanation:
|
||||
'The server understood the request but refuses to serve it. In a crawl this is most often a bot block or a WAF rule rather than a genuine permission issue.',
|
||||
causes: [
|
||||
'WAF / bot-protection rule triggered by the crawler User-Agent, IP, or request rate',
|
||||
'Directory or file genuinely not accessible to anonymous users',
|
||||
'Hotlink / referer protection',
|
||||
],
|
||||
whatToDo: [
|
||||
'Try a browser-like User-Agent (Settings → Requests)',
|
||||
'Lower the crawl rate (Settings → Speed)',
|
||||
"If it's site-wide, the site is actively blocking automated access",
|
||||
],
|
||||
},
|
||||
404: {
|
||||
title: '404 — Not Found',
|
||||
severity: 'warning',
|
||||
explanation: 'The server has no resource at this URL.',
|
||||
causes: ['Broken internal/external link', 'Page was removed without a redirect', 'Typo in a link href'],
|
||||
whatToDo: ['Fix or remove the linking pages — see the Inlinks tab for who links here'],
|
||||
},
|
||||
408: {
|
||||
title: '408 — Request Timeout',
|
||||
severity: 'warning',
|
||||
explanation: "The server timed out waiting for the request to complete.",
|
||||
causes: ['Slow network between crawler and server', 'Server-side connection timeout set very low'],
|
||||
whatToDo: ['Usually transient — bump Retry Attempts (Settings → Speed)'],
|
||||
},
|
||||
410: {
|
||||
title: '410 — Gone',
|
||||
severity: 'info',
|
||||
explanation: 'The resource was intentionally and permanently removed — stronger than a 404.',
|
||||
causes: ['Page deliberately retired; the server is telling crawlers to drop it'],
|
||||
whatToDo: ['Remove links to this URL — Google will de-index it faster than a 404'],
|
||||
},
|
||||
429: {
|
||||
title: '429 — Too Many Requests (rate limited)',
|
||||
severity: 'warning',
|
||||
explanation:
|
||||
'The server is rate-limiting the crawler — too many requests in too short a window from this IP/User-Agent.',
|
||||
causes: ['Crawl rate exceeds the server\'s per-client limit', 'Shared hosting with aggressive throttling'],
|
||||
whatToDo: [
|
||||
'Lower Max Threads / Max URL/s and add a Crawl Delay (Settings → Speed)',
|
||||
'Honour the Retry-After header — see the Signals below if the server sent one',
|
||||
'Enable robots.txt Crawl-Delay respect (Settings → Crawler)',
|
||||
],
|
||||
},
|
||||
451: {
|
||||
title: '451 — Unavailable for Legal Reasons',
|
||||
severity: 'info',
|
||||
explanation: 'The resource is blocked for legal reasons (court order, GDPR geoblock, DMCA, etc.).',
|
||||
causes: ['Geo-restricted content', 'Legally-mandated takedown'],
|
||||
whatToDo: ['Nothing — the block is intentional'],
|
||||
},
|
||||
500: {
|
||||
title: '500 — Internal Server Error',
|
||||
severity: 'error',
|
||||
explanation: 'An unhandled error occurred in the server-side application while building the page.',
|
||||
causes: ['Bug in the page template / backend code', 'Database unavailable', 'Misconfiguration on the server'],
|
||||
whatToDo: [
|
||||
'Retry after the crawl (transient 500s clear; persistent ones are a real bug)',
|
||||
'If 500s appear under load, the crawl may be overwhelming a database — slow the crawl (Settings → Speed)',
|
||||
],
|
||||
},
|
||||
502: {
|
||||
title: '502 — Bad Gateway',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
'A reverse proxy / load balancer in front of the app got an invalid response from the upstream application server.',
|
||||
causes: [
|
||||
'Upstream app server crashed or returned garbage',
|
||||
'Upstream is overloaded — common under crawl load',
|
||||
'Proxy↔upstream timeout',
|
||||
],
|
||||
whatToDo: [
|
||||
'Lower the crawl rate (Settings → Speed) if 502s appear in bursts',
|
||||
'Retry affected URLs after the crawl',
|
||||
],
|
||||
},
|
||||
503: {
|
||||
title: '503 — Service Unavailable',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
'The server is temporarily unable to handle the request — usually overload or maintenance. Often comes with a Retry-After header.',
|
||||
causes: [
|
||||
'Server overloaded — the crawl itself can cause this',
|
||||
'Scheduled maintenance window',
|
||||
'Auto-scaling lag (new instances spinning up)',
|
||||
],
|
||||
whatToDo: [
|
||||
'Lower Max Threads / Max URL/s and add a Crawl Delay (Settings → Speed)',
|
||||
'Honour Retry-After (see Signals below)',
|
||||
'Retry affected URLs after the crawl',
|
||||
],
|
||||
},
|
||||
504: {
|
||||
title: '504 — Gateway Timeout',
|
||||
severity: 'error',
|
||||
explanation:
|
||||
"A reverse proxy / load balancer didn't get a response from the upstream app server in time.",
|
||||
causes: [
|
||||
'Slow database query / external API call on the page',
|
||||
'Upstream overloaded — requests queueing',
|
||||
'Under crawl load: the crawl saturated the upstream worker pool',
|
||||
],
|
||||
whatToDo: [
|
||||
'Lower the crawl rate (Settings → Speed)',
|
||||
'Increase Response Timeout (Settings → Crawler) for genuinely slow pages',
|
||||
'Retry affected URLs after the crawl',
|
||||
],
|
||||
},
|
||||
508: {
|
||||
title: '508 — Loop Detected',
|
||||
severity: 'error',
|
||||
explanation: 'The server detected an infinite loop while processing the request (often WebDAV or a redirect loop server-side).',
|
||||
causes: ['Server-side redirect loop', 'Recursive include / symlink loop'],
|
||||
whatToDo: ['Server bug — nothing the crawler can do'],
|
||||
},
|
||||
509: {
|
||||
title: '509 — Bandwidth Limit Exceeded',
|
||||
severity: 'error',
|
||||
explanation: 'A non-standard code (cPanel/Apache) meaning the hosting account hit its monthly bandwidth cap.',
|
||||
causes: ['Shared hosting bandwidth quota exhausted'],
|
||||
whatToDo: ['Nothing the crawler can do — the host is throttling the whole account'],
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Build the diagnosis for a (statusCode, statusText, headers) triple.
|
||||
* `statusCode` may be null/0 for pre-response network failures, in
|
||||
@@ -357,7 +421,24 @@ export function diagnoseStatus(
|
||||
statusText: string | null,
|
||||
headers: HeaderList,
|
||||
indexability?: string | null,
|
||||
t?: DiagnosisTranslate,
|
||||
): StatusDiagnosis {
|
||||
const tr = (key: DiagnosisKey, vars: Record<string, string | number> = {}): string => {
|
||||
const en: string = DIAGNOSIS_EN[key];
|
||||
if (t) return t(`diag.${key}`, { defaultValue: en, ...vars });
|
||||
return en.replace(/\{\{(\w+)\}\}/g, (m, name: string) => (name in vars ? String(vars[name]) : m));
|
||||
};
|
||||
const list = (prefix: string, kind: 'cause' | 'todo', n: number): string[] =>
|
||||
Array.from({ length: n }, (_, i) => tr(`${prefix}.${kind}${i + 1}` as DiagnosisKey));
|
||||
|
||||
// Bot protection answered instead of the page: the row keeps no status
|
||||
// (it isn't the page's answer), but the code the challenge came with is
|
||||
// in the status text — "Cloudflare challenge (HTTP 403)". Diagnose that
|
||||
// code rather than calling it a network failure.
|
||||
if ((statusCode === null || statusCode === 0) && indexability === 'unknown:bot-blocked') {
|
||||
const code = /\(HTTP (\d{3})\)/.exec(statusText ?? '')?.[1];
|
||||
if (code) return diagnoseStatus(Number(code), statusText, headers, null, t);
|
||||
}
|
||||
const h = headerMap(headers);
|
||||
const front = detectFront(h);
|
||||
|
||||
@@ -365,25 +446,25 @@ export function diagnoseStatus(
|
||||
// useful on the error codes.
|
||||
const signals: string[] = [];
|
||||
if (h.has('cf-ray')) {
|
||||
signals.push(`Behind Cloudflare — cf-ray ${h.get('cf-ray')}. The status code describes the Cloudflare↔origin link, not what a browser sees.`);
|
||||
signals.push(tr('signal.cloudflare', { ray: h.get('cf-ray') ?? '' }));
|
||||
} else if (front && front !== 'nginx' && front !== 'Apache' && front !== 'IIS' && front !== 'LiteSpeed') {
|
||||
signals.push(`Fronted by ${front}.`);
|
||||
signals.push(tr('signal.fronted', { front }));
|
||||
} else if (front) {
|
||||
signals.push(`Origin server: ${front}${h.get('server') ? ` (${h.get('server')})` : ''}.`);
|
||||
signals.push(tr('signal.origin', { server: `${front}${h.get('server') ? ` (${h.get('server')})` : ''}` }));
|
||||
}
|
||||
const retryAfter = h.get('retry-after');
|
||||
if (retryAfter) {
|
||||
const secs = Number(retryAfter);
|
||||
const seconds = Number(retryAfter);
|
||||
signals.push(
|
||||
Number.isFinite(secs)
|
||||
? `Server sent Retry-After: ${secs} s — it is explicitly asking clients to back off for that long. A polite crawl should not re-request this URL sooner.`
|
||||
: `Server sent Retry-After: ${retryAfter}.`,
|
||||
Number.isFinite(seconds)
|
||||
? tr('signal.retryAfterSeconds', { seconds })
|
||||
: tr('signal.retryAfter', { value: retryAfter }),
|
||||
);
|
||||
}
|
||||
const cfCache = h.get('cf-cache-status');
|
||||
if (cfCache) signals.push(`Cloudflare cache status: ${cfCache}.`);
|
||||
if (cfCache) signals.push(tr('signal.cfCache', { status: cfCache }));
|
||||
const xRobots = h.get('x-robots-tag');
|
||||
if (xRobots) signals.push(`X-Robots-Tag: ${xRobots} — affects indexability regardless of the status.`);
|
||||
if (xRobots) signals.push(tr('signal.xRobots', { value: xRobots }));
|
||||
|
||||
// ----- Deliberately not requested (robots.txt) -----
|
||||
// Shares the "no status code" shape with a network failure, so without
|
||||
@@ -394,21 +475,11 @@ export function diagnoseStatus(
|
||||
indexability === 'non-indexable:robots-blocked'
|
||||
) {
|
||||
return {
|
||||
title: 'Not Requested — Disallowed by robots.txt',
|
||||
title: tr('robots.title'),
|
||||
severity: 'info',
|
||||
explanation:
|
||||
'The crawler never sent a request for this URL: a robots.txt rule for the ' +
|
||||
'configured user-agent disallows it. There is no status code because no ' +
|
||||
'response was ever asked for — this is not a connection failure.',
|
||||
causes: [
|
||||
'A Disallow rule in the site robots.txt matches this path for the crawl user-agent',
|
||||
'The URL is linked from crawled pages, so it is recorded and counted, but never fetched',
|
||||
],
|
||||
whatToDo: [
|
||||
'Nothing, if the exclusion is intended — Google will not crawl it either',
|
||||
'Check whether the page should be indexable: a disallowed URL cannot pass on link equity and its content is never seen',
|
||||
'Use Reports → Robots Tester to see which rule matches, or turn off "Respect robots.txt" in Settings → Crawler to audit it anyway',
|
||||
],
|
||||
explanation: tr('robots.explanation'),
|
||||
causes: list('robots', 'cause', 2),
|
||||
whatToDo: list('robots', 'todo', 3),
|
||||
signals,
|
||||
};
|
||||
}
|
||||
@@ -416,35 +487,24 @@ export function diagnoseStatus(
|
||||
// ----- Network error (no response) -----
|
||||
if (statusCode === null || statusCode === 0) {
|
||||
return {
|
||||
title: 'No HTTP Response — Network / Connection Error',
|
||||
title: tr('network.title'),
|
||||
severity: 'error',
|
||||
explanation:
|
||||
statusText && statusText.trim()
|
||||
? `The request never received an HTTP response. The crawler recorded: "${statusText}".`
|
||||
: 'The request never received an HTTP response — DNS lookup, TCP connection, TLS handshake, or the response read failed before any status line arrived.',
|
||||
causes: [
|
||||
'DNS could not resolve the hostname (typo, dead domain, or a name only resolvable from inside a private network)',
|
||||
'TCP connection refused or timed out (host down, firewall, wrong port)',
|
||||
'TLS handshake failed (expired/invalid certificate, protocol mismatch)',
|
||||
'Connection reset or read timed out mid-response',
|
||||
'Request aborted by the crawler because it exceeded Response Timeout (Settings → Crawler) or Max Response Time',
|
||||
],
|
||||
? tr('network.explanationDetail', { detail: statusText })
|
||||
: tr('network.explanation'),
|
||||
causes: list('network', 'cause', 5),
|
||||
signals,
|
||||
whatToDo: [
|
||||
'Check the hostname resolves and the site is reachable from a normal browser on this machine',
|
||||
'If it is reachable but slow, raise Response Timeout (Settings → Crawler)',
|
||||
'If it only fails under load, lower Max Threads / Max URL/s (Settings → Speed) — connection-table exhaustion on the origin looks like a network error to the client',
|
||||
'Retry the affected URLs after the crawl (transient resets clear on retry)',
|
||||
],
|
||||
whatToDo: list('network', 'todo', 4),
|
||||
};
|
||||
}
|
||||
|
||||
// ----- 1xx / 2xx / 3xx — not errors -----
|
||||
if (statusCode >= 100 && statusCode < 200) {
|
||||
return {
|
||||
title: `${statusCode} — Informational`,
|
||||
title: tr('informational.title', { code: statusCode }),
|
||||
severity: 'info',
|
||||
explanation: `A 1xx response (${statusText || 'Informational'}). These are interim responses; the crawler treats them as transient and waits for the final status.`,
|
||||
explanation: tr('informational.explanation', { text: statusText || tr('informational.fallback') }),
|
||||
causes: [],
|
||||
signals,
|
||||
whatToDo: [],
|
||||
@@ -452,9 +512,10 @@ export function diagnoseStatus(
|
||||
}
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return {
|
||||
title: `${statusCode} — ${statusText || 'Success'}`,
|
||||
// The reason phrase is the server's own words ("OK") and stays as sent.
|
||||
title: `${statusCode} — ${statusText || tr('success.fallback')}`,
|
||||
severity: 'ok',
|
||||
explanation: 'The request succeeded. No diagnosis needed — this section only has extra detail for error and redirect codes.',
|
||||
explanation: tr('success.explanation'),
|
||||
causes: [],
|
||||
signals,
|
||||
whatToDo: [],
|
||||
@@ -462,80 +523,64 @@ export function diagnoseStatus(
|
||||
}
|
||||
if (statusCode >= 300 && statusCode < 400) {
|
||||
const loc = h.get('location');
|
||||
const temporary = statusCode === 302 || statusCode === 307;
|
||||
return {
|
||||
title: `${statusCode} — ${statusText || 'Redirect'}`,
|
||||
title: `${statusCode} — ${statusText || tr('redirect.fallback')}`,
|
||||
severity: 'info',
|
||||
explanation:
|
||||
statusCode === 301
|
||||
? 'Permanent redirect. Search engines transfer ranking signals to the target and update their index to the new URL.'
|
||||
: statusCode === 302 || statusCode === 307
|
||||
? 'Temporary redirect. Search engines keep the original URL indexed. A 302 used long-term is a common SEO mistake — it should usually be a 301.'
|
||||
? tr('redirect.explanation301')
|
||||
: temporary
|
||||
? tr('redirect.explanation302')
|
||||
: statusCode === 308
|
||||
? 'Permanent redirect (like 301 but the request method is preserved).'
|
||||
? tr('redirect.explanation308')
|
||||
: statusCode === 304
|
||||
? 'Not Modified — the cached copy is still fresh. The crawler normally requests without conditional headers, so this is unusual.'
|
||||
: `A ${statusCode} redirect (${statusText || ''}).`,
|
||||
causes: loc ? [`Location header points to: ${loc}`] : ['No Location header was captured — unusual for a redirect.'],
|
||||
? tr('redirect.explanation304')
|
||||
: tr('redirect.explanationOther', { code: statusCode, text: statusText || '' }),
|
||||
causes: loc ? [tr('redirect.location', { location: loc })] : [tr('redirect.noLocation')],
|
||||
signals,
|
||||
whatToDo:
|
||||
statusCode === 302 || statusCode === 307
|
||||
? ['If this redirect is permanent, change it to a 301/308 so ranking signals consolidate', 'Check the Response Codes view to see the full chain']
|
||||
: ['Check the Response Codes view to see the full redirect chain and its final target'],
|
||||
whatToDo: temporary
|
||||
? [tr('redirect.todoPermanent'), tr('redirect.todoChain')]
|
||||
: [tr('redirect.todoChain')],
|
||||
};
|
||||
}
|
||||
|
||||
// ----- 4xx / 5xx — look up the knowledge base -----
|
||||
const isCfCode = statusCode >= 520 && statusCode <= 530;
|
||||
const info =
|
||||
(front === 'Cloudflare' && CLOUDFLARE_CODES[statusCode]) ||
|
||||
CLOUDFLARE_CODES[statusCode] || // 52x are Cloudflare-only even if we didn't detect the front (e.g. cf-ray stripped)
|
||||
GENERIC_CODES[statusCode] ||
|
||||
null;
|
||||
|
||||
const info = CODES[statusCode];
|
||||
if (info) {
|
||||
// For 530 specifically, if there's NO cf-ray we shouldn't claim Cloudflare.
|
||||
const adjusted: CodeInfo =
|
||||
isCfCode && front !== 'Cloudflare' && !h.has('cf-ray')
|
||||
? {
|
||||
...info,
|
||||
title: info.title.replace(' (Cloudflare)', ' (Cloudflare-style edge code)'),
|
||||
}
|
||||
: info;
|
||||
// 52x are Cloudflare-only, but without cf-ray (or a Cloudflare server
|
||||
// header) we shouldn't claim the response came from Cloudflare.
|
||||
const suffix = info.cloudflare
|
||||
? ` (${front === 'Cloudflare' || h.has('cf-ray') ? 'Cloudflare' : tr('cloudflareStyle')})`
|
||||
: '';
|
||||
const prefix = `code.${statusCode}`;
|
||||
return {
|
||||
title: adjusted.title,
|
||||
severity: adjusted.severity,
|
||||
explanation: adjusted.explanation,
|
||||
causes: adjusted.causes,
|
||||
title: `${statusCode} — ${tr(`${prefix}.name` as DiagnosisKey)}${suffix}`,
|
||||
severity: info.severity,
|
||||
explanation: tr(`${prefix}.explanation` as DiagnosisKey),
|
||||
causes: list(prefix, 'cause', info.causes),
|
||||
signals,
|
||||
whatToDo: adjusted.whatToDo,
|
||||
whatToDo: list(prefix, 'todo', info.todos),
|
||||
};
|
||||
}
|
||||
|
||||
// ----- 4xx / 5xx with no specific entry -----
|
||||
if (statusCode >= 400 && statusCode < 500) {
|
||||
return {
|
||||
title: `${statusCode} — ${statusText || 'Client Error'}`,
|
||||
title: `${statusCode} — ${statusText || tr('clientError.fallback')}`,
|
||||
severity: 'warning',
|
||||
explanation:
|
||||
'A 4xx code means the server rejected the request as the client\'s fault (bad request, not allowed, not found, etc.). In a crawl, persistent 4xx on linked pages usually means broken links; sporadic 4xx can be bot-protection.',
|
||||
causes: ['Broken or malformed link', 'Bot-protection / WAF rule', 'Method or content the server refuses'],
|
||||
explanation: tr('clientError.explanation'),
|
||||
causes: list('clientError', 'cause', 3),
|
||||
signals,
|
||||
whatToDo: [
|
||||
'Check the Inlinks tab to see what links to this URL',
|
||||
'If it looks like a block, try a browser-like User-Agent and a slower rate (Settings → Requests / Speed)',
|
||||
],
|
||||
whatToDo: list('clientError', 'todo', 2),
|
||||
};
|
||||
}
|
||||
return {
|
||||
title: `${statusCode} — ${statusText || 'Server Error'}`,
|
||||
title: `${statusCode} — ${statusText || tr('serverError.fallback')}`,
|
||||
severity: 'error',
|
||||
explanation:
|
||||
'A 5xx code means the server failed to fulfil a request it accepted as valid. Under crawl load these are frequently the crawl overwhelming the origin rather than a genuine page bug.',
|
||||
causes: ['Server-side error or crash', 'Origin overloaded — possibly by the crawl itself', 'Upstream/proxy failure'],
|
||||
explanation: tr('serverError.explanation'),
|
||||
causes: list('serverError', 'cause', 3),
|
||||
signals,
|
||||
whatToDo: [
|
||||
'Lower Max Threads / Max URL/s and add a Crawl Delay (Settings → Speed) if 5xx appear in bursts',
|
||||
'Bump Retry Attempts (Settings → Speed) and re-crawl affected URLs',
|
||||
],
|
||||
whatToDo: list('serverError', 'todo', 2),
|
||||
};
|
||||
}
|
||||
@@ -58,6 +58,28 @@ const FUNCTION_WORDS: Readonly<Record<string, readonly string[]>> = {
|
||||
uk: ['і', 'й', 'що', 'це', 'як', 'до', 'із', 'від', 'він', 'вона', 'але', 'його', 'її', 'був', 'були', 'тільки', 'або', 'ми', 'ви', 'вони', 'вже', 'є', 'якщо', 'та', 'також', 'дуже'],
|
||||
};
|
||||
|
||||
/**
|
||||
* Letters few other languages use, both cases. Text this full of the
|
||||
* declared language's own letters is written (at least partly) in it even
|
||||
* where its words are labels with no function words between them — found
|
||||
* live on a Turkish film page ("Fragmanı İzle · Yönetmen · Puanlanmadı ·
|
||||
* Orijinal Dili İngilizce") whose only sentences were an English synopsis.
|
||||
*/
|
||||
const DISTINCTIVE_LETTERS: Readonly<Record<string, string>> = {
|
||||
tr: 'ğĞıİşŞ',
|
||||
az: 'əƏğĞıİşŞ',
|
||||
es: 'ñÑ¿¡',
|
||||
pt: 'ãÃõÕ',
|
||||
ro: 'ăĂîÎșȘțȚşŞţŢ',
|
||||
pl: 'ąĄęĘłŁńŃśŚźŹżŻ',
|
||||
hu: 'őŐűŰ',
|
||||
de: 'ßäÄöÖüÜ',
|
||||
sv: 'åÅäÄöÖ',
|
||||
fi: 'äÄöÖ',
|
||||
};
|
||||
/** The declared language's own letters at this share of all letters = it is present. */
|
||||
const DECLARED_LETTERS_PRESENT_RATIO = 0.01;
|
||||
|
||||
/** word → the single language it is distinctive for (shared words omitted). */
|
||||
const DISTINCTIVE: ReadonlyMap<string, string> = (() => {
|
||||
const owners = new Map<string, Set<string>>();
|
||||
@@ -256,6 +278,14 @@ export function assessContentLanguage(text: string, declaredLang: string | null)
|
||||
return { detected, mismatch: false };
|
||||
}
|
||||
const declaredRatio = ratios.get(declared) ?? 0;
|
||||
const mismatch = declaredRatio < DECLARED_ABSENT_RATIO;
|
||||
return { detected, mismatch };
|
||||
if (declaredRatio >= DECLARED_ABSENT_RATIO) return { detected, mismatch: false };
|
||||
// Few of its function words — but labels have none. If the declared
|
||||
// language's own letters are all over the text, it is there.
|
||||
const own = DISTINCTIVE_LETTERS[declared];
|
||||
if (own) {
|
||||
let n = 0;
|
||||
for (const ch of sample) if (own.includes(ch)) n++;
|
||||
if (n / letters >= DECLARED_LETTERS_PRESENT_RATIO) return { detected, mismatch: false };
|
||||
}
|
||||
return { detected, mismatch: true };
|
||||
}
|
||||
@@ -240,6 +240,34 @@ export function xRobotsTagDirectives(
|
||||
return { noindex, nofollow };
|
||||
}
|
||||
|
||||
/**
|
||||
* Name the bot protection (or rate limit) that answered instead of the
|
||||
* site, or null. Only explicit signals count, so no real error page is
|
||||
* ever re-labelled: Cloudflare marks its challenges `cf-mitigated:
|
||||
* challenge`, Vercel `x-vercel-mitigated`, AWS WAF `x-amzn-waf-action`,
|
||||
* Sucuri `x-sucuri-block`, DataDome stamps `x-datadome` on what it blocks;
|
||||
* and a 429 is "too many requests" on any server. None of them says the
|
||||
* page is broken — counting them as 4xx filled Broken Links and Non-200
|
||||
* in Sitemap with pages that work (a live crawl of a Cloudflare site
|
||||
* reported 266 pages with broken links, all challenge pages).
|
||||
*/
|
||||
export function detectBotProtection(
|
||||
status: number,
|
||||
headers: { get(name: string): string | null },
|
||||
): string | null {
|
||||
if (status < 400) return null;
|
||||
const cf = headers.get('cf-mitigated');
|
||||
if (cf) return `Cloudflare ${cf.trim().toLowerCase()} (HTTP ${status})`;
|
||||
const vercel = headers.get('x-vercel-mitigated');
|
||||
if (vercel) return `Vercel ${vercel.trim().toLowerCase()} (HTTP ${status})`;
|
||||
const aws = headers.get('x-amzn-waf-action');
|
||||
if (aws) return `AWS WAF ${aws.trim().toLowerCase()} (HTTP ${status})`;
|
||||
if (headers.get('x-sucuri-block')) return `Sucuri block (HTTP ${status})`;
|
||||
if (status === 403 && headers.get('x-datadome')) return `DataDome block (HTTP ${status})`;
|
||||
if (status === 429) return `Rate limited (HTTP ${status})`;
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Back-compat convenience wrapper. */
|
||||
export function xRobotsTagNoindex(header: string, uaToken: string): boolean {
|
||||
return xRobotsTagDirectives(header, uaToken).noindex;
|
||||
@@ -673,6 +701,8 @@ export class Crawler extends EventEmitter {
|
||||
} | null;
|
||||
/** Client-side routes the SPA reached during render (Bölüm 29). */
|
||||
spaRoutes?: string[];
|
||||
/** The document was replaced during the render (meta refresh / JS redirect) — the URL it ended on. */
|
||||
navigatedTo?: string | null;
|
||||
}>;
|
||||
|
||||
constructor(
|
||||
@@ -976,6 +1006,12 @@ export class Crawler extends EventEmitter {
|
||||
>();
|
||||
private calendarTraps = new Set<string>();
|
||||
private calendarTrapDropped = 0;
|
||||
/**
|
||||
* URLs whose latest answer came from bot protection / a rate limit (see
|
||||
* `detectBotProtection`). Kept per URL so the end-of-crawl retry neither
|
||||
* counts a URL as failed twice nor leaves it failed once it got through.
|
||||
*/
|
||||
private botBlockedUrls = new Set<string>();
|
||||
|
||||
private isCalendarTrapped(url: string): boolean {
|
||||
if (this.calendarTraps.size === 0) return false;
|
||||
@@ -2874,6 +2910,7 @@ export class Crawler extends EventEmitter {
|
||||
}
|
||||
|
||||
// If the start URL isn't in the DB yet, kick off a brand-new crawl from it.
|
||||
let seededStartUrl: string | null = null;
|
||||
if (!this.db.hasUrl(this.config.startUrl)) {
|
||||
// Stub upsert before enqueue so the seed surfaces in the URL
|
||||
// table within ~100 ms of clicking Start instead of waiting for
|
||||
@@ -2896,6 +2933,7 @@ export class Crawler extends EventEmitter {
|
||||
/* non-fatal: fetchAndProcess will still write the real row */
|
||||
}
|
||||
this.enqueue({ url: this.config.startUrl, depth: 0 });
|
||||
seededStartUrl = this.config.startUrl;
|
||||
}
|
||||
|
||||
// Re-queue any internal link targets that were discovered before the
|
||||
@@ -2912,6 +2950,11 @@ export class Crawler extends EventEmitter {
|
||||
[{ excludeNofollow }],
|
||||
);
|
||||
for (const pending of pendingLinks) {
|
||||
// The start-URL stub written above has no status code yet, so the
|
||||
// query's re-crawl half returns it as unfinished work. Re-queueing
|
||||
// it fetched the start page twice on every new crawl and counted it
|
||||
// twice in "Crawled" (3 crawled for a 2-page site).
|
||||
if (pending.url === seededStartUrl) continue;
|
||||
// Drop from `seen` so enqueue accepts it — these URLs are genuinely
|
||||
// unfinished work.
|
||||
this.seen.delete(pending.url);
|
||||
@@ -3024,11 +3067,16 @@ export class Crawler extends EventEmitter {
|
||||
// 1) Cheap HEAD with the configured UA.
|
||||
let { res, err } = await attempt('HEAD', cfgUa);
|
||||
|
||||
// 2) Retry with GET (browser UA) when HEAD is unreliable or threw a
|
||||
// recoverable error. The browser UA slips past UA-based bot walls; the
|
||||
// GET reveals the status a real visitor would get.
|
||||
const headBlocked =
|
||||
!!res && (res.status >= 500 || HEAD_UNRELIABLE_STATUSES.has(res.status));
|
||||
// 2) Confirm any HEAD error with a GET before calling the link broken.
|
||||
// Servers that only route GET answer HEAD with 404 (bsky.app profile
|
||||
// links: HEAD 404, GET 200 — found live as 191 "broken" pages), and
|
||||
// bot walls treat the two differently. The configured UA goes first:
|
||||
// a site that serves an honest crawler can still challenge the dated
|
||||
// browser UA below (seen live on a Cloudflare news site). The browser
|
||||
// UA is the second try, for walls that block bot UAs outright. The
|
||||
// best answer wins — a working page, else a bot-protection answer
|
||||
// (unknown, not broken), else the error itself.
|
||||
const headBlocked = !!res && res.status >= 400;
|
||||
const headRecoverableThrow = !res && !isHardTransportError(err);
|
||||
if (headBlocked || headRecoverableThrow) {
|
||||
try {
|
||||
@@ -3036,7 +3084,33 @@ export class Crawler extends EventEmitter {
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
const got = await attempt('GET', BROWSER_FALLBACK_UA);
|
||||
const rank = (r: Awaited<ReturnType<typeof undiciFetch>>): number =>
|
||||
r.status < 400 ? 0 : detectBotProtection(r.status, r.headers) ? 1 : 2;
|
||||
let got = await attempt('GET', cfgUa);
|
||||
if (
|
||||
got.res &&
|
||||
got.res.status >= 400 &&
|
||||
(got.res.status >= 500 || HEAD_UNRELIABLE_STATUSES.has(got.res.status)) &&
|
||||
cfgUa !== BROWSER_FALLBACK_UA
|
||||
) {
|
||||
const alt = await attempt('GET', BROWSER_FALLBACK_UA);
|
||||
if (alt.res && rank(alt.res) < rank(got.res)) {
|
||||
try {
|
||||
await got.res.body?.cancel();
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
got = alt;
|
||||
} else {
|
||||
try {
|
||||
await alt.res?.body?.cancel();
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
} else if (!got.res && !isHardTransportError(got.err)) {
|
||||
got = await attempt('GET', BROWSER_FALLBACK_UA);
|
||||
}
|
||||
if (got.res) {
|
||||
res = got.res;
|
||||
err = undefined;
|
||||
@@ -3074,11 +3148,13 @@ export class Crawler extends EventEmitter {
|
||||
const loc = res.headers.get('location');
|
||||
if (loc) redirectTarget = normalizeUrl(loc, url) ?? loc;
|
||||
}
|
||||
const botBlock = detectBotProtection(res.status, res.headers);
|
||||
await this.dbCall<void>('updateExternalProbe', [
|
||||
url,
|
||||
{
|
||||
statusCode: res.status,
|
||||
statusText: res.statusText || null,
|
||||
statusText: botBlock ?? (res.statusText || null),
|
||||
botBlocked: botBlock,
|
||||
contentType: res.headers.get('content-type'),
|
||||
contentLength: parseIntSafe(res.headers.get('content-length')),
|
||||
responseTimeMs,
|
||||
@@ -3883,6 +3959,9 @@ export class Crawler extends EventEmitter {
|
||||
// are bounded by ~concurrency / maxRps, so Stop stays responsive.
|
||||
await this.acquireRateSlot();
|
||||
if (this.stopped) return;
|
||||
// A retry of a URL bot protection blocked earlier: it is failed again
|
||||
// only if it is blocked again.
|
||||
if (this.botBlockedUrls.delete(item.url)) this.failed--;
|
||||
|
||||
const t0 = Date.now();
|
||||
const controller = new AbortController();
|
||||
@@ -3994,6 +4073,39 @@ export class Crawler extends EventEmitter {
|
||||
this.noteCleanResponse();
|
||||
}
|
||||
}
|
||||
// Bot protection answered instead of the page. Recorded as "not
|
||||
// crawled" (NULL status, unknown indexability) rather than as the
|
||||
// page's 4xx — see `detectBotProtection`. The URL stays pending, so
|
||||
// the end-of-crawl drain asks for it once more (such blocks often
|
||||
// lift after a pause), and a later Start retries it again.
|
||||
const botBlock = detectBotProtection(statusCode, res.headers);
|
||||
if (botBlock) {
|
||||
try {
|
||||
await res.body?.cancel();
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
if (this.config.adaptiveSpeed && statusCode !== 429 && statusCode !== 503) {
|
||||
this.noteRateLimited(null);
|
||||
}
|
||||
this.failed++;
|
||||
this.botBlockedUrls.add(item.url);
|
||||
await this.dbCall<number>('upsertUrl', [
|
||||
{
|
||||
url: item.url,
|
||||
urlMalformed: isUrlMalformed(item.url) ? 1 : 0,
|
||||
urlTrap: this.trapKind(item.url),
|
||||
contentKind: 'html',
|
||||
statusCode: null,
|
||||
statusText: botBlock,
|
||||
indexability: 'unknown:bot-blocked',
|
||||
indexabilityReason: `Blocked by bot protection — ${botBlock}`,
|
||||
responseTimeMs,
|
||||
depth: item.depth,
|
||||
},
|
||||
]);
|
||||
return;
|
||||
}
|
||||
const contentLengthHeader = res.headers.get('content-length');
|
||||
const xRobotsTag = res.headers.get('x-robots-tag');
|
||||
// Security / performance headers — captured per URL for the Security
|
||||
@@ -4406,7 +4518,18 @@ export class Crawler extends EventEmitter {
|
||||
controller.signal,
|
||||
);
|
||||
renderMs = renderRes.timingMs;
|
||||
if (renderRes.ok && renderRes.html) {
|
||||
if (renderRes.navigatedTo) {
|
||||
// The page replaced itself while rendering (meta refresh, JS
|
||||
// redirect): the DOM, screenshots and audits are the
|
||||
// destination's. Recording them under this URL gave it the
|
||||
// destination's title / H1 / content — a "duplicate" of the
|
||||
// target and a meta refresh nobody saw. Keep the served HTML,
|
||||
// which still carries the refresh / redirect the checks read.
|
||||
this.emit(
|
||||
'debug',
|
||||
`JS render of ${item.url} ended on ${renderRes.navigatedTo} — using the served HTML`,
|
||||
);
|
||||
} else if (renderRes.ok && renderRes.html) {
|
||||
parseBody = renderRes.html;
|
||||
renderedHtml = renderRes.html;
|
||||
} else if (renderRes.error) {
|
||||
@@ -4415,11 +4538,13 @@ export class Crawler extends EventEmitter {
|
||||
`JS render failed for ${item.url}: ${renderRes.error} (falling back to static HTML)`,
|
||||
);
|
||||
}
|
||||
if (!renderRes.navigatedTo) {
|
||||
if (renderRes.screenshots) renderScreenshots = renderRes.screenshots;
|
||||
if (renderRes.spaRoutes?.length) renderSpaRoutes = renderRes.spaRoutes;
|
||||
if (renderRes.lcp) renderLcp = renderRes.lcp;
|
||||
if (renderRes.mobileUsability) renderMobile = renderRes.mobileUsability;
|
||||
if (renderRes.a11y) renderA11y = renderRes.a11y;
|
||||
}
|
||||
} catch (err) {
|
||||
this.emit(
|
||||
'warn',
|
||||
|
||||
@@ -497,6 +497,9 @@ const JS_SCRIPT_TYPES = new Set([
|
||||
'application/ecmascript',
|
||||
]);
|
||||
|
||||
/** A Cloudflare-obfuscated `mailto:` (see the link loop in `parseHtml`). */
|
||||
const CF_EMAIL_PROTECTION_RE = /^https?:\/\/[^/]+\/cdn-cgi\/l\/email-protection(?:[?#]|$)/i;
|
||||
|
||||
/** An `onclick` body that navigates somewhere rather than acting in place. */
|
||||
const JS_NAVIGATION_RE =
|
||||
/(?:^|[^\w$.])(?:window\.|document\.|top\.|self\.)?location(?:\.href)?\s*=(?!=)|location\.(?:assign|replace)\s*\(|window\.open\s*\(/;
|
||||
@@ -541,9 +544,14 @@ function findInlineScriptHeadWrites($: cheerio.CheerioAPI): InlineScriptHeadWrit
|
||||
const m = JS_TITLE_ASSIGN_RE.exec(code);
|
||||
if (m) {
|
||||
out.titleWrite = true;
|
||||
// `document.title = " Rendering test 1 - " + num` — the literal is
|
||||
// only the start of an expression, so it isn't the title; showing
|
||||
// it in the JS Title column would invent one. Known only when the
|
||||
// string is the whole right-hand side.
|
||||
const literal = m[2];
|
||||
const continued = /^\s*(?:[-+*%?.|&[(]|\/(?![/*]))/.test(code.slice(m.index + m[0].length));
|
||||
out.title =
|
||||
literal !== undefined
|
||||
literal !== undefined && !continued
|
||||
? decodeEntities(literal.replace(/\\(.)/g, '$1')).trim() || null
|
||||
: null;
|
||||
} else if (JS_TITLE_ELEMENT_RE.test(code)) {
|
||||
@@ -784,7 +792,10 @@ export function parseHtml(
|
||||
// We parse with decodeEntities:false for speed, so extracted strings
|
||||
// contain raw entities like `'` or `&`. Decode them before
|
||||
// storing so UI / CSV export / search see human-readable text.
|
||||
const titleEls = $('title');
|
||||
// `<title>` inside an inline `<svg>` is the icon's accessible name, not
|
||||
// a document title — counting them reported "Multiple <title> Tags" on
|
||||
// every page of a site with titled SVG icons (found live: 77 pages).
|
||||
const titleEls = $('title').filter((_, el) => $(el).closest('svg').length === 0);
|
||||
const titleCount = titleEls.length;
|
||||
const title = decodeEntities(titleEls.first().text().trim()) || null;
|
||||
const metaDescription =
|
||||
@@ -1585,6 +1596,12 @@ export function parseHtml(
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Cloudflare's Email Address Obfuscation rewrites every `mailto:` into
|
||||
// `/cdn-cgi/l/email-protection#<hex>` and its script turns it back in
|
||||
// the browser. To a crawler that is a 404 no visitor ever reaches (found
|
||||
// live as the one "broken internal link" on a Cloudflare site) — it is a
|
||||
// mailto link, and is left out like one.
|
||||
if (CF_EMAIL_PROTECTION_RE.test(normalized)) return;
|
||||
const $el = $(el);
|
||||
const rel = ($el.attr('rel') ?? '').trim().toLowerCase() || null;
|
||||
const target = ($el.attr('target') ?? '').trim() || null;
|
||||
@@ -2428,8 +2445,9 @@ const SCHEMA_REQUIRED_PROPS: Record<
|
||||
* Walk a parsed JSON-LD payload and accumulate four structured-data
|
||||
* health signals into the caller's tallies / map:
|
||||
*
|
||||
* - **Duplicate `@id`** — populated by recording every `@id` value
|
||||
* into `idCounts`. The caller computes the "duplicates" count as
|
||||
* - **Duplicate `@id`** — populated by recording the `@id` of every
|
||||
* node that defines an entity (has properties of its own — a bare
|
||||
* `{"@id": …}` reference doesn't count) into `idCounts`. The caller computes the "duplicates" count as
|
||||
* `Σ(count > 1 ? count - 1 : 0)` after walking every block on the
|
||||
* page. We do this in the caller (not here) so a `@id` shared
|
||||
* across multiple `<script>` tags is still detected.
|
||||
@@ -2479,9 +2497,19 @@ function walkSchemaForValidation(
|
||||
if (typeof n !== 'object') return;
|
||||
const obj = n as Record<string, unknown>;
|
||||
|
||||
// Record `@id` for cross-block duplicate detection.
|
||||
// Record `@id` for cross-block duplicate detection — definitions only.
|
||||
// `{"@id": "#org"}` (optionally with `@type`) is a *reference* to a
|
||||
// node defined elsewhere: the linking pattern Schema.org recommends
|
||||
// and Yoast / Rank Math emit on every page (`publisher`, `isPartOf`,
|
||||
// `about` → `#org`, `#website`). Counting references flagged nearly
|
||||
// every well-built graph as "Duplicate @id"; only a node that carries
|
||||
// its own properties defines the entity.
|
||||
const idVal = obj['@id'];
|
||||
if (typeof idVal === 'string' && idVal) {
|
||||
if (
|
||||
typeof idVal === 'string' &&
|
||||
idVal &&
|
||||
Object.keys(obj).some((k) => k !== '@id' && k !== '@type' && k !== '@context')
|
||||
) {
|
||||
idCounts.set(idVal, (idCounts.get(idVal) ?? 0) + 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -314,12 +314,23 @@ function accentStyle(options: HtmlReportOptions): string {
|
||||
|
||||
/** Site-wide findings that belong to no single URL (shown above the issue table). */
|
||||
function renderSiteNotices(counts: OverviewCounts): string {
|
||||
let out = '';
|
||||
const blocked = counts.site?.botBlocked;
|
||||
if (blocked) {
|
||||
out +=
|
||||
`<p><span class="sev-warn">WARN</span> Bot protection blocked ${fmtNum(blocked.count)} ` +
|
||||
`URL${blocked.count === 1 ? '' : 's'}` +
|
||||
(blocked.sample ? ` (<span class="mono">${escape(blocked.sample)}</span>)` : '') +
|
||||
`: they were not crawled, so this report has no data on them and leaves them out of the ` +
|
||||
`broken-link and sitemap checks. Lower the crawl speed or allowlist the crawler, then re-crawl.</p>`;
|
||||
}
|
||||
const probe = counts.site?.soft404Probe;
|
||||
if (!probe) return '';
|
||||
if (!probe) return out;
|
||||
const via = probe.finalUrl
|
||||
? ` after redirecting to <span class="mono">${escape(probe.finalUrl)}</span>`
|
||||
: '';
|
||||
return (
|
||||
out +
|
||||
`<p><span class="sev-warn">WARN</span> Missing pages return ${probe.status}: ` +
|
||||
`<span class="mono">${escape(probe.url)}</span>, a page that does not exist, was answered ` +
|
||||
`${probe.status}${via} instead of 404. Broken links on this site look like working pages ` +
|
||||
|
||||
@@ -71,6 +71,14 @@ export interface RenderResult {
|
||||
* `discoverSpaRoutes` was set.
|
||||
*/
|
||||
spaRoutes?: string[];
|
||||
/**
|
||||
* Set when the document itself was replaced during the render — a meta
|
||||
* refresh or `location.href = …` fired while we waited — to the URL the
|
||||
* browser ended on. `html` then belongs to that URL, not to `url`.
|
||||
* Client-side routing (`pushState`, hash routes) keeps the document and
|
||||
* never sets this.
|
||||
*/
|
||||
navigatedTo?: string | null;
|
||||
}
|
||||
|
||||
/** V2 Faz 16 — in-page accessibility audit result. */
|
||||
@@ -449,6 +457,19 @@ export async function renderUrl(
|
||||
const html = await page.content();
|
||||
const headers = response ? await response.allHeaders() : {};
|
||||
const finalUrl = response?.url() ?? page.url();
|
||||
// The current document's own navigation entry names the URL it was
|
||||
// loaded from. History-API routing leaves it alone; a meta refresh or
|
||||
// a `location` assignment that ran during the wait replaces it.
|
||||
let navigatedTo: string | null = null;
|
||||
try {
|
||||
const docUrl = (await page.evaluate(
|
||||
"(() => { const e = performance.getEntriesByType('navigation')[0]; return e ? e.name : null; })()",
|
||||
)) as string | null;
|
||||
const strip = (u: string): string => u.split('#')[0] ?? u;
|
||||
if (docUrl && strip(docUrl) !== strip(finalUrl)) navigatedTo = docUrl;
|
||||
} catch {
|
||||
/* page went away mid-check — treat as not navigated */
|
||||
}
|
||||
|
||||
// LCP candidate detection — runs before screenshot so the in-page
|
||||
// layout pass it triggers is included in the captured viewport.
|
||||
@@ -529,6 +550,7 @@ export async function renderUrl(
|
||||
lcp: opts.detectLcp ? lcp : undefined,
|
||||
a11y: opts.detectA11y ? a11y : undefined,
|
||||
spaRoutes,
|
||||
navigatedTo,
|
||||
};
|
||||
} catch (err) {
|
||||
result = {
|
||||
|
||||
@@ -2046,6 +2046,8 @@ export class ProjectDb {
|
||||
contentLength?: number | null;
|
||||
responseTimeMs?: number | null;
|
||||
redirectTarget?: string | null;
|
||||
/** Bot protection answered instead of the page — e.g. "Cloudflare challenge (HTTP 403)". */
|
||||
botBlocked?: string | null;
|
||||
},
|
||||
): void {
|
||||
// Re-classify the external stub's content_kind from the probed
|
||||
@@ -2058,10 +2060,16 @@ export class ProjectDb {
|
||||
// with a placeholder 'indexable'; without recomputing here, a link that
|
||||
// 404s, is bot-blocked, or never responded would stay reported as
|
||||
// "indexable" (misleading, and inflates the Indexable count).
|
||||
const status = patch.statusCode;
|
||||
const status = patch.botBlocked ? null : patch.statusCode;
|
||||
let indexability: string;
|
||||
let indexabilityReason: string | null;
|
||||
if (status === null) {
|
||||
if (patch.botBlocked) {
|
||||
// A challenge page is the CDN's answer, not the link target's — it
|
||||
// says nothing about whether the link works, so it is no "broken
|
||||
// external link".
|
||||
indexability = 'unknown:bot-blocked';
|
||||
indexabilityReason = `Blocked by bot protection — ${patch.botBlocked}`;
|
||||
} else if (status === null) {
|
||||
indexability = 'non-indexable:client-error';
|
||||
indexabilityReason = 'No response';
|
||||
} else if (status >= 500) {
|
||||
@@ -2093,7 +2101,7 @@ export class ProjectDb {
|
||||
)
|
||||
.run({
|
||||
url,
|
||||
status_code: patch.statusCode,
|
||||
status_code: status,
|
||||
status_text: patch.statusText ?? null,
|
||||
content_type: patch.contentType ?? null,
|
||||
content_kind: contentKind,
|
||||
@@ -3927,18 +3935,32 @@ export class ProjectDb {
|
||||
for (const e of entries) {
|
||||
if (!e.langValid) invalidCount++;
|
||||
if (e.href === r.url) selfRef = true;
|
||||
const meta = targetMeta.get(e.href);
|
||||
// Bot protection answered for the target: nothing is known about
|
||||
// it, so it is neither a broken alternate nor a missing return link.
|
||||
if (meta?.indexability === 'unknown:bot-blocked') continue;
|
||||
// x-default is meant for a language selector or an auto-redirecting
|
||||
// home page (Google's own example). A 3xx there is the design, and a
|
||||
// redirect carries no hreflang back — found live as the one "issue"
|
||||
// on all 58 locale home pages of a large multilingual site.
|
||||
const xDefaultRedirect =
|
||||
e.lang.toLowerCase() === 'x-default' &&
|
||||
meta !== undefined &&
|
||||
meta.status !== null &&
|
||||
meta.status >= 300 &&
|
||||
meta.status < 400;
|
||||
// Reciprocity — only score targets we actually crawled. Targets
|
||||
// outside the crawl scope can't be checked, and counting them
|
||||
// as missing would punish partial / scoped crawls.
|
||||
if (targetMeta.has(e.href) && e.href !== r.url) {
|
||||
if (meta && e.href !== r.url && !xDefaultRedirect) {
|
||||
const back = declaredBy.get(r.url);
|
||||
if (!back || !back.has(e.href)) reciprocityMissing++;
|
||||
}
|
||||
// Target issues: non-200, noindex, or canonicalised away.
|
||||
const meta = targetMeta.get(e.href);
|
||||
if (meta) {
|
||||
const badStatus =
|
||||
meta.status === null || meta.status < 200 || meta.status >= 300;
|
||||
!xDefaultRedirect &&
|
||||
(meta.status === null || meta.status < 200 || meta.status >= 300);
|
||||
const isNoindex = meta.indexability === 'non-indexable:noindex';
|
||||
const isCanonAway =
|
||||
meta.canonical !== null &&
|
||||
@@ -6421,6 +6443,7 @@ export class ProjectDb {
|
||||
const totalNonIndexable = countCategory('indexability:non-indexable');
|
||||
// Probe-only rows (share-card images, `occurrences = 0`) aren't on any page.
|
||||
const totalImages = this.scalarCount('SELECT COUNT(*) AS c FROM images WHERE occurrences > 0');
|
||||
const botBlocked = countCategory('status:bot-blocked');
|
||||
|
||||
return {
|
||||
summary: {
|
||||
@@ -6451,6 +6474,7 @@ export class ProjectDb {
|
||||
responseCodes: {
|
||||
all: countCategory('all'),
|
||||
blockedRobots: countCategory('status:blocked-robots'),
|
||||
botBlocked,
|
||||
noResponse: countCategory('status:no-response'),
|
||||
success2xx: countCategory('status:2xx'),
|
||||
redirect3xx: countCategory('status:3xx'),
|
||||
@@ -6469,10 +6493,25 @@ export class ProjectDb {
|
||||
blockedRobots: countCategory('indexability:blocked-robots'),
|
||||
},
|
||||
issues: this.silenceDisabledIssues(this.getIssuesCounts()),
|
||||
site: { soft404Probe: this.readSoft404Probe() },
|
||||
site: {
|
||||
soft404Probe: this.readSoft404Probe(),
|
||||
botBlocked: botBlocked > 0 ? { count: botBlocked, sample: this.botBlockedSample() } : null,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** What answered for one bot-blocked URL — e.g. "Cloudflare challenge (HTTP 403)". */
|
||||
private botBlockedSample(): string | null {
|
||||
if (this.overviewCollect) return null;
|
||||
const row = this.db
|
||||
.prepare(
|
||||
`SELECT status_text AS s FROM urls
|
||||
WHERE indexability = 'unknown:bot-blocked' AND status_text IS NOT NULL LIMIT 1`,
|
||||
)
|
||||
.get() as { s: string } | undefined;
|
||||
return row?.s ?? null;
|
||||
}
|
||||
|
||||
private readSoft404Probe(): Soft404ProbeResult | null {
|
||||
const raw = this.getMeta(SOFT404_PROBE_META_KEY);
|
||||
if (!raw) return null;
|
||||
@@ -10679,14 +10718,22 @@ export function categoryWhereClause(
|
||||
return "is_external = 1 AND content_kind != 'html'";
|
||||
case 'status:blocked-robots':
|
||||
return "indexability = 'non-indexable:robots-blocked'";
|
||||
case 'status:bot-blocked':
|
||||
// Bot protection (Cloudflare challenge, WAF block) or a rate limit
|
||||
// answered instead of the page. Stored with a NULL status — it is
|
||||
// the CDN's answer, not the page's — so no 4xx check (broken links,
|
||||
// Non-200 in Sitemap, canonical targets…) reports a page that may
|
||||
// be perfectly fine.
|
||||
return "indexability = 'unknown:bot-blocked'";
|
||||
case 'status:no-response':
|
||||
// Robots-blocked URLs are recorded with a NULL status because the
|
||||
// crawler deliberately never requested them — they are not pages
|
||||
// that failed to answer. Counting them here made "No Response"
|
||||
// read as N failures on a crawl whose failure count was zero, and
|
||||
// double-counted them against the sibling Blocked by Robots row.
|
||||
// Bot-blocked URLs did answer — just not with the page.
|
||||
return `status_code IS NULL
|
||||
AND indexability != 'non-indexable:robots-blocked'`;
|
||||
AND indexability NOT IN ('non-indexable:robots-blocked', 'unknown:bot-blocked')`;
|
||||
case 'status:2xx':
|
||||
return 'status_code >= 200 AND status_code < 300';
|
||||
case 'status:3xx':
|
||||
|
||||
@@ -40,6 +40,7 @@ export type UrlCategory =
|
||||
| 'external:html'
|
||||
| 'external:other'
|
||||
| 'status:blocked-robots'
|
||||
| 'status:bot-blocked'
|
||||
| 'status:no-response'
|
||||
| 'status:2xx'
|
||||
| 'status:3xx'
|
||||
@@ -261,7 +262,11 @@ export type Indexability =
|
||||
| 'non-indexable:robots-blocked'
|
||||
| 'non-indexable:redirect'
|
||||
| 'non-indexable:client-error'
|
||||
| 'non-indexable:server-error';
|
||||
| 'non-indexable:server-error'
|
||||
// Bot protection (Cloudflare challenge, WAF block) or a rate limit
|
||||
// answered instead of the page. Not "non-indexable": search engines are
|
||||
// usually let through, the crawler wasn't — indexability is unknown.
|
||||
| 'unknown:bot-blocked';
|
||||
|
||||
export type ContentKind = 'html' | 'css' | 'js' | 'image' | 'pdf' | 'font' | 'other';
|
||||
|
||||
@@ -1795,7 +1800,11 @@ export interface Soft404ProbeResult {
|
||||
|
||||
export interface OverviewCounts {
|
||||
/** Findings about the site as a whole rather than any one URL. */
|
||||
site?: { soft404Probe: Soft404ProbeResult | null };
|
||||
site?: {
|
||||
soft404Probe: Soft404ProbeResult | null;
|
||||
/** URLs bot protection kept the crawler from, with one example of what answered. */
|
||||
botBlocked?: { count: number; sample: string | null } | null;
|
||||
};
|
||||
summary: {
|
||||
totalInternalUrls: number;
|
||||
totalIndexable: number;
|
||||
@@ -1810,6 +1819,8 @@ export interface OverviewCounts {
|
||||
responseCodes: {
|
||||
all: number;
|
||||
blockedRobots: number;
|
||||
/** Answered by bot protection / a rate limit instead of the page — not crawled. */
|
||||
botBlocked: number;
|
||||
noResponse: number;
|
||||
success2xx: number;
|
||||
redirect3xx: number;
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Generated by scripts/generate-hreflang-codes.mjs from ICU 77.1 (Node v24.11.0). Do not edit by hand — re-run the script.
|
||||
|
||||
/** Two-letter (ISO 639-1) language subtags. */
|
||||
export const LANGUAGES_2 = 'aa ab ae af ak am an ar as av ay az ba be bg bh bi bm bn bo br bs ca ce ch co cr cs cu cv cy da de dv dz ee el en eo es et eu fa ff fi fj fo fr fy ga gd gl gn gu gv ha he hi ho hr ht hu hy hz ia id ie ig ii ik in io is it iu iw ja ji jv jw ka kg ki kj kk kl km kn ko kr ks ku kv kw ky la lb lg li ln lo lt lu lv mg mh mi mk ml mn mo mr ms mt my na nb nd ne ng nl nn no nr nv ny oc oj om or os pa pi pl ps pt qu rm rn ro ru rw sa sc sd se sg sh si sk sl sm sn so sq sr ss st su sv sw ta te tg th ti tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa wo xh yi yo za zh zu';
|
||||
|
||||
/** ISO 15924 script subtags. */
|
||||
export const SCRIPTS = 'Adlm Afak Aghb Ahom Arab Aran Armi Armn Avst Bali Bamu Bass Batk Beng Bhks Blis Bopo Brah Brai Bugi Buhd Cakm Cans Cari Cham Cher Chrs Cirt Copt Cpmn Cprt Cyrl Cyrs Deva Diak Dogr Dsrt Dupl Egyd Egyh Egyp Elba Elym Ethi Gara Geok Geor Glag Gong Gonm Goth Gran Grek Gujr Gukh Guru Hanb Hang Hani Hano Hans Hant Hatr Hebr Hira Hluw Hmng Hmnp Hrkt Hung Inds Ital Jamo Java Jpan Jurc Kali Kana Kawi Khar Khmr Khoj Kits Knda Kore Kpel Krai Kthi Lana Laoo Latf Latg Latn Lepc Limb Lina Linb Lisu Loma Lyci Lydi Mahj Maka Mand Mani Marc Maya Medf Mend Merc Mero Mlym Modi Mong Moon Mroo Mtei Mult Mymr Nagm Nand Narb Nbat Newa Nkgb Nkoo Nshu Ogam Olck Onao Orkh Orya Osge Osma Ougr Palm Pauc Perm Phag Phli Phlp Phlv Phnx Plrd Prti Qaag Rjng Rohg Roro Runr Samr Sara Sarb Saur Sgnw Shaw Shrd Sidd Sind Sinh Sogd Sogo Sora Soyo Sund Sunu Sylo Syrc Syre Syrj Syrn Tagb Takr Tale Talu Taml Tang Tavt Telu Teng Tfng Tglg Thaa Thai Tibt Tirh Tnsa Todr Toto Tutg Ugar Vaii Visp Vith Wara Wcho Wole Xpeo Xsux Yezi Yiii Zanb Zinh Zmth Zsye Zsym Zxxx Zyyy Zzzz';
|
||||
|
||||
/** Two-letter region subtags (ISO 3166-1 and the aliases ICU knows). */
|
||||
export const REGIONS_2 = 'AC AD AE AF AG AI AL AM AN AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BU BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CP CQ CR CS CU CV CW CX CY CZ DD DE DG DJ DK DM DO DY DZ EA EC EE EG EH ER ES ET EU EZ FI FJ FK FM FO FR FX GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU HV IC ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NH NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA QO RE RH RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SU SV SX SY SZ TA TC TD TF TG TH TJ TK TL TM TN TO TP TR TT TV TW TZ UA UG UK UM UN US UY UZ VA VC VD VE VG VI VN VU WF WS XA XB XK YD YE YT YU ZA ZM ZR ZW ZZ';
|
||||
|
||||
/** UN M.49 numeric region subtags. */
|
||||
export const REGIONS_NUMERIC = '001 002 003 005 009 011 013 014 015 017 018 019 021 029 030 034 035 039 053 054 057 061 142 143 145 150 151 154 155 202 419';
|
||||
@@ -8,32 +8,33 @@
|
||||
* is GB) and `jp` (the language code is ja) — while rejecting valid
|
||||
* script subtags such as `zh-Hant`.
|
||||
*
|
||||
* The code tables come from ICU via `Intl` (Node and Electron ship full
|
||||
* ICU), so there is no hand-maintained list to drift out of date. ICU also
|
||||
* knows the deprecated aliases (`UK` → `GB`, `eng` → `en`); a subtag that
|
||||
* only exists as an alias is reported as invalid, because search engines
|
||||
* match hreflang against the canonical codes.
|
||||
* The code tables are generated from Node's full ICU
|
||||
* (`scripts/generate-hreflang-codes.mjs`) rather than read from `Intl` at
|
||||
* run time: the desktop app runs this inside Electron, whose trimmed ICU
|
||||
* has no name for dozens of valid codes (`ff`, `kl`, `419` in `es-419`) —
|
||||
* asking it there called real codes invalid. ICU's alias data, which is
|
||||
* the same in both runtimes, still decides `UK` → `GB` and `eng` → `en`: a
|
||||
* subtag that only exists as an alias is reported as invalid, because
|
||||
* search engines match hreflang against the canonical codes.
|
||||
*/
|
||||
import { LANGUAGES_2, REGIONS_2, REGIONS_NUMERIC, SCRIPTS } from './hreflang-codes.js';
|
||||
|
||||
/** Region subtags that are reserved or private-use, never a real country. */
|
||||
const RESERVED_REGIONS = new Set(['EU', 'EZ', 'UN', 'ZZ', 'QO', 'AA']);
|
||||
|
||||
const SHAPE = /^([a-z]{2,3})(?:-([a-z]{4}))?(?:-([a-z]{2}|\d{3}))?$/i;
|
||||
|
||||
type Names = { of(code: string): string | undefined };
|
||||
let names: { language: Names; script: Names; region: Names } | null | undefined;
|
||||
let tables: { languages: Set<string>; scripts: Set<string>; regions: Set<string>; numeric: Set<string> } | null =
|
||||
null;
|
||||
|
||||
function displayNames(): typeof names {
|
||||
if (names !== undefined) return names;
|
||||
try {
|
||||
const make = (type: 'language' | 'script' | 'region'): Names =>
|
||||
new Intl.DisplayNames(['en'], { type, fallback: 'none' });
|
||||
names = { language: make('language'), script: make('script'), region: make('region') };
|
||||
} catch {
|
||||
// A runtime without ICU display names: fall back to the shape check.
|
||||
names = null;
|
||||
}
|
||||
return names;
|
||||
function codeTables(): NonNullable<typeof tables> {
|
||||
tables ??= {
|
||||
languages: new Set(LANGUAGES_2.split(' ')),
|
||||
scripts: new Set(SCRIPTS.split(' ')),
|
||||
regions: new Set(REGIONS_2.split(' ')),
|
||||
numeric: new Set(REGIONS_NUMERIC.split(' ')),
|
||||
};
|
||||
return tables;
|
||||
}
|
||||
|
||||
function canonical(tag: string): string | null {
|
||||
@@ -53,26 +54,29 @@ export function isValidHreflangCode(raw: string): boolean {
|
||||
const lang = (m[1] ?? '').toLowerCase();
|
||||
const script = m[2];
|
||||
const region = m[3]?.toUpperCase();
|
||||
const t = codeTables();
|
||||
|
||||
const dn = displayNames();
|
||||
if (!dn) return true;
|
||||
|
||||
if (!dn.language.of(lang)) return false;
|
||||
if (lang.length === 2) {
|
||||
if (!t.languages.has(lang)) return false;
|
||||
} else {
|
||||
// A three-letter code for a language that has a two-letter one (`eng`,
|
||||
// `tur`) isn't ISO 639-1; three-letter-only languages (`fil`) are fine.
|
||||
if (lang.length === 3 && (canonical(lang)?.length ?? 0) === 2) return false;
|
||||
// `jpn`) isn't ISO 639-1. Three-letter-only languages (`fil`, `dsb`,
|
||||
// `skr`) are left alone — ISO 639-3 has thousands no table here lists,
|
||||
// and an unknown one is not evidence of a mistake.
|
||||
if ((canonical(lang)?.length ?? 0) === 2) return false;
|
||||
}
|
||||
|
||||
if (script) {
|
||||
const s = script.charAt(0).toUpperCase() + script.slice(1).toLowerCase();
|
||||
if (!dn.script.of(s)) return false;
|
||||
if (!t.scripts.has(s)) return false;
|
||||
}
|
||||
|
||||
if (region) {
|
||||
if (/^\d{3}$/.test(region)) return dn.region.of(region) !== undefined;
|
||||
if (/^\d{3}$/.test(region)) return t.numeric.has(region);
|
||||
if (RESERVED_REGIONS.has(region)) return false;
|
||||
if (/^(Q[M-Z]|X[A-Z])$/.test(region) && region !== 'XK') return false;
|
||||
if (!dn.region.of(region)) return false;
|
||||
// `UK` resolves to a name only as an alias of `GB`.
|
||||
if (!t.regions.has(region)) return false;
|
||||
// `UK` is in the table only as an alias of `GB`.
|
||||
if (canonical(`${lang}-${region}`)?.split('-').pop()?.toUpperCase() !== region) return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Regenerates `packages/shared-types/src/hreflang-codes.ts` — the code
|
||||
* tables `isValidHreflangCode` checks against.
|
||||
*
|
||||
* Why a generated table instead of asking `Intl` at run time: the desktop
|
||||
* app validates hreflang inside Electron, and Chromium ships a trimmed ICU
|
||||
* that has no display name for dozens of valid codes (`ff` Fula, `kl`,
|
||||
* `se`, the UN M.49 region `419` behind `es-419`, …). Asking ICU there
|
||||
* reported real codes as invalid, while the same check passed under Node.
|
||||
* The table is read from Node's full ICU here, once, so every runtime
|
||||
* judges a code the same way.
|
||||
*
|
||||
* Run under Node (not Electron) after a Node / ICU upgrade:
|
||||
* node scripts/generate-hreflang-codes.mjs
|
||||
*/
|
||||
import { writeFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
if (process.versions.electron) {
|
||||
console.error('Run this with Node: Electron’s ICU lacks many language and region names.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const A = 'abcdefghijklmnopqrstuvwxyz';
|
||||
const named = (type) => {
|
||||
const dn = new Intl.DisplayNames(['en'], { type, fallback: 'none' });
|
||||
return (code) => {
|
||||
try {
|
||||
return dn.of(code) !== undefined;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
};
|
||||
const isLanguage = named('language');
|
||||
const isRegion = named('region');
|
||||
const isScript = named('script');
|
||||
|
||||
const languages = [];
|
||||
const regions = [];
|
||||
for (const a of A) {
|
||||
for (const b of A) {
|
||||
if (isLanguage(a + b)) languages.push(a + b);
|
||||
const r = (a + b).toUpperCase();
|
||||
if (isRegion(r)) regions.push(r);
|
||||
}
|
||||
}
|
||||
const numericRegions = [];
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const r = String(i).padStart(3, '0');
|
||||
if (isRegion(r)) numericRegions.push(r);
|
||||
}
|
||||
const scripts = [];
|
||||
for (const a of A) for (const b of A) for (const c of A) for (const d of A) {
|
||||
const s = a.toUpperCase() + b + c + d;
|
||||
if (isScript(s)) scripts.push(s);
|
||||
}
|
||||
|
||||
const out = join(dirname(fileURLToPath(import.meta.url)), '..', 'packages', 'shared-types', 'src', 'hreflang-codes.ts');
|
||||
const line = (name, doc, list) => `/** ${doc} */\nexport const ${name} = '${list.join(' ')}';\n`;
|
||||
writeFileSync(
|
||||
out,
|
||||
`// Generated by scripts/generate-hreflang-codes.mjs from ICU ${process.versions.icu}` +
|
||||
` (Node ${process.version}). Do not edit by hand — re-run the script.\n\n` +
|
||||
line('LANGUAGES_2', 'Two-letter (ISO 639-1) language subtags.', languages) +
|
||||
'\n' +
|
||||
line('SCRIPTS', 'ISO 15924 script subtags.', scripts) +
|
||||
'\n' +
|
||||
line('REGIONS_2', 'Two-letter region subtags (ISO 3166-1 and the aliases ICU knows).', regions) +
|
||||
'\n' +
|
||||
line('REGIONS_NUMERIC', 'UN M.49 numeric region subtags.', numericRegions),
|
||||
);
|
||||
console.log(
|
||||
`hreflang-codes.ts: ${languages.length} languages, ${scripts.length} scripts, ` +
|
||||
`${regions.length} regions, ${numericRegions.length} numeric regions`,
|
||||
);
|
||||
Reference in new issue
Block a user