惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Commits to openclaw:main
Recent Commits to openclaw:main
L
LangChain Blog
月光博客
月光博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 【当耐特】
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
博客园_首页
T
Tailwind CSS Blog
P
Proofpoint News Feed
雷峰网
雷峰网
D
Darknet – Hacking Tools, Hacker News & Cyber Security
IT之家
IT之家
V
Vulnerabilities – Threatpost
阮一峰的网络日志
阮一峰的网络日志
C
CERT Recently Published Vulnerability Notes
Attack and Defense Labs
Attack and Defense Labs
S
Schneier on Security
Security Archives - TechRepublic
Security Archives - TechRepublic
L
Lohrmann on Cybersecurity
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
WordPress大学
WordPress大学
The Register - Security
The Register - Security
N
Netflix TechBlog - Medium
Hugging Face - Blog
Hugging Face - Blog
Project Zero
Project Zero
博客园 - 叶小钗
F
Full Disclosure
大猫的无限游戏
大猫的无限游戏
Latest news
Latest news
S
SegmentFault 最新的问题
C
Cyber Attacks, Cyber Crime and Cyber Security
Google Online Security Blog
Google Online Security Blog
Recorded Future
Recorded Future
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
L
LINUX DO - 最新话题
Google DeepMind News
Google DeepMind News
P
Privacy International News Feed
I
InfoQ
F
Fortinet All Blogs
Vercel News
Vercel News
H
Hackread – Cybersecurity News, Data Breaches, AI and More
T
Threatpost
T
Tenable Blog
B
Blog RSS Feed

CSS Wizardry

Front-End’s Missing Metric: The TBT Window Meet Your Users Where They Are with Obs.js Better Browser Caching with No-Vary-Search font-family Doesn’t Fall Back the Way You Think What Is CSS Containment and How Can I Use It? When All You Can Do Is All or Nothing, Do Nothing Obs.js: Context-Aware Web Performance for Everyone Low- and Mid-Tier Mobile for the Real World (2025) The Fastest Site in the Tour de France Making Sense of the Performance Extensibility API Why Do We Have a Cache-Control Request Header? HTML Is Not a Programming Language… Build for the Web, Build on the Web, Build with the Web Licensing Code on CSS Wizardry A Layered Approach to Speculation Rules Designing (and Evolving) a New Web Performance Score Core Web Vitals Colours The Ultimate Contract Templates for Tech Consultants: Protect Your Business and Get Paid Optimising for High Latency Environments Cache Grab: How Much Are You Leaving on the Table? blocking=render: Why would you do that?! The Three Cs: 🤝 Concatenate, 🗜️ Compress, 🗳️ Cache What Is the Maximum max-age? How to Clear Cache and Cookies on a Customer’s Device The Ultimate Low-Quality Image Placeholder Technique Core Web Vitals for Search Engine Optimisation: What Do We Need to Know? The HTTP/1-liness of HTTP/2 In Defence of DOM­Content­Loaded Site-Speed Topography Remapped Why Not document.write()? Speeding Up Async Snippets Critical CSS? Not So Fast! Measure What You Impact, Not What You Influence Optimising Largest Contentful Paint Measuring Web Performance in Mobile Safari Site-Speed Topography Speed Up Google Fonts Real-World Effectiveness of Brotli Performance Budgets, Pragmatically Lazy Pre-Browsing with Prefetch Making Cloud.typography Fast(er) Time to First Byte: What It Is and How to Improve It Self-Host Your Static Assets Tips for Technical Interviews Cache-Control for Civilians Bandwidth or Latency: When to Optimise for Which ITCSS × Skillshare What If? CSS and Network Performance The Three Types of Performance Testing Getting to Know a Legacy Codebase Image Inconsistencies: How and When Browsers Download Images Identifying, Auditing, and Discussing Third Parties My Digital Music Setup Measuring the Hard-to-Measure Finding Dead CSS The Fallacies of Distributed Computing (Applied to Front-End Performance) Ten Years Old Relative Requirements Airplanes and Ashtrays Performance and Resilience: Stress-Testing Third Parties Refactoring Tunnels Little Things I Like to Do with Git Writing Tidy Code Configuring Git and Vim Base64 Encoding & Performance, Part 2: Gathering Data Base64 Encoding & Performance, Part 1: What’s Up with Base64? Code Smells in CSS Revisited Typography for Developers Moving CSS Wizardry onto HTTPS and HTTP/2 Ack for CSS Developers A New Year, a New Focus Preparing Vim for Apple’s Touch Bar Choosing the Correct Average CSS Shorthand Syntax Considered an Anti-Pattern CSS Wizardry Newsletter Nesting Your BEM? Improving Perceived Performance with Multiple Background Images Continue Normalising Your CSS Pure CSS Content Filter Pragmatic, Practical, and Progressive Theming with Custom Properties Refactoring CSS: The Three I’s Speaker’s Checklist: Before and After Your Talk Improving Your CSS with Parker The Importance of !important: Forcing Immutability in CSS Mixins Better for Performance Managing Typography on Large Apps White October Events Workshop Partnership BEMIT: Taking the BEM Naming Convention a Step Further Travelling Like You Want to, When You Have To Contextual Styling: UI Components, Nesting, and Implementation Detail Subtleties with Self-Chained Classes Cyclomatic Complexity: Logic in CSS Immutable CSS Can CSS Be Too Modular? More Transparent UI Code with Namespaces When to use @extend; when to use a mixin The Specificity Graph CSS Wizardry Ltd.: Year 1 in review CSS Guidelines 2.0.0
Correctly Configure (Pre) Connections
Harry Roberts · 2023-12-10 · via CSS Wizardry

Written by on CSS Wizardry.

Table of Contents

Independent writing is brought to you via my wonderful Supporters.

  1. Learn by Example
  2. Working Out Which Origins to preconnect
  3. Don’t preconnect Too Many Origins
  4. When to Use crossorigin
    1. Sec-* Request Headers
  5. preconnect and DNS

A trivial performance optimisation to help speed up third-party or other-origin requests is to preconnect them: hint that the browser should preemptively open a full connection (DNS, TCP, TLS) to the origin in question, for example:

<link rel=preconnect href=https://fonts.googleapis.com>

In the right circumstances, this simple, single line of HTML can make pages hundreds of milliseconds faster! But time and again, I see developers misconfiguring even this most basic of features. Because, as is often the case, there’s much more to this ‘basic feature’ than meets the eye. Let’s dive in…

Learn by Example

At the time of writing, the BBC News homepage (in the UK, at least) has these four preconnects defined early in the <head>:

<link rel=preconnect href=//static.bbc.co.uk crossorigin>
<link rel=preconnect href=//m.files.bbci.co.uk crossorigin>
<link rel=preconnect href=//nav.files.bbci.co.uk crossorigin>
<link rel=preconnect href=//ichef.bbci.co.uk crossorigin>

Readers on narrow screens should know that each of these preconnects also carries a crossorigin attribute—scroll along to see for yourself!

Note that the BBC use schemeless URLs (i.e. href=//…). I would not recommend doing this. Always force HTTPS when it’s available.

Having consulted for the BBC a number of times, I know that they make heavy use of internal subdomains to share resources across teams. While this suits developer ergonomics, it’s not great for performance, particularly in cases where the subdomain in question is on the critical path. Warming up connections to important origins is a must for the BBC.

However, a look at a waterfall tells me that none of these preconnects worked!

Above, you can see that the browser discovered references to each of these origins in the first chunk of HTML, before the 1-second mark. This is evidenced by the light white bars that denote ‘waiting’ time—the browser knows it needs the files, but is waiting to dispatch the requests. However, we can also see that the browser didn’t begin network negotiation until closer to the 1.5-second mark, when we begin seeing a tiny slither of green—DNS—followed by the much more costly TCP and TLS. What went wrong?!

Working Out Which Origins to preconnect

In the example above, we have five connections to the following four domains (more on that later):

  • nav.files.bbci.co.uk: On the critical path with render-blocking CSS.
  • static.files.bbci.co.uk: On the critical path with render-blocking CSS and JS.
  • m.files.bbci.co.uk: On the critical path with render-blocking CSS.
    • The screenshot above marks the CSS as non-blocking because of the way it’s fetched—it’s preloaded, which is non-blocking, but it’s then conditionally applied to the page using document.write() (which is its own performance faux pas in itself).
  • ichef.bbci.co.uk: Not on the critical path, but does host the homepage’s LCP element.

N.B. For neatness, I am omitting the https:// from written prose, but it is vital that you include the relevant scheme in your href attribute. All code examples are complete and correct.

Each of these four origins is vital to the page, so all four would be candidates for preconnect. However, the BBC aren’t attempting to preconnect static.files.bbci.co.uk at all; instead, they’re preconnecting static.bbc.co.uk, which is also used, but isn’t on the critical path. This feels more like a simple oversight or a typo than anything else.

As a rule, if the origin is important to the page and is used within the first five seconds of the page-load lifecycle, preconnect it. If the origin is not important, don’t preconnect it; if it is important but is used more than five seconds into the page load lifecycle, your priority should be moving it sooner.

Note that important is very subjective. Your analytics isn’t important; your chat client isn’t important. Your consent management platform is important; your image CDN is important.

One easy way to get an overview of early and important origins—and the method I use when advising clients—is to use WebPageTest. Once you’ve run a test, you can head to a Connection View of the waterfall which shows a diagram comprising entries per origin, not per response:

Note that some connections are actually shared across more than one domain: this is HTTP2’s connection coalescence, available when origins share the same IP address and certificates.

As easy as that—that’s your list of potential origins!

Don’t preconnect Too Many Origins

preconnect should be used sparingly. Connection overhead isn’t huge, but too many preconnects that either a) aren’t critical, or b) don’t get used at all, is definitely wasteful.

Flooding the network with unnecessary preconnects early in the page load lifecycle can steal valuable bandwidth that could have been given to more important resources—the overhead of certificates alone can exceed 3KB. Further, opening and persisting connections has a CPU overhead on both the client and the server. Lastly, Chrome will close a connection if it isn’t used within the first 10 seconds of being opened, so if you act too soon, you might end up doing it all over again anyway.

With preconnect, you should strive for as few as possible but as many as necessary. In fact, I would consider too many preconnects a code smell, and you probably ought to solve larger issues like self-hosting your static assets and reducing reliance on third parties in general.

When to Use crossorigin

Okay. Now it’s time to learn why the BBC’s preconnects weren’t working!

This is the third time I’ve seen this problem this month (and we’re only nine days in…). It stems from a misunderstanding around when to use crossorigin. I get the impression that developers think ‘this request is going to another origin, so it must need the crossorigin attribute’. But that’s not what the attribute is for—crossorigin is used to define the CORS policy for the request. crossorigin=anonymous (or a bare crossorigin attribute) will never exchange any user credentials (e.g. cookies); crossorigin=use-credentials will always exchange credentials. Unless you know that you need it, you almost never need the latter. But when do we use the former?

If the resulting request for a file would be CORS-enabled, you would need crossorigin on the corresponding preconnect. Unfortunately, CORS isn’t the most straightforward thing in the world. Fortunately, I have a shortcut…

Firstly, identify a file on the origin that you’re considering preconnecting. For example, let’s take a look at the BBC’s box.css. In DevTools (or WebPageTest if you already have one available—you don’t need to run one just for this task), look at the resource’s request headers:

There it is right there: Sec-Fetch-Mode: no-cors.

The preconnect for nav.files.bbci.co.uk doesn’t currently (I’ll come back to that shortly) need a crossorigin attribute:

<link rel=preconnect href=https://nav.files.bbci.co.uk>

Let’s look at another request. orbit-v5-ltr.min.css from static.files.bbci.co.uk also carries a Sec-Fetch-Mode: no-cors request header, so that won’t need crossorigin either:

<link rel=preconnect href=https://nav.files.bbci.co.uk>
<link rel=preconnect href=https://static.files.bbci.co.uk>

Let’s keep looking.

How about the font BBCReithSans_W_Rg.woff2 also from static.files.bbci.co.uk?

Hmm. This does need crossorigin as it’s marked Sec-Fetch-Mode: cors. What do we do here?

Simple!

<link rel=preconnect href=https://nav.files.bbci.co.uk>
<link rel=preconnect href=https://static.files.bbci.co.uk>
<link rel=preconnect href=https://static.files.bbci.co.uk crossorigin>

We just add a second preconnect to open an additional CORS-enabled connection to static.files.bbci.co.uk. (Remember earlier when the browser had opened five connections to four origins? One of them was CORS-enabled!)

Let’s keep going and see where we end up…

As it stands, the very specific example of the homepage right now, needs the following preconnects. Notice that all origins didn’t need crossorigin, except static.files.bbci.co.uk which needed both:

<link rel=preconnect href=https://nav.files.bbci.co.uk>
<link rel=preconnect href=https://static.files.bbci.co.uk>
<link rel=preconnect href=https://static.files.bbci.co.uk crossorigin>
<link rel=preconnect href=https://m.files.bbci.co.uk>
<link rel=preconnect href=https://ichef.bbci.co.uk>

This feels comfortable! The browser naturally opened five connections, so I’m happy to see that we’ve also landed on five preconnects; nothing is unaccounted for.

I’d recommend familiarising yourself with the entire suite of Sec-* headers—they’re incredibly useful debugging tools.

preconnect and DNS

Because DNS is simply IP resolution, it is unaffected by anything CORS-related. This means that:

  1. If you have mistakenly configured your preconnects to use or omit crossorigin when you should have actually omitted or used crossorigin, the DNS step can still be reused—only the TCP and TLS need discarding and doing again. That said, DNS is usually—by far—the fastest part of the process anyway, so speeding it up while missing out on TCP and TLS isn’t much of an optimisation to celebrate.
  2. If you have everything configured correctly, or you aren’t using preconnect at all, you’ll actually see the browser reusing the DNS resolution for a subsequent request that needs a different CORS mode. If you zoom right in on this abridged waterfall, you’ll see that the second CORS-enabled request to static.files.bbci.co.uk doesn’t incur any DNS at all:

Frequently Asked Questions

What does preconnect do?

preconnect tells the browser to set up the connection to another origin early by performing DNS lookup, TCP connection setup, and usually TLS negotiation before the resource itself is requested.

What is the difference between preconnect and dns-prefetch?

dns-prefetch only resolves the hostname. preconnect goes further by also opening the transport connection, which makes it more powerful but also more expensive.

Should I preconnect to every third-party origin?

No. preconnecting to too many origins can waste resources and reduce the benefit, so it is best reserved for a small number of genuinely important, early-needed connections.

When should I use crossorigin with preconnect?

You should use crossorigin when the eventual request will need a CORS-enabled connection, such as many font requests, so the warmed connection can be reused properly.

Can too many preconnects hurt performance?

Yes. Because preconnect is relatively expensive, overusing it can create unnecessary work and compete with more important requests.