Uploading custom fonts to WordPress lets you use typography that is not limited to the fonts bundled with your theme or installed on a visitor’s device.
You might want to upload a custom font because it is part of a brand identity, because you purchased a commercial typeface, because you want to self-host an open-source font or because you want more control over privacy and performance than a third-party font service provides.
Modern WordPress makes this considerably easier than it used to be.
Starting with WordPress 7.0, the built-in Font Library is available globally for both block themes and classic themes through:
Appearance → Fonts
From there, WordPress can upload and manage supported font files without requiring you to manually build every @font-face rule.
Developers and theme authors can still register fonts through theme.json, bundle them directly with a theme or load them through conventional CSS when more control is required.
This guide explains the complete process, including:
- which custom font formats WordPress supports;
- how to upload fonts with the native Font Library;
- where WordPress stores uploaded fonts;
- how to register fonts with
theme.json; - how to use
@font-facemanually; - how font weights and styles should be configured;
- how variable fonts differ from static files;
- how to avoid unnecessary font downloads;
- how font loading can affect performance and layout stability;
- what to check before uploading commercially licensed fonts.
What does uploading a custom font to WordPress actually mean?
A web font has to complete several steps before visitors can see it.
At a high level:
font file exists
↓
font file is hosted somewhere accessible
↓
browser is told about the font
↓
font family, weight and style are registered
↓
CSS applies the font
↓
browser downloads it
↓
text renders with the custom typeface
Uploading the file is therefore only one part of the process.
If you manually place:
my-font.woff2
on the server but never create a corresponding font definition, the browser has no reason to use it.
WordPress’s Font Library now handles much of this relationship automatically.
Self-hosted fonts
A self-hosted font is served from infrastructure controlled by your WordPress site rather than fetched from a third-party font provider during the visitor’s page request.
For example:
https://example.com/wp-content/uploads/fonts/my-font.woff2
is self-hosted by the website.
A URL such as:
https://fonts.example-provider.com/my-font.woff2
would instead be a third-party request.
For the broader architectural comparison, see Self-Hosted Fonts vs. Google Fonts in WordPress.
Which font formats does WordPress support?
The current WordPress Font Library supports uploading:
.ttf;.otf;.woff;.woff2.
The official WordPress Font Library documentation lists these formats for custom font uploads.
WOFF2 is usually the preferred web format
For modern websites, WOFF2 is normally the best default format when it is available.
WOFF2 was designed specifically for delivering fonts over the web and generally provides substantially better compression than raw desktop font formats such as TTF or OTF.
A smaller font file generally means:
- less network transfer;
- faster downloading;
- less competition with other page resources;
- a better chance that text switches to the intended font quickly.
TTF and OTF are not automatically better because they are original files
Desktop font files may contain more information than a browser needs for ordinary web rendering.
Uploading a 700 KB OTF file when a 90 KB optimized WOFF2 version is available can unnecessarily increase page weight.
The browser does not reward the website for carrying around the heaviest possible version of the typeface.
WOFF is still useful for compatibility requirements
WOFF is an older web-font format and remains widely supported.
For a modern site targeting current browsers, WOFF2 alone is often sufficient.
If your project has specific legacy-browser requirements, your compatibility strategy may justify additional formats.
Check the font license before uploading it
Technical compatibility does not automatically mean legal permission.
A font may be:
- free for personal use only;
- licensed for desktop use but not web embedding;
- licensed for a limited number of page views;
- licensed for one domain;
- open source;
- licensed specifically for web use.
Before self-hosting a commercial font, confirm that your license permits web embedding and self-hosted delivery.
Do not assume that purchasing a desktop font automatically gives you unlimited web-font rights.
How to upload a custom font in WordPress 7.0
On current WordPress installations, the easiest method is the native Font Library.
Navigate to:
Appearance
→ Fonts
The Font Library provides sections for managing installed fonts, uploading files and installing supported font collections.
Open the Upload section
Inside the Font Library, select the upload interface.
Choose the font file from your computer.
Supported files include:
.ttf
.otf
.woff
.woff2
WordPress processes the upload and adds the resulting font family or variant to the site’s Font Library.
Upload each variant you actually need
A font family may contain several styles and weights.
For example:
Inter Regular
Inter Medium
Inter SemiBold
Inter Bold
Inter Italic
These are not necessarily one file.
With static fonts you may have:
inter-regular.woff2
inter-medium.woff2
inter-semibold.woff2
inter-bold.woff2
inter-italic.woff2
Upload the variants your design actually uses.
Do not automatically upload every weight provided by the font family.
Where WordPress stores uploaded fonts
WordPress has dedicated infrastructure for uploaded fonts.
The wp_font_dir() function returns the path and URL for the WordPress font upload directory.
Fonts installed through the Font Library are stored locally rather than requiring the browser to retrieve them from an external provider.
The typical upload structure uses a fonts directory associated with WordPress uploads.
This separation is useful because fonts are conceptually different from ordinary image and document attachments.
WordPress handles Font Library uploads separately
Internally, WordPress’s Font Face REST API handles font-file uploads using the standard WordPress upload mechanisms with font-specific MIME restrictions.
The Core WP_REST_Font_Faces_Controller is responsible for managing font-face resources through the REST API.
This is another reason you should prefer the native Font Library over trying to disguise font files as ordinary media uploads.
How to use an uploaded font
Installing a font makes it available to WordPress, but you still need to decide where it should be used.
With themes and blocks that support typography controls, installed font families can appear in the Font Family interface.
You can then assign the font to:
- site-wide typography;
- headings;
- paragraphs;
- specific blocks;
- theme styles;
- other typography-aware components.
Changing the site’s global font
For themes with Global Styles support, typography settings can be changed from the Styles interface.
You might configure:
Body
→ Custom Sans
Headings
→ Custom Display
This is generally preferable to manually applying a font family to every block on every page.
Per-block font selection
Blocks that support a font-family control can also use installed fonts individually.
This can be useful for intentionally different typography, but excessive per-block overrides can make the site’s design difficult to maintain.
For most sites, establish a small global typography system first.
How to upload Google Fonts locally through WordPress
The WordPress Font Library can also install fonts from Google Fonts.
This is different from embedding Google’s normal remote stylesheet.
When you install a Google Font through WordPress’s Font Library, WordPress downloads the selected font files and stores them locally on your website.
The visitor therefore retrieves the installed font from your site rather than directly from Google’s font servers.
Only install the variants you use
If a family provides:
100
200
300
400
500
600
700
800
900
normal
italic
that can represent a large number of separate static font files.
A site using only:
400
600
700
does not need every other weight simply because they are available.
Static fonts versus variable fonts
A traditional static font file normally represents a specific combination of properties.
For example:
font-weight: 400
font-style: normal
Another file may represent:
font-weight: 700
font-style: normal
A variable font can contain a range of values inside a single font resource.
Variable font example
A variable font may support:
font-weight: 100 900
allowing the browser to use many weights from one variable file.
This can simplify font delivery when a design genuinely uses several weights.
A variable font is not automatically smaller
If the website uses only:
400
700
two well-subsetted static WOFF2 files may be smaller than one large variable font containing an entire design space.
If the site uses many weights, a variable font may be more efficient.
Measure the actual resources rather than relying on the word “variable” as a performance guarantee.
Using custom fonts through theme.json
Theme developers can register custom fonts directly in theme.json.
The official WordPress Theme Handbook typography documentation supports custom font families through:
settings.typography.fontFamilies
A font-family definition can include one or more font faces.
For example:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"typography": {
"fontFamilies": [
{
"name": "Brand Sans",
"slug": "brand-sans",
"fontFamily": "\"Brand Sans\", sans-serif",
"fontFace": [
{
"fontFamily": "Brand Sans",
"fontStyle": "normal",
"fontWeight": "400",
"src": [
"file:./assets/fonts/brand-sans-regular.woff2"
]
},
{
"fontFamily": "Brand Sans",
"fontStyle": "normal",
"fontWeight": "700",
"src": [
"file:./assets/fonts/brand-sans-bold.woff2"
]
}
]
}
]
}
}
}
This approach is particularly useful when the font is an intentional part of the theme itself.
The file: notation references theme files
WordPress supports paths such as:
file:./assets/fonts/brand-sans.woff2
for font files bundled with the theme.
This keeps the font definition portable with the theme rather than depending on a hard-coded production URL.
theme.json can expose the font to WordPress typography controls
Registered font families become part of WordPress’s typography presets.
The slug is also used to generate a CSS custom property in the form:
--wp--preset--font-family--brand-sans
This helps keep typography consistent across WordPress-generated styles.
Uploading a font manually with @font-face
You do not have to use the Font Library or theme.json.
Traditional CSS works too.
The CSS @font-face rule tells the browser where a custom font file lives and how it should be classified.
For example:
@font-face {
font-family: "Brand Sans";
src: url("/wp-content/themes/my-theme/assets/fonts/brand-sans-regular.woff2") format("woff2");
font-style: normal;
font-weight: 400;
font-display: swap;
}
You can then use:
body {
font-family: "Brand Sans", Arial, sans-serif;
}
Register each static weight correctly
If the family has separate files, define them separately.
For example:
@font-face {
font-family: "Brand Sans";
src: url("/fonts/brand-sans-regular.woff2") format("woff2");
font-style: normal;
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: "Brand Sans";
src: url("/fonts/brand-sans-semibold.woff2") format("woff2");
font-style: normal;
font-weight: 600;
font-display: swap;
}
@font-face {
font-family: "Brand Sans";
src: url("/fonts/brand-sans-bold.woff2") format("woff2");
font-style: normal;
font-weight: 700;
font-display: swap;
}
Do not label every file as weight 400
If a bold font file is registered as:
font-weight: 400
the browser cannot correctly understand the family.
You can end up with:
- the wrong file being selected;
- synthetic bolding;
- incorrect font matching;
- unnecessary downloads.
Font style matters too
Italic is not simply another font weight.
A dedicated italic face should normally be registered with:
font-style: italic;
For example:
@font-face {
font-family: "Brand Sans";
src: url("/fonts/brand-sans-italic.woff2") format("woff2");
font-style: italic;
font-weight: 400;
font-display: swap;
}
If no true italic face exists, browsers may synthesize a slanted version.
That may not match the intended typography.
Use a fallback font stack
A custom font declaration should normally include fallback fonts.
For example:
font-family: "Brand Sans", Arial, Helvetica, sans-serif;
or:
font-family: "Brand Serif", Georgia, "Times New Roman", serif;
The fallback matters because the custom font may:
- still be downloading;
- fail to load;
- be blocked;
- be unavailable due to a server issue.
The fallback also has a major effect on visual stability while the custom font loads.
Understand font-display
The font-display descriptor controls how browsers handle text while a web font is loading.
Common values include:
auto;block;swap;fallback;optional.
font-display: swap
A common strategy is:
font-display: swap;
This allows fallback text to render instead of leaving text invisible while waiting for the custom font.
When the web font becomes available, the browser can replace the fallback.
Swap can create visual movement
The downside is that two fonts may have different metrics.
If the fallback font is narrower or wider than the web font, switching between them can change:
- line breaks;
- paragraph height;
- button width;
- heading size;
- element positions.
That can contribute to layout shift.
For a deeper explanation, read Why Web Fonts Cause Layout Shift and How to Avoid It.
Custom fonts can affect Core Web Vitals
Fonts are visual assets, but they can influence several aspects of page loading.
A custom font can affect:
- render timing;
- layout stability;
- page weight;
- network competition;
- the appearance of above-the-fold text.
The main mistake is assuming that typography has no performance cost because font files are smaller than images.
A page requesting several font files before important content stabilizes can still create a noticeable user experience problem.
Do not upload every font weight
Suppose a font family provides nine weights:
100
200
300
400
500
600
700
800
900
and also provides italics for each.
That could mean eighteen static font resources.
If your design only uses:
400 regular
600 regular
700 regular
400 italic
then those four variants are probably the useful ones to host.
Adding files that no element uses creates unnecessary storage and can create accidental network requests if CSS references them.
Avoid duplicate font loading
A common WordPress performance problem is loading the same family from multiple sources.
For example:
theme loads Google Font remotely
+
page builder self-hosts same family
+
custom CSS loads another copy
+
plugin adds its own stylesheet
You now have several competing font implementations.
Before uploading a custom font, inspect whether your theme, builder or plugin already loads that family.
Check the browser Network panel
Open browser developer tools and filter requests by:
Font
or extensions such as:
woff
woff2
ttf
otf
Look for:
- duplicate families;
- duplicate weights;
- remote font providers;
- 404 responses;
- unexpected font files;
- very large font resources.
Preload only critical fonts
A browser can preload a font that is known to be needed immediately.
A typical preload might look like:
<link
rel="preload"
href="/fonts/brand-sans-regular.woff2"
as="font"
type="font/woff2"
crossorigin
>
Preloading can help when an important above-the-fold font would otherwise be discovered late.
But preloading every font file is not optimization.
Excessive preload creates competition
Every preload tells the browser:
This resource is important. Fetch it early.
If you preload:
- six font weights;
- several images;
- multiple scripts;
- other resources;
they compete for bandwidth and connection priority.
Preload only fonts that are truly important during initial rendering.
Use subsets where appropriate
Font files can contain glyphs for many languages and writing systems.
A website that requires only a particular character set may sometimes use a subsetted font to reduce file size.
This is especially useful for very large typefaces with broad Unicode coverage.
However, incorrect subsetting can remove characters visitors genuinely need.
Before reducing glyph coverage, consider:
- site languages;
- accented characters;
- currency symbols;
- mathematical symbols;
- editorial requirements;
- future localization.
Custom fonts and CORS
When a font is served from the same WordPress domain, cross-origin configuration is usually straightforward.
Problems can appear when fonts are served from:
- a CDN;
- another subdomain;
- external asset storage;
- a separate static domain.
Browsers apply cross-origin rules to font resources.
If the response does not allow the requesting origin appropriately, the browser may refuse to use the font even though the URL works when opened directly.
A font URL returning 200 does not guarantee successful use
Always inspect browser console errors as well as network status.
A request can technically download while still being rejected for use because of:
- CORS policy;
- incorrect MIME type;
- invalid font data;
- incorrect CSS descriptors.
Custom fonts and caching
Font files are normally static resources.
That makes them good candidates for browser and CDN caching.
A visitor generally should not need to download the same unchanged font file on every page view.
Use versioned or stable font URLs
If you replace a font file while retaining exactly the same URL and aggressive caching headers, some visitors may temporarily continue receiving the old cached version.
A deployment process can solve this through:
- versioned filenames;
- cache invalidation;
- appropriate CDN purging;
- controlled cache headers.
Font Library versus theme-bundled fonts
There is no universal winner.
Use the Font Library when:
- site administrators should manage typography;
- fonts should remain independent of the active theme;
- you want a UI-based workflow;
- you want to install custom or locally hosted fonts without editing theme files.
Bundle fonts with the theme when:
- the typeface is fundamental to the theme design;
- the font should version together with theme code;
- developers control deployment;
- the theme needs deterministic typography without site-level setup.
A theme-specific design system often benefits from theme.json.
A site owner experimenting with branding may prefer the Font Library.
Font Library versus ordinary Media Library uploads
Fonts should not be thought of as ordinary editorial media.
The Media Library is primarily designed around attachments such as:
- images;
- audio;
- video;
- documents.
The Font Library provides font-specific registration and management.
That distinction matters because a font file needs more than a URL.
WordPress also needs to understand:
- font family;
- weight;
- style;
- available variants;
- how it should appear in typography controls.
When you may still need additional upload permissions
Some workflows involve font files outside WordPress’s native Font Library, custom upload interfaces or plugin-specific asset managers.
In those situations, WordPress MIME restrictions can become relevant.
TheOneWP File Upload Types provides controlled support for additional upload formats and role-based upload permissions.
Do not broadly enable arbitrary file types merely to solve one font-upload problem.
Allow only the formats the workflow genuinely requires.
Custom fonts in the WordPress admin area are separate
Uploading a frontend font does not automatically change the typography of wp-admin.
The WordPress administration interface has its own stylesheets and loading context.
If the goal is specifically to change backend typography, read How to Change the WordPress Admin Font.
Custom fonts on the WordPress login screen are separate too
The login page is another independent WordPress context.
A font available to the active theme does not necessarily become available automatically on:
wp-login.php
Login-screen typography requires its own enqueueing or customization strategy.
For that specific context, see Choosing Google Fonts for a WordPress Login Page.
Common custom-font mistakes
Uploading the font but never registering it
A file on the server does not automatically become a usable CSS font family.
Uploading every available weight
This increases complexity and can increase network transfer without improving the design.
Using a large TTF when WOFF2 is available
Web delivery should favor efficient web-font formats.
Registering the wrong font weight
A bold file should not normally be declared as weight 400.
Forgetting italic variants
The browser may synthesize fake italics instead.
Using no fallback font
This weakens the loading experience and can make failure behavior unpredictable.
Loading the same font twice
Check themes, builders, plugins and custom CSS before adding another source.
Preloading every font file
Preload is a priority hint, not a collection badge.
Ignoring layout shift
A fallback font with very different metrics can cause visible movement when the web font arrives.
Ignoring licensing
The ability to upload a font does not imply the right to distribute it from your website.
Using a font only because it looks good in a design tool
Check real browser rendering, available weights, language coverage, readability and file size before committing to it.
A practical custom font workflow for WordPress
- Choose the font family.
- Confirm its web-use license.
- Identify the weights and styles the site actually uses.
- Prefer WOFF2 files where appropriate.
- Check whether the font is already being loaded elsewhere.
- Upload it through Appearance → Fonts or register it through the theme.
- Confirm each weight and style is classified correctly.
- Define sensible fallback fonts.
- Apply the font through global typography where possible.
- Test the frontend.
- Test the Block Editor.
- Inspect font requests in browser developer tools.
- Check for duplicate downloads.
- Measure font file sizes.
- Check layout stability while fonts load.
- Preload only genuinely critical font resources.
- Test mobile and slower network conditions.
- Verify caching and CDN behavior.
Custom WordPress fonts checklist
- Confirm you have permission to self-host the font.
- Prefer WOFF2 for modern web delivery.
- Upload only required font weights and styles.
- Use the native WordPress Font Library where appropriate.
- Remember that current WordPress exposes fonts under Appearance → Fonts.
- Use
theme.jsonfor fonts that belong to the theme design system. - Use
@font-facewhen manual CSS control is required. - Register every static weight correctly.
- Register italic faces with the correct style.
- Use a suitable fallback stack.
- Consider
font-displaybehavior. - Check for layout shift.
- Do not load duplicate font families.
- Do not preload every font.
- Check font file sizes.
- Consider variable fonts when many weights are genuinely required.
- Check character coverage before subsetting.
- Verify CORS when fonts are served from another origin.
- Configure caching for static font assets.
- Test fonts in both editor and frontend contexts.
TheOneWP tools related to custom font workflows
Custom typography can interact with several different WordPress areas, so the relevant TheOneWP modules depend on where the font is being used.
File Upload Types
File Upload Types lets administrators enable additional upload formats and control who can upload them.
This is useful for custom asset workflows that operate outside the native Font Library.
Backend Interface Font
Backend Interface Font addresses typography inside the WordPress administration area rather than the public theme.
Frontend, editor, login and administration typography should be treated as separate contexts.
Related guides
- Self-Hosted Fonts vs. Google Fonts in WordPress
- Why Web Fonts Cause Layout Shift and How to Avoid It
- How to Change the WordPress Admin Font
- Choosing Google Fonts for a WordPress Login Page
- CDN vs. Self-Hosted Assets in WordPress
- WordPress Privacy and Third-Party Requests
- wp_enqueue_scripts explained
- PX vs. EM vs. REM CSS Units Explained
Final thoughts
Uploading a custom font to WordPress is now much easier than it was in older versions of the platform.
For most current installations, the native Font Library provides the simplest workflow:
Appearance
→ Fonts
→ Upload
→ install required variants
→ apply through typography settings
Developers still have more controlled options through theme.json and conventional @font-face declarations.
The best method depends on whether the font belongs to the site, the active theme or a custom application layer.
Whatever method you choose, do not stop at successfully uploading the file.
A good WordPress font implementation also considers:
- licensing;
- file format;
- font weights;
- font styles;
- fallbacks;
- loading behavior;
- layout stability;
- network transfer;
- caching;
- editor consistency.
A carefully selected WOFF2 family with only the required variants can provide strong brand consistency without creating unnecessary page weight.
A poorly configured family with ten unused weights, duplicate remote requests and mismatched fallbacks can do the opposite.
Upload what the design actually needs, register each face correctly, let WordPress manage typography where possible and verify the final implementation in the browser rather than assuming that an uploaded font is automatically an optimized font.

