Most hreflang problems on an English and Portuguese site come down to five things: missing return links, wrong language codes, canonicals that point at the wrong language, automatic language redirects, and sitemaps that disagree with the page tags. Fix those and the rest is detail.
Everything below is based on Google's own documentation on localized versions. This post covers Google only; if other search engines matter to you, check their own documentation.
What does hreflang actually do?
Hreflang is an annotation that says: this page has an equivalent in another language or region, and here is where it is. Google uses it to show searchers the version that fits them. It does not make a page rank better, and it does not translate anything.
You can declare it in three places: HTML <link> tags in the head, an HTTP Link header, or your XML sitemap. Google says the three methods are equivalent, so pick the one that is easiest to keep correct. Do not mix methods carelessly, because then you have to keep them all in agreement.
A minimal English and Brazilian Portuguese pair in HTML looks like this, and the same block goes on both pages:
<link rel="alternate" hreflang="en" href="https://example.com/en/services/" /><link rel="alternate" hreflang="pt-BR" href="https://example.com/pt/servicos/" /><link rel="alternate" hreflang="x-default" href="https://example.com/" />
Why are my return links not working?
Return links are the number one cause of ignored hreflang. Google states that if two pages do not both point to each other, the tags will be ignored. Each language version must list itself as well as every other version.
Common ways this goes wrong:
- The English page lists the Portuguese page, but the Portuguese page has no annotations at all.
- The Portuguese page was edited or its slug changed and the English page still points to the old address.
- A page exists in English only, but a template adds a Portuguese alternate anyway, which then returns a 404 or redirects.
- A page lists the other version but forgets to list itself.
The fix is mechanical. Generate the annotations from one source of truth (for example, a translation group in your CMS) rather than typing them per page. A single-language page should have no alternate at all, only a self-referencing canonical.
Which language codes are right, and what about x-default?
Google accepts a language code (ISO 639-1) and, optionally, a region code (ISO 3166-1 Alpha 2), joined by a dash. A region on its own is invalid. For a Brazilian Portuguese audience, pt-BR is the correct form; use plain pt if one Portuguese version serves all Portuguese speakers. Use en for English unless you really have separate versions for, say, the UK and the US.
Mistakes seen over and over:
- Writing
pt_BRwith an underscore. Google's format is language, dash, region. Use the dash. - Using
bralone (that is a country, not a language). - Using
en-UK. Google listsUKamong the reserved or incorrect codes. The valid region for the United Kingdom isGB, soen-GB. - Relative URLs. Google requires fully qualified URLs, including
https://.
On x-default: it is a fallback for users whose language setting matches none of your versions. Google says it works best on language selector pages. If your root address redirects visitors to one language, consider whether it is a proper fallback. Using it is optional; if you cannot name a sensible fallback page, leave it out.
What if my canonical and hreflang disagree?
A canonical tells Google the preferred URL for a piece of content. Hreflang links between different pages that are equivalents. They have to agree.
The usual error is a Portuguese page whose canonical points to the English page, perhaps because a plugin or a template used one canonical for the whole translation group. That sends Google two contradictory signals about the same Portuguese page, and you should not expect the hreflang to work. Google's canonical guidance says to include a self-referencing canonical on the canonical page itself, to use absolute URLs, and not to send conflicting canonical signals. With hreflang, specify a canonical page in the same language.
The rule for your build: every language version canonicalises to itself.
Related: do not redirect visitors automatically by browser language. Google advises avoiding automatic redirects from one language version to another, because neither users nor search engines can then reach all versions. Show a link to the other language instead.
Subfolders or separate domains?
Google lists three workable structures:
- Country domains (
example.com.br): clear geotargeting, but cost and availability are drawbacks. - Subdomains (
pt.example.com): easy to set up and allow different server locations, but users may not recognise the geotargeting from the URL. - Subdirectories (
example.com/pt/): easy to set up, low maintenance on one host.
For a small business site we would pick subfolders: one domain, one hosting setup, one Search Console property to watch. Choose a country domain if you already operate a legal or commercial presence in Brazil and want that signal to be unmistakable. URL parameters (?lang=pt) are not recommended by Google.
Should translated pages have translated slugs?
You can. Hreflang points to full URLs, so /en/services/ and /pt/servicos/ can differ without problem, as long as each is listed in both annotations. Translated slugs help readers, and Portuguese searchers will see Portuguese words in the address.
The cost is bookkeeping: every translated slug must be stored with its translation group, and the language switcher has to link to the matching page, not to the same slug under another prefix. If your switcher just swaps /en/ for /pt/, translated slugs will produce broken links. Either keep the slugs identical, or make the switcher slug-aware.
How do I put hreflang in the sitemap?
Google supports xhtml:link entries inside the sitemap, listing every alternate under each <url>, itself included:
<url> <loc>https://example.com/en/services/</loc> <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/services/" /> <xhtml:link rel="alternate" hreflang="pt-BR" href="https://example.com/pt/servicos/" /></url><url> <loc>https://example.com/pt/servicos/</loc> <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/services/" /> <xhtml:link rel="alternate" hreflang="pt-BR" href="https://example.com/pt/servicos/" /></url>
Each page needs its own <url> entry with the full set. Keep to Google's sitemap rules: absolute URLs, a limit of 50,000 URLs or 50 MB per file, and a lastmod date only when the content really changed.
If you use both HTML tags and sitemap annotations, they must say the same thing.
A checklist to run on your site
- Pick one place to generate hreflang and stop hand-writing it.
- Every annotated page lists itself and all alternates, and every alternate links back.
- Codes are
enandpt-BR(orpt), with a dash and no invented values. - All URLs are absolute, return 200 and are not redirects.
- Each version has a self-referencing canonical in its own language.
- Pages that exist in one language only have no hreflang alternates.
- No automatic redirect by browser language; a visible language link instead.
- The sitemap and the page tags agree.
- Crawl the site and check for any alternate that returns a non-200 status.
- After launch, watch the Search Console Page indexing report for pages from either language that are not indexed.
When this is not for you
If your site exists in one language, skip all of this. If your Portuguese pages are a machine translation nobody reviewed, hreflang will not rescue them; fix the content first. And if you run a tiny two-language site with a handful of pages, hand-written tags can be fine; this level of tooling is for sites that will keep growing.
Next step
If you want a bilingual site built with correct language handling from day one, or an existing one checked, book a free call with Kevin. See our website development service or go to contact.



