Skip to content
Gaurav TiwariDocs

Images, Fonts and Embeds

Version 1.3.6 Updated October 26, 2026

The lowest-risk, highest-return group: image loading, hero images, WebP and AVIF variants, local Google Fonts, YouTube previews, lazy rendering, speculative loading, and the WordPress quick toggles. All of it is on the Optimization tab except the lazy-render selectors and Google Maps path exceptions, which are on Exceptions.

Image, font and embed changes are made to the HTML of pages the page cache stores, so they need the page-cache drop-in installed and the origin page cache enabled. Logged-in users and bypassed requests see the page unchanged. Speculative loading and the WordPress quick toggles work through WordPress hooks and apply on every request.

Images

On Optimization > Images and embeds:

SettingKeyDefaultRange
Lazy-load non-critical imagesmedia.lazy_loadon
Add missing image dimensionsmedia.add_dimensionson
Images to load immediatelymedia.critical_images20 to 10
Hero image rulesmedia.hero_rulesemptyup to 30 rules
Generate optimized variantsmedia.optimize_uploadsoff
Serve optimized image variantsmedia.rewrite_variantsoff
Generated formatmedia.formatwebpwebp, avif
Image qualitymedia.compression8230 to 100
Lightweight YouTube previewsmedia.youtube_previewsoff

MediaOptimizer walks every <img> in document order:

  1. The hero, when a hero rule finds it: fetchpriority="high", loading="eager", and optionally a preload link (see below).
  2. The first N images (Images to load immediately, counted from the top of the page): loading="eager". The first one also gets fetchpriority="high" unless a hero was found or a url: hero is declared; the rest get fetchpriority="auto".
  3. Every later image: loading="lazy" when lazy-loading is on.
  4. Every image without a decoding attribute gets decoding="async".
  5. Missing dimensions: an image with a wp-image-<ID> class and no width or height gets both from the attachment’s metadata, which cuts layout shift.

A loading or fetchpriority attribute that is already present is never changed. Your theme, the author, and WordPress core’s own image prioritization have information document order does not, so their decisions win.

Images to load immediately matters more than it looks. Lazy-loading the LCP image hurts the metric you are trying to improve, because the browser delays the one request that decides when the page looks finished. Include your main hero image in the count, or better, name it with a hero rule.

When EWWW Image Optimizer (or Easy IO) already lazy-loads images, adds dimensions, or serves WebP, GT Performance steps back from that feature so two implementations never touch the same image. See Integrations.

To turn lazy-loading off for some pages in code:

add_filter( 'gt_performance_media_lazy_load', function ( bool $lazy ): bool {
    return is_front_page() ? false : $lazy;
} );

The filter is applied once per page, not per image. gt_performance_media_add_dimensions works the same way for dimensions.

Hero image rules

Document order is a guess. A hero rule is a statement. One rule per line:

<scope> => <target> [preload]
PartAccepted values
Scope* (every page), front_page, post_type:<type>, template:<template>
Targetattachment:<ID>, .<class on the img>, or url:<background image URL>
preloadOptional. Adds a preload link for the hero.
post_type:product => .wp-post-image preload
front_page => url:https://example.com/wp-content/uploads/hero.jpg
  • The first matching rule wins. Up to 30 rules are read, and lines that do not parse are dropped on save.
  • template: matches a classic page template by its file (template:templates/landing.php) or its name without .php (template:landing), or a block theme template by its slug (template:front-page).
  • attachment:<ID> finds the image by its wp-image-<ID> class or by a src that starts with the attachment’s file path, which also matches its resized copies.
  • url: is for a hero drawn as a CSS background. It is always preloaded, and it is never guessed from stylesheets.
  • With preload, a <link rel="preload" as="image" fetchpriority="high"> is added to <head>, carrying the image’s srcset and sizes as imagesrcset and imagesizes so the browser preloads the right size. It is skipped when the page already preloads that image, or when the hero <img> itself says loading="lazy".

A single page can override the site rules from the GT Performance box in the editor sidebar. Its Hero image field takes the target part only (attachment:123 preload, .my-hero, or url:…); leave it empty to use the site rules. The value is stored in the _gtperf_hero post meta and applies to newly cached copies of the page. The same box holds the per-page JavaScript options; see JavaScript Optimization.

WebP and AVIF variants

Generate optimized variants creates a WebP or AVIF copy whenever WordPress generates attachment metadata: on upload, or when thumbnails are regenerated. Existing uploads get variants only after their thumbnails are regenerated.

  • One background job per unique file (the original and each registered size), so a large import never blocks the upload request.
  • JPEG and PNG sources only.
  • The variant sits next to its source with the extension swapped: photo-300x200.jpg becomes photo-300x200.webp. An existing variant file is never overwritten.
  • Encoding uses WordPress’s image editor (Imagick or GD) at Image quality. AVIF needs an image editor with AVIF support on the server.

Serve optimized image variants rewrites src and srcset entries under your uploads URL to the variant, but only where the variant file exists. Everything else keeps its original URL. The original URL is replaced; there is no <picture> fallback.

Filters: gt_performance_generate_image_variants (bool, attachment ID, metadata) and gt_performance_rewrite_image_variants (bool).

Lazy rendering

Exceptions > Media exceptions > Lazy-render selectors (media.lazy_render_selectors) adds one rule to <head>:

<your selectors> { content-visibility: auto; contain-intrinsic-size: auto 800px; }

The browser then skips layout and paint for matching containers while they are off screen. Effective on long pages. Use it on whole sections, never on anything that must be measurable immediately or that an anchor link jumps to. A selector may use letters, digits, spaces, quotes and _ # . - > : ( ) , [ ] =; a selector containing any other character is ignored.

Fonts

On Optimization > Fonts:

SettingKeyDefault
Self-host Google Fontsfonts.self_host_googleoff
Font displayfonts.font_displayswap (swap, fallback, optional, block)

Self-hosting works on <link> stylesheets whose host is exactly fonts.googleapis.com (FontOptimizer):

  1. The first page render that sees one queues a background job and leaves the link unchanged. Nothing is fetched during a visitor’s request.
  2. The job downloads the stylesheet once (asking for woff2), then up to 40 font files it names from fonts.gstatic.com. A file is kept only if its header identifies it as WOFF2, WOFF, OTF or TTF.
  3. Fonts and the rewritten stylesheet are stored in wp-content/cache/gt-performance/assets/fonts/.
  4. Later renders point the <link> at the local copy. After that, no visitor request reaches Google, which is the privacy win as much as the performance one.

Google Fonts pulled in by an @import inside another stylesheet or by a JavaScript loader are not localized. See External Services.

Font display is written into each @font-face block of the self-hosted stylesheet when that stylesheet does not already set font-display (a Google Fonts URL with display= in it does). It has no effect on fonts that are not self-hosted. swap shows fallback text straight away; optional may skip the web font on a slow connection; block can leave text invisible for a moment.

The hourly cleanup removes generated font files that nothing has read or modified for 14 days. A stylesheet whose local copy is gone is queued for localization again.

YouTube embeds

Lightweight YouTube previews replaces a YouTube <iframe> (youtube.com/embed/, youtube-nocookie.com/embed/, or youtu.be/) with a 16:9 box showing the video’s thumbnail and YouTube’s red play button. A click anywhere on the thumbnail loads the real player from youtube-nocookie.com with autoplay.

  • The embed’s own parameters (start time, captions, and so on) carry over to the player.
  • Playlist (videoseries) and live-stream embeds have no single thumbnail, so they are left as real embeds.
  • The button covers the whole thumbnail and is labelled with the iframe’s title for screen readers.
  • The preview’s styles are one stylesheet in the page’s <head>; only the thumbnail is set inline. Inside a theme with responsive embeds, where core reserves the 16:9 box on the embed block’s wrapper, the preview is pinned over that box the way core pins an iframe, so no empty band appears above it, and a theme’s own embed wrapper can position it too.
  • Pages cached before 1.1.1 keep the older preview until they are rebuilt.

A standard embed loads YouTube’s player and its third-party connections on every page view, whether or not anyone watches. The preview costs one image. Thumbnails load from i.ytimg.com in the visitor’s browser; your server sends nothing to YouTube.

Speculative loading

WordPress 6.8 and later prefetch a page when a visitor starts to click its link. The Speculative loading card appears only on those versions (speculation.mode):

ModeEffect
WordPress default, with safety exclusions (recommended)core, the default. WordPress’s own mode and eagerness, plus GT Performance’s exclusions.
Prefetch only when a link is pressedconservative. Prefetch, conservative eagerness, plus the exclusions.
OffSpeculative loading is disabled.

The exclusions (SpeculationPolicy) are every cache bypass path, which includes the cart, checkout and account paths of every active commerce adapter and your own Paths that must stay dynamic. Prefetching those would start sessions and spend PHP workers on pages that are never cached. With plain permalinks, links carrying action, add-to-cart, remove_item, edd_action, eddfile, download_file, key, token, signature or redirect_to are excluded too. WordPress’s own exclusions (admin, login, files, query-string links) still apply.

When the Speculative Loading feature plugin is active, it keeps control of mode and eagerness and GT Performance adds only its exclusions. See Commerce Safety and Cache Exclusions.

WordPress quick toggles

Switches for things WordPress loads that many sites never use. All off by default.

ToggleEffect
Disable WordPress emoji assetsRemoves the emoji detection script and styles
Disable Dashicons for visitorsDequeues Dashicons for logged-out visitors only
Disable WordPress embedsRemoves oEmbed discovery links and the embed host script
Disable XML-RPCTurns off XML-RPC; the REST API stays available
Remove RSD linkRemoves the Really Simple Discovery link
Remove jQuery Migrate for visitorsDrops jQuery Migrate from jQuery’s dependencies on the front end
Remove WordPress versionRemoves the generator tag and replaces any asset ver= value equal to the WordPress version with a site-specific hash, so cache busting still works after an update
Remove shortlinkRemoves the shortlink from <head> and response headers
Disable every RSS feedEvery feed, including the main one, returns 404
Disable secondary feeds only/feed/ stays live; comment, category, tag, taxonomy, author, date, search and post type feeds return 404
Remove every RSS feed linkRemoves all feed discovery links; feeds keep working
Remove secondary RSS feed linksKeeps the main feed link and removes the rest
Disable self pingbacksStops pings to links on your own site
Remove REST API linksRemoves REST discovery links; the API keeps working
Disable Google MapsDequeues registered scripts from maps.googleapis.com or maps.google.com, except on paths listed under Exceptions > WordPress exceptions > Google Maps path exceptions (a fragment of the request path, such as /contact/)
Disable password strength meterDequeues the strength meter scripts; check sign-up and account forms
Remove comment author URLsDiscards comment author website links
Add a blank fallback faviconPrints an empty icon when the site has no Site Icon, saving a failed request
Remove global stylesDequeues global-styles and classic-theme-styles
Load separate core block stylesLoads core block CSS only for blocks that render

Apply site baseline checks the toggles used on gauravtiwari.org (every one above except the two whole-feed switches, the blank favicon, and the two block-style switches) and unchecks the rest. It does not touch the settings under Editor, comments, and APIs. Clear quick toggles unchecks them all. Neither takes effect until you save.

Optimization > Editor, comments, and APIs holds the rest:

SettingDefaultNotes
Heartbeat behaviorReduce frequencyWordPress default, Reduce frequency, Disable outside the editor, or Disable everywhere. Disabling everywhere can break autosave, post locking and some plugins.
Heartbeat interval60 s15 to 120, used by Reduce frequency
Autosave interval60 s15 to 3600. Not applied when AUTOSAVE_INTERVAL is already defined.
REST API accessKeep enabledAdministrators only blocks the REST API for everyone else: visitors, and editors and authors using the block editor. Disable all requests blocks it for administrators too, which breaks the block editor.
Disable commentsoffSite-wide: closes comments and pings, hides existing comments, removes comment support from every post type, and removes the Comments admin menu

GT Performance does not limit how many post revisions WordPress keeps; the WP_POST_REVISIONS constant in wp-config.php does that. Deleting existing revisions is covered in Database Cleanup.

Turning a stage off in code

Every HTML stage can be switched off per request with gt_performance_optimize_stage. The stages are css, javascript, media, images (variant rewriting), fonts and embeds (YouTube previews and lazy rendering).

add_filter( 'gt_performance_optimize_stage', function ( bool $allowed, string $stage ): bool {
    return 'embeds' === $stage && is_page( 'videos' ) ? false : $allowed;
}, 10, 2 );
Share

Was this helpful?

Still stuck? Open a support ticket