Validation
The <head>
element sets up all of the necessary metadata for a page to load properly and performantly. capo.js performs a number of validation checks on the <head>
to ensure it meets modern best practices.
There are a few ways to see when an element is invalid:
- a warning is logged to the console
- the element appears striped in the color bar
- the expanded console entry is annotated with an ❌ icon
In the example above, you can see all three ways that an element can be flagged as invalid: top-level warning, striped color bar, and warning styles in its expanded entry.
The extension’s color bar similarly displays a striped pattern when an element is invalid.
No disallowed elements
According to the HTML specification, the only elements allowed in the <head>
are:
<base>
<link>
<meta>
<noscript>
<script>
<style>
<template>
<title>
If capo.js detects any other elements in the <head>
, it will log a validation warning.
In the example above, a <noscript>
element contains an <img>
child element, so capo.js warns that “IMG elements are not allowed in the <head>
”.
Of all the invalid elements, <img>
elements are the most widespread, found on over 1.5 million web pages. This antipattern is commonly used by analytics scripts to fall back to image beacons when users have JavaScript disabled. Unless you routinely test your website with JavaScript disabled, you may be unaware of the potential breakages caused by prematurely closing the <head>
element.
Exactly one <title>
element
The HTML specification requires that there be exactly one <title>
element in the <head>
, to specify the document title.
If capo.js detects zero or more than one <title>
element, it will log a validation warning:
In the example above, the <title>
element is missing, so capo.js warns that “Expected exactly 1 <title>
element, found 0”.
No more than one <base>
element
The HTML specification requires that there be no more than one <base>
element in the <head>
, to specify the document base URL.
If capo.js detects more than one <base>
element, it will log a validation warning:
In the example above, there is more than one <base>
element, so capo.js warns that “Expected at most 1 <base>
element, found 2”.
No invalid <meta http-equiv>
elements
There are a handful of standardized pragma directives that can be used with the http-equiv
attribute to change browser behavior. There are even some non-standard directives that browsers still choose support, the most notable being origin-trial
. And there are actually a couple of standardized directives that are non-conforming, meaning that their use is totally discouraged, specifically: content-language
and set-cookie
.
Even when using a standardized, supported, and conforming http-equiv
directive, its content
attribute value may still be invalid. For example, the content-type
directive must declare a character encoding of UTF-8. There are some additional restrictions on this directive, specifically the requirements that it be discovered within the first 1024 bytes and it doesn’t coincide with the <meta charset>
element.
Many of these cases are harmless no-ops, but capo.js will validate them and provide a helpful warning message with relevant metadata to help you understand whether it’s safe to remove it or if anything needs to be changed for it to work properly.
There are some cases where it’s not so harmless:
set-cookie
doesn’t actually set any cookies, which could lead to broken behavior.content-type
character encoding can be declared too late, which may lead to performance issues.content-security-policy
breaks Chrome’s preload scanner, which may lead to performance issues.content-security-policy
directives likeframe-ancestors
andsandbox
are not supported in meta tags and may have been set mistakenly, breaking assumptions about the page’s security.description
and similar metadata may have been mistakenly assigned to thehttp-equiv
meta attribute instead of the intendedname
attribute, which could break assumptions about the page’s SEO.refresh
can reload or redirect the page and is known to cause accessibility issues.
No <meta>
CSP
According to the W3C specification, a Content Security Policy (CSP) can be set as either an HTTP header or a <meta http-equiv>
tag.
Despite <meta>
CSP declarations being technically valid, per the spec, browsers handle them differently. In particular, Chrome will disable the preload scanner if it discovers a CSP declared after a <script>
element. The preload scanner can improve performance by 20%, so this behavior has major implications on the user experience.
If capo.js detects a <meta>
CSP anywhere in the <head>
, it will log a validation warning:
In the example above, there is a <meta>
CSP element, so capo.js warns that “CSP meta tags disable the preload scanner due to a bug in Chrome. Use the CSP header instead.”
This validation warning is an example of capo.js being more opinionated than simply following the specification. The warning includes a recommendation to use the CSP header instead, which avoids the preload scanner issue all together. Also note that the Content-Security-Policy-Report-Only
directive is only valid as an HTTP header and not as a <meta http-equiv>
element.
Additionally, some CSP directives are not allowed in the <meta>
declaration:
frame-ancestors
sandbox
report-uri
If capo.js detects any of these directives in a <meta>
CSP element, it will log a validation warning.
No invalid origin trials
Sites can register for origin trials to enable individual experimental web platform features. To enable them on a given site, a token must be included as either an Origin-Trial
HTTP header or <meta http-equiv>
element.
These tokens contain encoded metadata about the origin trial registration, including:
- the name of the experimental feature
- the allowed origin
- the expiration date
- whether other subdomains are allowed
- whether other origins are allowed
capo.js decodes these tokens and validates their metadata to ensure that:
- the token is not expired
- the origin is an allowed subdomain
- the origin is an allowed third party
If capo.js detects an invalid origin trial token, it will log a validation warning:
In the example above, two separate embedded third party scripts injected origin trial <meta>
elements with invalid tokens, so in each case capo.js warns that there is an “Invalid origin trial token”. The warning also includes a reference to the <meta>
element as well as the decoded token metadata.
In the first warning, the token contains an invalid origin. The token metadata is missing the isThirdParty
flag and the origin
property is set https://googlesyndication.com:443
, which is presumably the third party that injected the token. However, because the origin of the page is different from the one in the origin trial metadata, and it wasn’t registered as a third party token, it’s not valid. A similar warning would appear if the origin of the page is https://www.example.com
but the origin in the metadata is https://example.com:443
and it’s missing the isSubdomain
flag.
In the second warning, the origin is a valid third party, but the token is expired. In the token metadata, you can see that it expired in November 2022.
No invalid default-style
directives
The default-style
directive indicates which alternative stylesheet should be enabled. While it’s completely standard and supported across browsers, adoption is extremely low at only about 1k websites. Of those, about 31% are used incorrectly.
To be used correctly, the content
attribute of the directive must be equal to the title
value of an alternative stylesheet. capo.js will warn when the title cannot be found and the directive has no effect.
In addition to flagging incorrect usage, capo.js will also discourage using this directive entirely. Setting a preferred stylesheet results in a flash of unstyled content, which could be avoided by using default stylesheets with @media
rules instead.
No invalid character encoding
The content-type
directive is an alias for the charset
meta tag, which declares the document’s content encoding. The HTML spec places strict requirements on how and where this declaration can occur:
- The document cannot have both a
content-type
directive and acharset
meta tag - If one does exist, the character encoding declaration must be found within the firsts 1024 bytes of the document
- If one does exist, the character encoding must be set to UTF-8
It’s valid for a document not to have either content-type
nor charset
meta tags, as long as the Content-Type
HTTP header is set, also to UTF-8. capo.js does not presently validate HTTP headers, only the contents of the document <head>
.
capo.js will validate that all three of the requirements above are met, and log a warning if not. If there are redundant character encoding declarations, capo.js will warn on the content-type
element, giving preference to the charset
meta tag. If the declaration occurs too late, capo.js will include the byte index for reference. And if the encoding is not set to UTF-8, capo.js will log the actual encoding used.
No meta refresh
The refresh
directive can force the page to reload or redirect after a specified amount of time. This is considered an accessibility issue by the WCAG. It can be disorienting to users for the page to suddenly reload or redirect, and there are more semantic ways to indicate that a page’s contents have moved.
capo.js issues a warning whenever a meta refresh
directive is found. If it contains a redirect, capo.js encourages using HTTP 3xx redirects instead. Otherwise, it encourages including a mechanism for users to disable the auto-reloading behavior, if one is not already provided by browsers.
Don’t disable DNS prefetching
The x-dns-prefetch-control
HTTP header and meta directive are non-standard, but widely supported. By default, most browsers will speculatively resolve the DNS records for URLs needed by the page, which is a generally accepted performance optimization.
In Chrome, any value other than on
will disable the default DNS prefetching behavior. capo.js will always warn when this directive is found, and the message will be unique to the usage. In cases when DNS prefetching is explicitly enabled, capo.js clarifies that this has no effect as it’s already the default behavior. In cases where it’s set to off
, capo.js emphasizes the performance benefits of DNS prefetching and clarifies that it should only be disabled when there are legitimate security concerns. For all other values besides on
and off
, capo.js warns about using non-standard values and reiterates the performance and security considerations.
No invalid viewport directives
The viewport
meta tag is typically used to control the layout of the page on mobile devices. There are a small number of supported viewport directives, and capo.js will validate them to ensure they are used correctly.
capo.js will log a warning if any unsupported directives are used, or if any of the supported directives are used incorrectly. For example, the initial-scale
directive must be a positive number between 0.1 and 10 and capo.js will warn if it is not.
Some directives have accessibility implications. For example, the user-scalable
directive can be set to no
to prevent users from zooming in on the page. This can be a problem for users with low vision who rely on zooming to read text. capo.js will log a warning if the user-scalable
directive is set to no
, or its alias 0
.