Skip to content

Preconnect & DNS-Prefetch — Early Connection Setup for Third-Party Origins

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Preconnect & DNS. We cover key concepts, practical examples, and best practices to help you master this topic.

Preconnect and DNS-Prefetch establish early connections to external origins, reducing DNS and TCP handshake latency for third-party resources.

What You'll Learn

By the end of this tutorial, you'll understand the difference between DNS-Prefetch, Preconnect, and Preload, how to implement early connection hints for third-party origins, and how to measure the performance impact.

Why It Matters

Every third-party request — fonts, analytics, CDN resources, embeds — requires DNS resolution, TCP handshake, and TLS negotiation. These steps add 100–500ms of latency before any data transfers. Preconnect and DNS-Prefetch move this work to idle time, so when the browser encounters the resource, the connection is already established.

Real-World Use

A news site loads analytics from Plausible, fonts from Google Fonts, images from a CDN, and a video embed from YouTube. With Preconnect hints, the DNS and TCP handshake for each origin starts before the HTML parser discovers the resources. Page load time drops by 250ms, and the Largest Contentful Paint improves by 15%.

Connection Setup Flow

graph LR
    A[Browser Parses HTML] --> B{Connection Hint Found?}
    B -->|DNS-Prefetch| C[Resolve DNS
~50ms saved] B -->|Preconnect| D[Resolve DNS + TCP + TLS
~300ms saved] B -->|No Hint| E[Wait until resource discovered] C --> F[Resource request
uses cached DNS] D --> G[Resource request
uses open connection] E --> H[DNS + TCP + TLS
full latency] style B fill:#4a90d9,color:#fff style D fill:#e74c3c,color:#fff style E fill:#f39c12,color:#fff

DNS-Prefetch Implementation

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>DNS-Prefetch Examples</title>

    <!-- DNS-Prefetch for external origins -->
    <link rel="dns-prefetch" href="//fonts.googleapis.com">
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link rel="dns-prefetch" href="//www.google-analytics.com">
    <link rel="dns-prefetch" href="//images.example.com">
    <link rel="dns-prefetch" href="//api.example.com">
</head>
<body>
    <p>DNS resolution for external origins starts immediately.</p>
</body>
</html>

DNS-Prefetch is the lightest hint. It only resolves the domain name to an IP address. It works in all browsers and consumes minimal overhead. Use it for every third-party origin your page references.

Preconnect Implementation

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Preconnect Examples</title>

    <!-- Preconnect: DNS + TCP + TLS -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

    <!-- Preconnect to analytics -->
    <link rel="preconnect" href="https://plausible.io">

    <!-- Preconnect to API -->
    <link rel="preconnect" href="https://api.example.com">

    <!-- Preconnect to image CDN -->
    <link rel="preconnect" href="https://cdn.example.com">

    <!-- Fallback: DNS-Prefetch in older browsers -->
    <link rel="dns-prefetch" href="https://fonts.gstatic.com">
</head>
<body>
    <p>Connections to external origins begin immediately.</p>
</body>
</html>

Preconnect goes further than DNS-Prefetch. It performs DNS resolution, TCP handshake, and TLS negotiation. The connection is fully established and ready for data transfer when the browser needs it. Use Preconnect for the most critical third-party origins — limit to 3–6 origins per page.

Dynamic Preconnect for User Interactions

// utils/early-connect.js — Establish connections on user intent
class ConnectionManager {
    constructor() {
        this.connectedOrigins = new Set();
    }

    preconnect(origin, options = {}) {
        if (this.connectedOrigins.has(origin)) return;
        this.connectedOrigins.add(origin);

        const link = document.createElement('link');
        link.rel = 'preconnect';
        link.href = origin;
        if (options.crossorigin) link.crossOrigin = 'anonymous';
        document.head.appendChild(link);
    }

    dnsPrefetch(origin) {
        if (this.connectedOrigins.has(origin)) return;
        this.connectedOrigins.add(origin);

        const link = document.createElement('link');
        link.rel = 'dns-prefetch';
        link.href = origin;
        document.head.appendChild(link);
    }

    // Preconnect on hover — predict user navigation
    onHover(selector, origin) {
        document.querySelectorAll(selector).forEach(el => {
            el.addEventListener('mouseenter', () => {
                this.preconnect(origin);
            }, { once: true });

            el.addEventListener('touchstart', () => {
                this.preconnect(origin);
            }, { once: true });
        });
    }
}

const connectionManager = new ConnectionManager();

// Preconnect to critical origins on page load
connectionManager.preconnect('https://fonts.googleapis.com', { crossorigin: true });
connectionManager.dnsPrefetch('https://images.example.com');

// Preconnect to payment API when user focuses checkout button
connectionManager.onHover('#checkout-btn', 'https://payments.example.com');

// Preconnect to video CDN when user approaches video section
const videoSection = document.getElementById('video-section');
const videoObserver = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            connectionManager.preconnect('https://videos.example.com');
            videoObserver.disconnect();
        }
    });
});
videoObserver.observe(document.querySelector('#video-section'));

console.log('Connection hints established for:', [...connectionManager.connectedOrigins]);

Measuring Impact

// Measure connection times with Performance API
async function measureConnections(origins) {
    const results = [];

    for (const origin of origins) {
        const start = performance.now();

        try {
            // Attempt a fetch to measure actual connection time
            const controller = new AbortController();
            const timeout = setTimeout(() => controller.abort(), 5000);

            await fetch(`${origin}/health`, {
                method: 'HEAD',
                mode: 'no-cors',
                signal: controller.signal
            });

            clearTimeout(timeout);
            const elapsed = performance.now() - start;
            results.push({ origin, time: `${elapsed.toFixed(0)}ms`, status: 'Connected' });
        } catch (err) {
            results.push({ origin, time: 'N/A', status: `Error: ${err.message}` });
        }
    }

    console.table(results);
    return results;
}

// Usage
measureConnections([
    'https://fonts.googleapis.com',
    'https://plausible.io',
    'https://api.example.com'
]);

// Check if Resource Timing API has preconnect info
function reportPreconnectSavings() {
    if (!performance.getEntriesByType) {
        console.log('Resource Timing API not supported');
        return;
    }

    const resources = performance.getEntriesByType('resource');
    const preconnectSavings = resources
        .filter(r => r.initiatorType === 'link' && r.name.includes('preconnect'))
        .map(r => ({
            url: r.name,
            dnsTime: `${r.domainLookupEnd - r.domainLookupStart}ms`,
            tcpTime: `${r.connectEnd - r.connectStart}ms`,
            tlsTime: `${r.secureConnectionStart > 0 ? r.connectEnd - r.secureConnectionStart : 0}ms`,
            total: `${r.connectEnd - r.connectStart}ms`
        }));

    console.table(preconnectSavings);
}

Common Mistakes

  1. Preconnecting to too many origins. Each preconnect opens and holds a TCP connection. More than 6 preconnects wastes resources. Prioritize the 3–5 most critical origins.
  2. Missing crossorigin for CORS origins. Fonts and other CORS resources need crossorigin on the preconnect. Without it, the browser creates a non-CORS connection, then opens a separate CORS connection when the actual fetch happens.
  3. Preconnect without a fallback. Some browsers (Safari <11, older mobile) don't support preconnect. Always pair preconnect with dns-prefetch as a fallback.
  4. Preconnecting to origins that load small resources. A 2KB analytics pixel doesn't justify a preconnect. Reserve preconnect for origins delivering 50KB+ of critical resources.
  5. Preconnecting too late in the head. Preconnect hints should be among the first elements in the <head>. If they appear after CSS imports, the connection setup doesn't start early enough.

Practice Questions

  1. What is the difference between DNS-Prefetch and Preconnect?
  2. When should you use Preconnect instead of DNS-Prefetch?
  3. Why do font origins need the crossorigin attribute on Preconnect?
  4. How many preconnects is too many for a typical page?
  5. How can you measure the time saved by Preconnect?

Challenge: Implement a connection hint Strategy for a page that loads fonts from Google Fonts, analytics from a self-hosted endpoint, images from a CDN, and a video embed from YouTube. Use Preconnect for the 3 most critical origins, DNS-Prefetch for the rest, and demonstrate the time savings using the Performance API.

FAQ

Does DNS-Prefetch work in all browsers?

Yes. DNS-Prefetch is supported in all major browsers including Chrome, Firefox, Safari, Edge, and older mobile browsers. It is the most widely supported connection hint.

Can Preconnect reduce time-to-first-byte?

Not directly. Preconnect affects the connection phase, not the server response time. It reduces the latency before the first byte starts transferring, which improves overall load time but not TTFB itself.

Should I preconnect to my own origin?

No. The browser automatically resolves and connects to the document origin. Preconnecting to the same origin is redundant.

Does HTTPS affect Preconnect behavior?

Yes. For HTTPS origins, Preconnect performs the full TLS handshake in addition to DNS resolution and the TCP handshake. This makes Preconnect especially valuable for secure origins.

How do I verify preconnect is working?

Use Chrome DevTools Network panel: filter by 'preconnect' and the timing tab should show 0ms for DNS, TCP, and TLS on subsequent requests. Also check the Performance panel for reduced connect times.

Mini Project

Build a connection hint analyzer: create a page that loads resources from 5+ external origins, implements Preconnect and DNS-Prefetch for each, measures DNS/TCP/TLS timing with the Performance API, visualizes the time saved compared to loading without hints, and generates a report of optimal origins to preconnect.

What's Next

You've mastered Preconnect and DNS-Prefetch. Next, learn about Font Lazy Loading to defer web font loading and eliminate flash-of-invisible-text (FOIT).

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro