The classic mistakes made when a TYPO3 website goes live, checked against the
site itself: 23 automatic checks that name the value they actually found, say
in one sentence why it matters, and link to the place where it is fixed —
plus twelve points only a human can confirm, recorded with the user who
confirmed them and when.
A robots.txt taken along from staging hides the entire site. Nothing
breaks, the pages look right, and the mistake surfaces weeks later when someone
asks why the site is not in Google. The sitemap line in robots.txt is
the only place in a TYPO3 configuration where the domain is spelled out, so it
is the only one that does not travel along when the site moves. A 404 handler
pointing at a hidden page turns every unknown address into a server error.
This extension adds one backend module that looks for those, and for about two
dozen more. It reads the site configuration, the page tree and
$GLOBALS['TYPO3_CONF_VARS']
, and it sends real HTTP requests to the
site's own live address — because half of these mistakes are invisible from the
inside.
Every finding names the value that was found, not a rule that was violated:
the wrong base URL, the pages still flagged as
no_index
, the sender
address still pointing at example.com.
One report per site. The header counts by severity, and the verdict says in
three words whether this site can go live.
What it is not
Not a scanner
It checks the site it is installed in, over its own configured base URL.
It cannot be pointed at a foreign domain.
Not a rule engine
The list of checks is fixed in code, and so are the word lists behind a few
of them (placeholder text, template titles, common passwords). There is no
configuration to tune, and no thresholds to set. Extensions can
add their own checks.
Not a cron job
The report is built when the module is opened, in the request of the
backend user who opened it. Nothing runs in the background, nothing is
stored except the ticks of the manual checklist, and nothing is sent by
e-mail.
Not a guarantee
A green report means the things this extension knows how to look at are in
order. The twelve manual points exist precisely because the important
remainder — a tested backup restore, imported redirects, consent before
tracking — cannot be established by a machine.
Why it skips instead of passing
A check that cannot reach its answer reports as not verifiable, never as
passed. No loopback connection to the live URL, an extension that is not
installed, a relative base URL that makes a domain comparison meaningless —
all of these end in a grey result with the reason.
A false green is worse than no statement at all, because it replaces the very
check it pretends to be.
Installation
Target group: Administrators
Requirements
TYPO3 13.4 LTS or 14
PHP 8.2, 8.3 or 8.4
An administrator account — the module is not available to editors
Without it, the scheduler check is skipped instead of reporting a missing
setup.
Installation with Composer
composer require webagentur-yahya/golive-check
Copied!
Installation without Composer
Install the extension from the
TYPO3 Extension Repository
in the Admin Tools > Extensions module, or download it there and
upload the archive.
After installing
Open Site Management > Go-Live Check. The report is built when the
page loads; on a site with a reachable live URL this takes a few seconds
because a dozen checks send real HTTP requests.
Nothing else has to be configured. The extension writes nothing to the database
schema — the ticks of the manual checklist live in
sys_registry
under the
namespace golive_check.
If every live check reports "not verifiable"
The live checks are sent from the web server to the site's own base URL. That
request has to work from the server, which is not the same as working from
your browser:
In a local development environment (DDEV, Docker) the container often
cannot resolve the production domain, and there is nothing to reach anyway.
This is expected — the report is meant for the live installation.
A firewall or a load balancer may block a request that leaves and
immediately comes back (hairpin NAT).
If the site's base has no host at all, the report falls back to the host
of the current backend request and marks every comparison against a domain
as not verifiable, because the answer would be a statement about the
backend URL rather than about the site.
Each skipped check names its reason, so the report says which of these applies.
Using a report
Target group: Administrators
Opening the module
Site Management > Go-Live Check. The module answers a question
about one website, so it has a site selector in its header; installations with
a single site never see a choice to make.
Access is restricted to administrators. Several checks read backend user
records, mail configuration and debug switches — values an editor has no
business seeing.
When the checks run
Every time the module is opened. There is no stored result, no cache and no
background run: reloading the page is what re-checks the site.
Each URL is fetched only once per report — /robots.txt is read by three
checks, and three separate requests could even contradict each other if someone
saved in between. The HTTP timeout is five seconds per request, and at most
256 KB of a response body is read, which is why a multi-megabyte sitemap does
not slow the report down.
Reading a finding
The header counts the findings by severity:
Blocker
Do not go live. The site is invisible, broken, or legally exposed.
Warning
Going live is possible, but it costs visibility, security or trust.
Notice
Cosmetics and polish.
The severity belongs to the check, not to the result — a missing privacy page is
always a blocker, whether it is currently missing or not. One check deviates on
purpose: an expired TLS certificate is a blocker, a certificate expiring in 25
days is a warning. It is the same check with two weights.
Below each finding there are up to three things:
The value that was found, unformatted and untranslated. This is the
actual content of the configuration — the wrong URL, the affected page
titles, SYS/devIPmask = * — and it is deliberately not localised,
because the report has to show what really is in there.
A link to the place where it is fixed: the record, the page module, the
site configuration, or the URL in the frontend. External targets open in a
new tab so the report stays where it is.
A fix button, but only in the two cases described below.
A finding as the report shows it. The value in the monospaced box is what
really is in the configuration — here a base URL that never left the
development machine.
The one-click fix
Two findings can be repaired from the report: a robots.txt that locks
search engines out, and a missing or wrong Sitemap: line in it. Both times
the correct value follows necessarily from the site configuration, so there is
nothing to guess.
This requires that robots.txt is served by TYPO3 from a static route in
the site configuration. If the file is delivered by the web server from disk,
the report says so and offers no button.
Everywhere else there is no button, and that is a decision rather than a gap. A
control that quietly writes a guess into a configuration is worse than none,
because afterwards nobody looks again.
The manual checklist
Twelve points no machine can confirm — a restore actually tested, redirects from
the previous site imported, nothing tracking before consent, the imprint text
read by the customer.
Ticking one records the backend user and the timestamp, and the report shows
both. That is the point of the list: it doubles as a handover protocol, and
"ticked" without "by whom, when" is worthless once someone complains.
The ticks are stored per site in
sys_registry
(namespace
golive_check, key manual.<site identifier>). They survive a cache flush
and are not part of the page tree, so they do not travel with a content export.
Manual points sit in the same categories as the automatic checks, marked
by hand. They are the only entries with a button that records who
confirmed them.
Printing and handover
Print / save as PDF opens the browser's print dialog, and every
current browser can write that to a PDF. The printed version carries a document
header — site, domain, date, the name of the user who printed it, and the TYPO3
version — that is invisible on screen.
There is no PDF library involved, on purpose: the extension stays free of a
heavy dependency that every installation would have to carry along for a page
that is printed twice in the lifetime of a project.
"Not verifiable" and what causes it
Grey results are checks that could not reach their answer. The reason is always
named; these are the ones that occur:
The site has no absolute base URL, so there is no live address to call.
The live URL did not answer (the message contains the transport error).
The base URL was only assumed from the current backend request, which makes
a comparison against a domain meaningless.
There is no robots.txt or no sitemap.xml to look into — that
is reported once, by the check that is about the file's existence, and the
checks that would read its content skip.
The sitemap exists but contains no entries yet.
The base URL is not https, so the certificate and the HTTP-to-HTTPS
redirect have nothing to say.
The site has no pages, or there is no enabled administrator account.
What the report cannot see
Anything that only happens in the browser: a tracking script fired before
consent, a cookie banner that does not block, a layout that breaks on a
phone.
Whether content is correct — only whether it still contains filler text.
Whether a backup can be restored. A backup nobody has restored is a folder
of files, and the only way to know is to try it.
These are exactly the twelve points on the manual list.
What is checked
Target group: Administrators, Integrators
Twenty-three automatic checks in five categories, plus twelve manual points. The
categories are ordered by the damage of overlooking them, not alphabetically:
whoever goes live invisible to search engines notices weeks later, a missing
meta description never.
Each entry below names the severity and where the answer comes from — the local
configuration, the database, or a real HTTP request to the site's live address.
Scope
Checks that look at pages and content only look at the pages of the selected
site. Another site root in the tree ends the descent: a subtree with its own
config.yaml is a site of its own and is checked there, not here.
Indexing and sitemap
robots.txt is delivered
Warning · HTTP request to /robots.txt
Fails on any status other than 2xx, and reports the status code that came
back. Every other robots check on the page reads this same response.
Search engines are not locked out
Blocker · the robots.txt response
Fails when the file forbids all user agents the whole site
(User-agent: * together with Disallow: /). This is the
staging file that was taken along on launch.
Can be fixed from the report if robots.txt comes from a static route
in the site configuration.
Sitemap line points to this domain
Warning · the robots.txt response
Fails when there is no Sitemap: line at all, and when a line names a
host other than the site's own. The expected value is the site's base
followed by /sitemap.xml.
The host comparison is skipped when the base URL was only assumed, because
the finding would then be a statement about the backend host.
Can be fixed from the report under the same condition as above.
sitemap.xml delivers a sitemap
Blocker · HTTP request to /sitemap.xml
Fails on a status other than 2xx — and also on status 200 when the body is
not a sitemap. A 404 handler that serves a page answers 200 with HTML, so
the body has to start with <?xml and contain <urlset or
<sitemapindex.
Sitemap entries point to this domain
Blocker · the sitemap.xml response
Reads the first 50 <loc> entries and fails when any of them names a
different host, listing up to five of them. Entries on a foreign domain make
a search engine index the staging site, or discard the sitemap as not
belonging to this property.
Pages excluded from search
Notice · database, table
pages
Lists the visible pages of this site with
no_index = 1
, up to twelve,
each with a link to the record. It is a notice rather than a blocker
because on a thank-you page the flag is right — only you know which is meant
here.
Base address is the live domain
Blocker · site configuration, base
Fails when the base has no host at all, when the host is a development one
(localhost, .ddev.site, .local, .test, .localhost,
127.0.0.1), and when the scheme is not https.
Every absolute address TYPO3 builds comes from here — canonical tags,
hreflang, og:image, sitemap — and a wrong base makes all of them wrong at
once without anything looking broken.
Website title belongs to the customer
Notice · site configuration, websiteTitle
Fails when it is empty or still one of the known template titles
(New TYPO3 site, Bootstrap Package, Example and similar).
TYPO3 configuration
Production context
Blocker · application context
Anything other than Production fails. In Development, TYPO3 answers
errors with a full stack trace: file paths, class names and, depending on
the error, configuration values.
404 handler works
Blocker · site configuration, errorHandling
Fails when there is no handler for error code 404. If the handler is of type
Page and points at t3://page?uid=…, the target page has to exist
and must not be hidden — a handler pointing at a deleted or hidden page
turns every unknown address into a server error.
Other handler types and external URLs pass without further inspection.
Mail actually leaves the server
Blocker ·
$GLOBALS['TYPO3_CONF_VARS']['MAIL']
Fails on transport null or debug, on an SMTP server that is a mail
catcher (mailpit, mailhog, mailcatcher, localhost,
127.0.0.1, ::1), on an empty defaultMailFromAddress, and on a
sender address from a template domain (example.com, example.org,
example.ch, domain.tld, localhost).
The contact form says thank you either way, and nobody misses an enquiry
they never heard about.
No build-time extensions active
Warning · installed packages
Reports styleguide, adminpanel and bootstrap_package_demo while
they are still active. Demo content is the fastest way to build a site,
which is exactly why it stays in: its pages are reachable, appear in the
sitemap and compete with the real ones.
Scheduler runs
Warning · database, table
tx_scheduler_task
Skipped when
typo3/cms-scheduler
is not installed. Fails when no
enabled task exists, when nothing has ever run, and when the last execution
is more than 24 hours old — that last case is the cron job nobody set up.
Security
Debug output is off
Blocker ·
$GLOBALS['TYPO3_CONF_VARS']
Reports SYS/devIPmask = *, SYS/displayErrors = 1,
BE/debug and FE/debug. A * as the developer IP mask
means every visitor on earth counts as a developer, and every error hands
them the stack trace.
Host header is restricted
Warning ·
SYS/trustedHostsPattern
Tested by behaviour, not by wording: the pattern is matched against a host
it must never accept. That catches .* as well as .*.*, (.*),
.+ and ^.*$ — there are arbitrarily many spellings for "everything".
The special value SERVER_NAME passes.
An empty or syntactically invalid pattern is not reported: TYPO3 then
rejects every host, which is broken but not open, and this check only
reports what is open.
No build-time password left
Blocker · database, table
be_users
Takes up to ten enabled administrator accounts and tests their password
hashes against password, admin, 123456, typo3, test,
demo and Password1, using TYPO3's own hash implementation.
The verdict is cached against a fingerprint of the hash, so the hashing runs
again only for an account whose password changed. A hash no known
implementation can read is not reported — a false alarm on an administrator
account costs more trust than it buys.
Document root is set correctly
Blocker · HTTP requests to four paths
Requests /.env, /composer.json,
/config/system/settings.php and
/config/system/additional.php. A hit counts only when the body looks
like the file itself rather than like an HTML page, so a 404 page answering
with status 200 does not raise a false alarm.
If the web server points at the project directory instead of
public/, then .env and the database credentials are publicly
readable.
http redirects to https
Warning · one HTTP request, redirects not followed
Calls the http:// variant of the base URL once. Fails when the answer is
not a redirect, and when the Location does not point to https.
Skipped when plain http does not answer at all, and when the base URL is not
https to begin with — that is reported by the base check instead.
TLS certificate is valid
Blocker, Warning below 30 days · TLS connection to the base host
Reads the peer certificate and reports the subject, the issuer and the
expiry date. Expired, an untrusted chain, or fewer than seven days left is a
blocker; fewer than 30 days is a warning, because that is when Let's Encrypt
renews.
This is the one check whose severity depends on its finding: expired means
the site is unreachable, expiring in 25 days means it belongs in a calendar.
Protective headers are sent
Warning · HTTP request to the home page
Looks for X-Content-Type-Options: nosniff, Referrer-Policy, and
protection against framing — either X-Frame-Options or
frame-ancestors in a Content Security Policy, whichever is present.
Strict-Transport-Security is only required on an https site, where it
has an effect.
Legal
Imprint and privacy page exist
Blocker · database, table
pages
Looks for a page of this site whose slug contains impressum,
imprint, legal-notice or mentions-legales, and one containing
datenschutz, privacy or protection-des-donnees. Missing fails,
and so does present but hidden.
Both pages are mandatory in German-speaking countries and both are
actionable. They rarely miss entirely — more often they sit finished in the
tree and are still hidden.
Content
No filler text on visible pages
Warning · database, table
tt_content
Searches header and bodytext of the visible content elements on this site's
pages for lorem ipsum, dolor sit amet, Musterstrasse,
Musterstraße, Mustermann, Musterfirma, Platzhalter,
example.com, Ihr Text hier and your text here. Lists up to ten
hits, each linking to the page module.
Filler text survives exactly where nobody looks any more: the third column,
the collapsed accordion, the page that exists only for its menu entry.
Pages have a meta description
Notice · database, table
pages
Lists visible standard pages of this site that are indexable and have an
empty description, up to ten. Page types that never appear in a search
result — folders, shortcuts, spacers — are not counted.
The twelve manual points
These cannot be established by a machine. Ticking one records the backend user
and the timestamp; see The manual checklist.
Redirects from the previous site imported — blocker
Search Console set up and sitemap submitted — warning
Staging protected or taken offline — warning
DNS and mail records switched over — blocker
Backup set up and a restore actually tested — blocker
Uptime and certificate monitoring in place — warning
Contact form tested end to end — blocker
Imprint and privacy text checked by the customer — blocker
Nothing tracks before consent — blocker
Image licences clarified — warning
Tested on phone, tablet and desktop — warning
Content proofread and links clicked — warning
Adding your own check
Target group: Developers
A check is one class. Any extension can contribute checks to the same report,
and this extension does not need to know about them.
The class
Implement
CheckInterface
. The container tags every implementation
automatically, so there is no registration list to edit:
This assumes your extension requires webagentur-yahya/golive-check.
If it has to work without it as well, exclude the check classes from the YAML —
both from the
_instanceof
block and from the resource glob that would
otherwise autowire them — and register them behind a guard in
Configuration/Services.php, which is evaluated while the container is
built:
Passed. May carry details, which are shown as the confirmed value.
CheckResult::failed()
A finding. Takes a message key and a solution key, optional arguments for
the message, details, links, and a severity that overrides the check's own
for this one finding.
CheckResult::skipped()
The answer could not be reached. Use this whenever the check would otherwise
have to guess — an unreachable URL, a missing table, an extension that is
not installed. Never return
ok()
in that situation: a false green
replaces the very check it pretends to be.
The $details are the raw values that were found and are never translated.
They are what makes a finding actionable, and translating them would stop the
report from showing what is really in the configuration.
What the context offers
CheckContext
is handed to
run()
so that a check does not fetch
site, request and URI builder itself. That is also why a check can be unit
tested without a TYPO3 bootstrap — build the context by hand and call
run()
.
The resolved live address without a trailing slash. It is assumed when the
site has a relative base and the host had to be taken from the current
backend request — a finding based on it would not hold, so most checks skip
then.
fetch()
,
fetchAbsolute()
,
url()
HTTP requests against the site, with a five second timeout, at most 256 KB
of body, and 4xx or 5xx returned as a result rather than thrown. Each URL is
fetched once per report, so several checks can read the same file.
Targets for
CheckLink
, so the report can point at the record, the
page, or the site configuration instead of only describing the problem.
Texts
The texts of a check live in the language file of its own extension, at
Resources/Private/Language/locallang_golive.xlf. Which extension that is
gets answered by the file the class lives in, so a check declares nothing and
inherits nothing.
<trans-unitid="check.cookieBanner.title"><source>Consent is asked before tracking</source></trans-unit><trans-unitid="check.cookieBanner.why"><source>One sentence on what it costs to get this wrong.</source></trans-unit>
Copied!
Plus every message and solution key the check returns — above:
check.cookieBanner.ok, check.cookieBanner.fail and
check.cookieBanner.solution. Placeholders are %s, filled from the
message arguments in order.
The shared keys are found without you copying them: skip.*, link.*,
severity.* and status.* fall back to this extension's language file. A
key that resolves nowhere is shown verbatim in the report, which makes a
forgotten text visible immediately rather than silently empty.
A translation into another language is a second file next to it, prefixed with
the language key — de.locallang_golive.xlf.
A fix button
Implement
FixableCheckInterface
as well — but only if the correct value
follows necessarily from the configuration:
publicfunctiongetFixLabelKey(): string{
return'check.cookieBanner.fixLabel';
}
publicfunctionapplyFix(CheckContext $context): string{
// Throw \RuntimeException if it cannot be applied after all.return'check.cookieBanner.fixed';
}
Copied!
The returned key is the success message.
applyFix()
may assume that
run()
has just reported a finding; the controller verifies that before
calling.
Everything where a human's intention is involved keeps the link to the place
instead. A button that quietly writes a guess into a configuration is worse than
none, because afterwards nobody looks again.
Changelog
1.0.1
No functional change. ext_emconf.php no longer opens with a
declare(strict_types=1)
statement, because the TER cannot read a file
that does — it rejected 1.0.0 with "Details could not be extracted from the
provided file".
1.0.0 therefore exists on Packagist only; in the TER, 1.0.1 is the first
version. Both contain the same code.
1.0.0
First public release.
Backend module Site Management > Go-Live Check, administrators
only, with a site selector.
23 automatic checks in five categories — indexing and sitemap, TYPO3
configuration, security, legal, content. Local configuration, database and
real HTTP requests against the site's own live address.
12 manual points, ticked with user and timestamp, stored per site in
sys_registry
.
One-click fix for the two robots.txt findings whose correct value
follows from the site configuration.
Print view with a document header for the handover.
Checks are collected by DI tag, so other extensions can contribute their own
— see Adding your own check.
English and German.
TYPO3 13.4 and 14, PHP 8.2 to 8.4.
Sitemap
Reference to the headline
Copy and freely share the link
This link target has no permanent anchor assigned.The link below can be used, but is prone to change if the page gets moved.