BMT Systems

Getting Your HEAD in Order

Thomas M. Brodhead

General Considerations

Any browser compliant with HTML5 does not need to provide <head> tags explicitly. The <head> elements may simply be placed at the top of the HTML, but for clarity’s sake, it’s best to use <head> tags during composition (and to ensure errant browsers can parse the elements correctly).

The elements allowed in the head are (in alphabetical order): <base>, <link>, <meta>, <noscript>, <script>, <style>, <title>. A few considerations:

  • The only required <head> element is <title>.
  • <script> elements containing JavaScript should be excluded except in special circumstances. JavaScript is render-blocking and should only occur in the <head> if it expedites the critical rendering path, e.g., to help retrieve assets required for rendering above-the-fold content in the initial view, etc. The issue of deferring JavaScript until DOMContentLoaded or the window onload event is discussed in full in Deferring JavaScript and CSP 3 ’strict-dynamic’. In contrast, <script type=application/ld+json> elements and other non-JavaScript script elements are not processed by the browser, and therefore are not render-blocking.
  • If the basic styling of the <body> can be rendered without JavaScript, then it might be better to place the the <noscript> element there. This will allow for better visual placement and styling of the <noscript> warning.
  • <link> elements that reference CSS style sheets should be replaced with a single inline <style> block with the complete CSS needed for rendering the HTML. This is necessary to expedite the execution of the critical rendering path, which benefits from as few navigations away from the browser as possible. This strategy is mandatory for AMP sites, and for good reason: the more content that can be delivered with the initial HTTP request, the faster the rendering and greater the performance of the application, now a critical component of SEO.
  • The order of <head> elements is arbitrary, with one exception: the character encoding must be expressed within the first 1024 bytes of the document, as mandated in the W3C specification on the character encoding declaration. This is also explained by W3C in its dedicated article on the subject, Declaring Character Encodings in HTML.

While the last point may seem to mandate that <meta charset=utf-8> (or the necessary encoding) should be the first element of the head, it does not. It makes better sense to organize the <head> elements in the following semantic order, with sub-elements organized alphabetically:

  • <title> should appear first, as it uniquely identifies the document semantically.
  • <base> is essential for correct retrieval of all subsequent assets.
  • <meta> elements will provide info and links to assets that uniquely identify the site for search engines, page readers, and social media.
  • <link> elements that reference anything except CSS style sheets, e.g., fonts or favicons.
  • <style> containing the complete, minified CSS for the page.
  • <script type=application/ld+json> or other non-JavaScript script blocks.

Notice that the <noscript> element is omitted here, as it may be better graphically presented in a CSS-styled section of the <body>, as discussed earlier.


Some conventional advice is to put <meta charset=utf-8> first in the <head>, not two elements down in the ordering prescribed here. In fact, for AMP sites, <meta charset=utf-8> must be the first child of the <head>. However, for all other sites, the charset attribute only needs to occur in the first 1024 bytes of the HTTP payload. Since a picture paints a thousand words, here is the amount of data that would have to precede <meta charset=utf-8> for it to be received at the end of the first 1024 bytes of the HTML, which would still be valid:

brightness_high content_paste
<!DOCTYPE html>
<html lang=en-us>
    <title>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eget dolor morbi non arcu risus. A erat nam at lectus urna duis convallis convallis. Lobortis scelerisque fermentum dui faucibus in ornare quam. Mi tempus imperdiet nulla malesuada pellentesque elit eget gravida cum. Scelerisque purus semper eget duis at tellus at. Commodo ullamcorper a lacus vestibulum sed arcu non odio euismod. Ut enim blandit volutpat maecenas volutpat blandit aliquam etiam. Tristique senectus et netus et malesuada. Et malesuada fames ac turpis egestas sed. Eget velit aliquet sagittis id consectetur purus ut faucibus. Maecenas ultricies mi eget mauris pharetra et ultrices neque ornare. Ipsum dolor sit amet consectetur. Orci phasellus egestas tellus rutrum tellus pellentesque eu tincidunt. In nulla posuere sollicitudin aliquam ultrices. L</title>
    <base href="">
    <meta charset=utf-8>

That’s 1024 bytes altogether, and is exaggerated for illustration. The same page with a title of a reasonable length would be:

brightness_high content_paste
<!DOCTYPE html>
<html lang=en-us>
    <title>My Fantastic and SEO-friendly Title</title>
    <base href="">
    <meta charset=utf-8>

That’s only 168 bytes, and is a more realistic example of the data received at the start of the HTML. So, our recommendation is to put the charset element first in the <meta> section, which is where it would belong alphabetically, anyway: the allowed attributes of <meta> are charset, content, http-equiv, name, and scheme.

Don’t use HTML entities! (Well, with a few exeptions...)

Notice that declaring charset=utf-8 eliminates the need for HTML entities throughout your document, with the exception of ampersand (&), the less-than symbol (<), and any character that cannot be represented without special encoding, such as the non-breaking space ( ). With those exceptions in mind, do not use HTML entities in your documents; you’ve already declared utf-8, so they are unnecessary. Using them complicates the work of screen readers and makes copying text from the browser to a word processor more error-prone. See Google’s HTML/CSS style guide entry, Entity References.

Declaring OG Schema the Right Way

If you are using Facebook’s Open Graph (OG) schema in <meta> elements, e.g.:

brightness_high content_paste
<meta property=og:description content="Web Coding, Tutorials, Consulting, and Cookbook">
<meta property=og:image content="">
<meta property=og:image:alt content="BMT Systems">
<meta property=og:image:height content=630>
<meta property=og:image:secure_url content="">
<meta property=og:image:type content="image/png">
<meta property=og:image:width content=1200>
<meta property=og:title content="BMT Systems, Inc.">
<meta property=og:type content=website>
<meta property=og:url content="">

...then you need to define the og: prefix in two places. First, add to the <html> element prefix="og:", like this:

brightness_high content_paste
<html lang=en-us prefix="og:">

Second, in order for Schema parser of Google (and possibly the parsers of the other three other founders of the Schema initiative: Microsoft, Yahoo, and Yandex) to recognize the og: prefix, you must add to the <head> element typeof="", like this:

brightness_high content_paste
<head typeof="">

This will satisfy both the Facebook Open Graph parser and Google’s Schema parser when reading the site. (You may test your results with Google’s Structure Data Testing Tool and the Yandex Structure Data Validator.)

Alphabetize META and LINK elements

Alphabetize <meta> and <link> elements by attributes and then by their values. To see this organization in action, reload this page using ?beautify as a query string on the URL, then view the source. (Minified HTML for this site is delivered from the server by default: the query string ?beautify toggles the HTML to a human-readable view, and the query string ?minify restores the minification.) See if it’s easier for you to locate the properties in the <head> when its elements are organized and alphabetized as prescribed.

You might prefer to organize the entries by category or another scheme, which is fine. There is no HTML5 prescription for the precise ordering of <head> elements except that the <meta charset> element should occur within the first 1024 bytes received by the browser.

Preload Fonts and Declare @font-face CSS Immediately Afterwards

Custom fonts may only be employed via @font-face CSS declarations, which perform lazy-loading: the fonts are only pulled to the browser when text requiring them is encountered during the paint process. This comes with a performance penalty, and many solutions have been proposed.

At present, the best solution is two-part: (1) use the rel attribute with preload as its value in the <link> elements for fonts, and (2) immediately follow the font links with @font-face CSS. Thus, put font links above CSS links (or, better, before the complete <style> block), and let @font-face rules be the first CSS. This ensures that the fonts are downloaded immediately and ready to use when encountered in page rendering, something that using @font-face alone with non-preloaded fonts would not accomplish. Additionally, preload woff2 fonts, not woffs or other font types: optimize your site for users with the most current browsers.

So, to load the Noto Sans fonts for this site, the sequence is:

brightness_high content_paste
<link rel=preload href="" as=font type="font/woff2" crossorigin>
<link rel=preload href="" as=font type="font/woff2" crossorigin>
<link rel=preload href="" as=font type="font/woff2" crossorigin>
<link rel=preload href="" as=font type="font/woff2" crossorigin>

@font-face{font-family:'Noto Sans';font-weight:400;font-style:normal;src:url('');src:url('') format('embedded-opentype'),
local('Noto Sans'),
url('') format('woff2'),
url('') format('woff'),
url('') format('truetype'),
url('') format('svg');}

@font-face{font-family:'Noto Sans';font-weight:400;font-style:italic;src:url('');src:url('') format('embedded-opentype'),
local('Noto Sans Italic'),
url('') format('woff2'),
url('') format('woff'),
url('') format('truetype'),
url('') format('svg');}

@font-face{font-family:'Noto Sans';font-weight:700;font-style:normal;src:url('');src:url('') format('embedded-opentype'),
local('Noto Sans Bold'),
url('') format('woff2'),
url('') format('woff'),
url('') format('truetype'),
url('') format('svg');}

@font-face{font-family:'Noto Sans';font-weight:700;font-style:italic;src:url('');src:url('') format('embedded-opentype'),
local('Noto Sans Bold Italic'),
url('') format('woff2'),
url('') format('woff'),
url('') format('truetype'),
url('') format('svg');}

For an exhaustive account of font loading and its various solutions, including this one, see Zach Leatherman's A Comprehensive Guide to Font Loading Strategies.

Delete http-equiv="X-UA-Compatible"

You may have encountered this element from time to time:

<meta http-equiv="X-UA-Compatible" content="IE=edge">

Delete it. It applies only to IE, and it was introduced in IE8 and disabled in IE11. It was intended to affect Compatibility View in IE8, 9, and 10, and it merely tells Internet Explorer what engine to use before parsing the content. Should you need it, the best practice is an X-UA-Compatible HTTP Header sent from your site’s server, not from the user’s browser. More troubling:

  • It fails HTML Validation
  • It causes IE to stop and re-render your page
  • It was only ever designed for temporary use while you fix problems

For more information, see the discussion about it on Stack Overflow: Why use X-UA-Compatible IE=Edge anymore?

Custom Data

Custom data, including information that would normally require AJAX within a JavaScript routine, may be written to the HTML as values of data-* attributes in <meta> elements. With this technique, the name of the custom data would comprise the data-* attribute, and the data itself would be its value.

A <meta> element, however, must contain one of the standard attributes referenced above, so the data-* attribute would have to be a secondary attribute of that <meta> element. But which attribute should be the primary one? Providing custom data in a name/property attribute pair is a good choice, but for an unobvious reason. The name attribute is primarily restricted to these values:

  • application-name
  • author
  • description
  • generator
  • keywords
  • referrer

See Mozilla’s description of the meta name attibute for more info.

None seems like a good candidate for storing custom data-* attributes; a more generic name property would be better. Fortunately, there is a rich set of proposed values for the name attribute that will pass HTML validation. They are defined at the W3 wiki on MetaExtensions.

The most interesting candidate of them is web_author, a useful colophon that web developers would profitably employ to mark their work on a site. So, a <meta> element with a web_author attribute could be employed with custom data-* attributes. Importantly, this element would not obscure the semantics of <meta> elements with standard name attributes. For example:

brightness_high content_paste
<meta name=web_author content="Thomas M. Brodhead" data-root-dir="/" data-default-uri="blog/getting-your-head-in-order" data-webp-support=true data-lazyload-images=false data-timestamp=20180213152741>

This passes HTML validation and avoids confusing the more standard <meta> elements with custom data. It will be instrumental in the technique for conditionally switching between JPGs and WEBPs in supporting browsers, described in detail in Detecting WebP Support and Deploying WebP Images Efficiently, found elsewhere at this site.

Odds and Ends