Would you like a fast, cheap, and simple way to deploy hreflang tags without developer support? If so, I have a Cloudflare Workers script that might be just what you need!
This approach is perfect if all your locales are on the same domain. Please note that it hasn’t been tested on multi-domain setups, so your mileage may vary there.
The Script
Also, ensure your canonical tags are correctly implemented, as the script relies on them to validate hreflang injection.
This approach is perfect if all your locales are on the same domain. Please note that it hasn’t been tested on multi-domain setups, so your mileage may vary there.
How It Works
- Identifies the Canonical Tag
- The script reads the HTML of your pages to find the canonical tag and validates that the URI in the canonical tag matches the URI of the page request. This ensures hreflang tags are only added to canonical pages.
- Skips Non-Indexable Pages
- It avoids injecting hreflang tags on pages with noindex meta tags, query parameters, tracking codes, or redirects. This keeps your hreflang implementation clean and focused on indexable content.
- Inserts Hreflang Tags into the <head>
- On validated canonical pages, the script dynamically generates the appropriate hreflang tags for your supported locales and inserts them directly into the <head> section of the HTML.
Why Use This Script?
- Cost-Effective: You don’t need to hire developers or update server-side configurations to manage hreflang tags.
- Quick Deployment: A Cloudflare Worker can be set up and running in minutes, giving you immediate control over hreflang tags.
- Dynamic and Flexible: The script adapts to your page structure, automatically aligning hreflang tags with canonical URLs.
The Script
Code:
export default {
async fetch(request) {
const response = await fetch(request);
const contentType = response.headers.get('Content-Type') || '';
// Only process HTML responses
if (contentType.includes('text/html')) {
const html = await response.text();
const modifiedHtml = injectHreflangTags(html, request);
return new Response(modifiedHtml, {
headers: response.headers,
status: response.status,
});
}
return response;
},
};
function injectHreflangTags(html, request) {
const url = new URL(request.url);
// Parse canonical and noindex meta tags
let canonicalUrl = null;
let hasNoindex = false;
html.replace(/<link rel=["']canonical["'] href=["']([^"']+)["']|<meta\s+name=["']robots["']\s+content=["'].*noindex.*["']/gi, (match, canonical) => {
if (canonical) canonicalUrl = canonical;
if (match.includes('noindex')) hasNoindex = true;
});
// Skip if noindex meta is present
if (hasNoindex) {
console.log(`Skipping hreflang tags for: ${url.href} (Reason: noindex tag found)`);
return html;
}
// Normalize URLs for comparison
function normalizeUrl(url) {
return url.replace(/\/+$/, '').toLowerCase();
}
if (!canonicalUrl || normalizeUrl(canonicalUrl) !== normalizeUrl(url.href)) {
console.log(`Skipping hreflang tags for: ${url.href} (Canonical: ${canonicalUrl || "not found"})`);
return html;
}
// Define hreflang mappings - you can add to the array for other locales
const hreflangMappings = {
"en-us": `https://yourdomain.com/en-us${url.pathname}`,
"en-eu": `https:///yourdomain.com/en-eu${url.pathname}`,
"en-gb": `https:///yourdomain.com/en-gb${url.pathname}`,
"x-default": `https:///yourdomain.com${url.pathname}`,
};
const hreflangTags = Object.entries(hreflangMappings)
.map(([lang, href]) => `<link rel="alternate" hreflang="${lang}" href="${href}" />`)
.join("\n");
return html.replace("</head>", `${hreflangTags}</head>`);
}
Considerations
This script assumes all locales are served from the same domain (e.g., https://example.com/en-us/, https://example.com/en-gb/, etc.). If you’re working with multi-domain setups, additional testing or modifications may be required.Also, ensure your canonical tags are correctly implemented, as the script relies on them to validate hreflang injection.
Last edited: