The request read "change the font to", followed by the name of the brand font. It came from the marketing manager on the client side, reached me through the contact who channels revision requests for this site, and arrived on 1 September together with two other requests. One of those asked for two city names on the homepage to be written in capitals. I had no hand in building this site, and my job there is limited to revisions. It runs on WordPress with the Hello Elementor theme plus a child theme, and its pages are built in Elementor.
Once I traced the request, its meaning flipped. The font being asked for had been installed since March 2023 and was already used in the CSS. Five city headings on the homepage were already set to font-family:"Brand Title", Sans-serif. The only broken part was the protocol used to load it, and because of that the brand font never rendered anywhere. So the request was not really "change the font". It was "the font does not show up". (Every font name in this post has been swapped for a neutral one such as Brand Title.)
@font-face from Custom Fonts, all of it http://
The font declarations were printed inline by Elementor Pro Custom Fonts, inside <style id="elementor-pro-custom-fonts-inline-css">. Three families were registered there, Brand Display Bold, Brand Title and Brand Alt, and every one of their src values pointed at http:// while the page itself was https://. Simplified, with a neutral name and domain, it looked like this:
<!-- simplified: family name and domain replaced -->
<style id="elementor-pro-custom-fonts-inline-css">
@font-face {
font-family: "Brand Title";
src: url("http://example.com/fonts/brand-title.woff2");
}
</style>An https:// page asking for a font over http:// triggers mixed content, and the browser blocks the request. As a result, every piece of text that asked for the brand font fell through to the generic Sans-serif. The font files themselves were healthy. I requested each one over https:// and every one of them answered 200. The problem was purely the protocol written in src.
The scope was the whole site, not just the homepage. Every page carried 14 http:// URLs inside @font-face, and the lighter pages carried 9. Not a single page displayed the brand typography.
Loading fonts over http:// was actually already in my status notes as of 27 August. It was recorded there as an old defect nobody had reported, with 28 console errors on every page, all of them fonts. That this old defect had killed the brand font across the entire site, and that it was what the client was really complaining about, only came out on 1 September while I was tracing the font change request.
23 URLs rewritten, nothing re-uploaded
The fix needed no purchase and no new files. Rewriting the font URLs to https:// through Elementor > Custom Fonts was enough. On the elementor_font edit screen, the font file URL turned out to be a plain text input, so it could simply be edited and saved with Update, with no need to upload the file again. Of eleven Custom Fonts, ten needed fixing, 23 URLs in total going from http:// to https://. Only one font had been clean from the start.
To change them in bulk, the #post form on the edit screen was pulled in with fetch, its values patched, and the result posted back as FormData plus publish=Update. The serialization is exactly what the browser sends, so no field gets lost. A simplified version, run from a logged-in wp-admin tab:
// Simplified. editUrl: the edit screen of one elementor_font post,
// for example "/wp-admin/post.php?post=ID&action=edit".
const html = await (await fetch(editUrl)).text();
const form = new DOMParser()
.parseFromString(html, "text/html")
.querySelector("#post");
const body = new FormData();
for (const [key, value] of new FormData(form)) {
// patch only values that still start with http://
body.append(key, typeof value === "string"
? value.replace(/^http:\/\//, "https://")
: value);
}
body.append("publish", "Update");
await fetch("/wp-admin/post.php", { method: "POST", body });The font posts were right, the pages were not
Fixing the font posts turned out not to be enough. Elementor keeps a copy of the @font-face rules in its own CSS files, uploads/elementor/css/post-*.css, and those files still contained http:// after the font posts were correct. The step that is mandatory afterwards is Elementor > Tools > Clear Files & Data.
The difference is easy to read in document.fonts. Before Clear Files & Data, Brand Title had the status error. Afterwards it was loaded, and the console was clean. To see the status of every face at once, one line in the console is enough:
// General example: the status of every FontFace the page knows about
[...document.fonts].map((f) => `${f.family} ${f.weight} ${f.status}`);The site sits behind Cloudflare, and every change needs a purge, which I do myself. After the purge I verified on 1 September on the plain URL without a cache buster, and cf-cache-status was MISS everywhere, so everything was genuinely fetched again from the origin. The homepage carried 14 https:// font URLs and zero http://, and the kit CSS file and the homepage CSS file had zero http:// as well.
I had also noted, wrongly, that the site logo was caught by mixed content too. I corrected that on 2 September. On a recheck, zero <img> or <source> tags anywhere on the site used http://. The two http:// occurrences of the logo URL were inside Rank Math's JSON-LD, in the @id and url of an ImageObject, and the http URL itself returns a 301 to https. The impact is limited to structured data hygiene and one redirect hop, not a broken display and not a security problem, so I deliberately left it alone.
The capitalization that fixed itself
The capitalization request turned out to resolve itself as soon as the font came alive. Lowercase letters in Brand Title are drawn as full capitals, so city names stored in Title Case rendered in all caps.
At this point I briefly concluded the opposite. I checked the cmap table of BrandTitle-Base.ttf directly, and 0 of 26 lowercase letters used an uppercase glyph. From that I concluded the font had its own lowercase and was not a capitals-only font. As a basis for a conclusion, that was wrong. The cmap table only maps character codes to glyph IDs, and different glyph IDs do not mean different shapes. What decides the appearance is the render, not the cmap table.
I still changed the stored text of the six city headings on the homepage to capitals, so the fallback text stays correct if the font ever fails to load again. The plain-URL check on 1 September recorded all six city names on the homepage in capitals.
The second batch, and a family name that was never registered
On 2 September my contact forwarded two more emails from the client's marketing manager, along with a request to fix the header menu. Most of the complaints in them had already been answered by the 1 September fix, because the screenshots were taken before it. I checked the exact elements that had been sent: the H1 on one venue page now rendered in the brand font, and an animated heading on another page was correct as well. My contact also said "revisions all are gone", and that was not true. The homepage still held 54 revisions, two city pages held 24 and 48, and pages nobody had touched still held revisions from October and November 2025. Nothing had been deleted by Clear Files & Data.
The same day a second font defect came up. Elementor's global typography in the kit CSS file pointed to the family "Brand Display" for all three of its variables, primary, secondary and accent. But only the fully named variants had an @font-face: Brand Display Bold, Brand Display Medium Face, Brand Display Light Face and Brand Display Bold Face. Browsers match a family name as a whole, not as a prefix, so "Brand Display" pointed at no @font-face at all, and anything inheriting the global typography fell back to a generic sans-serif.
The impact turned out to be far smaller than it looked, and I misreported its scale twice. Elementor's global typography rule has low specificity, .elementor-widget-heading .elementor-heading-title at 0-2-0, while per-element rules at 0-4-0 override it almost everywhere with fonts that are actually served. A computed-font census on one venue page counted Brand Display Bold on 73 elements, Brand Alt on 30, Brand Display Medium Face on 22, and the bare Brand Display on only 13, all of them 14px footer text.
My first mistake was the figure "232 CSS rules". It came from grep "Brand Display", which also matched Brand Display Bold and Brand Display Medium Face, two healthy fonts. The real count was 8 to 32 per page, and the search needs a negative lookahead:
# family name replaced with a neutral one
grep -oP 'Brand Display(?! (Bold|Medium|Light|Face))'My second mistake was calling it a site-wide heading failure. Wrong, only the footer was hit. The footer's reach was wide, though: the site's footer templates carried the same five bare Brand Display rules, so the defect touched 99 of 113 pages plus all 182 posts.
Fixing the kit did not heal the footer
I fixed the global typography in the kit on 2 September. Its system_typography has four entries, and primary, secondary and accent were changed from Brand Display to Brand Display Bold. All 24 keys of each entry were copied intact, so weight and size did not change. The text entry was left empty because it inherits from the theme anyway.
What actually healed the defect was fixing 40 widgets across eight footer templates. The footers used font-family:"Brand Display" hardcoded per widget, so they did not change when the kit was fixed. The pattern was the same in every template: three icon-list widgets on the key icon_typography_font_family and two heading widgets on typography_font_family. Five widgets per template times eight templates gives those 40 widgets.
The broken widgets were found without guessing: walk every container in the editor and collect each key matching /font_family$/ whose value is exactly Brand Display. The logic, in simplified form:
// Simplified: root stands for the container tree in the editor,
// each node has settings (keys and values) and children.
const hits = [];
const stack = [root];
while (stack.length) {
const node = stack.pop();
for (const [key, value] of Object.entries(node.settings ?? {})) {
if (/font_family$/.test(key) && value === "Brand Display") {
hits.push({ id: node.id, key });
}
}
stack.push(...(node.children ?? []));
}After that, Clear Files & Data again, just as mandatory as with the first fix. The result was verified: references to the bare Brand Display dropped to zero across the site, and the census on the same venue page showed 13 footer elements moving from the fallback to Brand Display Bold. The count went from exactly 73 to 86, which is 73 plus 13.
First letters that rendered thin
As soon as that fix was live, I noticed something new: the first letter of every word rendered thin while the rest stayed bold. I briefly explained it as small caps. That was wrong. The issue was weight, not size.
The mechanism was only proven once I rendered the font at four weights, 400, 500, 600 and 700, and took screenshots of the result. The Brand Display Bold family is made up of several @font-face rules. At weights 400 and 500 the browser picked a face that only contains uppercase glyphs, a thin serif shape. Lowercase letters do not exist in that face, so the browser took them from a sibling face in the same family, a bold one. The text was stored in Title Case, so the first letter of each word came from the thin face and the rest from the bold one. At 600 and 700 the result was uniform, because the face chosen there contains both letter classes. That also explains why headings such as VENUES, INFORMATION and CONTACT looked fine from the start: all three are 22px at weight 600.
The fix I chose was to raise weight 500 to 600 on the five widgets in all eight footers, on the keys icon_typography_font_weight and typography_font_weight. The weight keys were derived from the family keys: for every *_font_family key whose value is Brand Display Bold, swap the suffix for _font_weight, then change only the ones whose value is 500, so no other widget gets touched. The rendering was verified as uniform afterwards.
The final check ran on the plain URL, and this time no extra Cloudflare purge was needed, because the HTML on the plain URL already pointed at the same CSS file version as the origin. The four footer CSS files I checked had zero font-weight:500 and eight font-weight:600, and references to the bare Brand Display stayed at zero. That day the Elementor cache was cleared twice.
Render first, then conclude
The general lesson from this case: do not infer the cause of what you see from a font's name or from its cmap. I was wrong twice on this site in the same way. Rendering for real and taking a screenshot is the only valid evidence.
The rest is more practical. When a client asks to "change the font" to a font whose name is already in the CSS, first check whether that font has ever actually rendered, for example through its status in document.fonts. In Elementor, fixing src in Custom Fonts is not done until the copy of @font-face in uploads/elementor/css has been thrown away with Clear Files & Data. When searching for a family name that is also the prefix of other family names, use a negative lookahead, because without it the count swallows the healthy families too. And not every http:// in your HTML is mixed content. URLs inside JSON-LD, for instance, are never requested by the browser as subresources.