Journal
9 min readEvery canonical URL pointed at a redirect.
Every canonical URL on our site pointed at a host that redirected. 307 is temporary, 308 is permanent, and the difference matters for entity resolution.
- canonical URL
- 307 vs 308 redirect
- apex vs www domain
- domain canonicalisation
All 28 <link rel="canonical"> tags on this site named https://hikaribuilds.com. So did all 28 sitemap <loc> entries, every og:url, both JSON-LD @id values, and the Host: and Sitemap: lines in robots.txt.
The site served from www.hikaribuilds.com. The apex returned a 307 to www.
Every page declared "my real address is X" while answering from Y, and X bounced to Y. A canonical tag is a declaration of identity, not a suggestion. Ours named a door that did not open.
We found this on our own site. That is the reason to write it up.
What does a canonical pointing at a redirect do?
It makes a crawler do extra work to reach an answer it was told was authoritative, and it makes that answer arrive from a different hostname than the one requested.
The immediate reading is not catastrophic. Search engines follow redirects, and a canonical that resolves through one hop is usually honoured. The problem is what the arrangement communicates over time. Two hostnames both serve the site. One is named in every identity signal the site emits. The other is the one that actually responds. Link equity and crawl budget divide across both, because from the outside there is no single settled address.
This is the quiet kind of fault. Nothing 404s, so nothing pages anyone. The site works perfectly for humans and reports two different identities to machines.
Why is 307 the wrong redirect for a domain?
Because 307 means temporary, and it means it in a way that has consequences.
A 307 tells a client the resource lives at the original URL and has been moved somewhere else for now. The instruction to a crawler is: keep the original indexed, come back and check again, this arrangement may change. That is a reasonable thing to say during a migration or a maintenance window. It is the wrong thing to say about which hostname your website permanently lives on.
| Code | What it tells a crawler | Right for domain canonicalisation |
|---|---|---|
| 301 | Moved permanently, method may change on the follow | Yes, the long-standing default |
| 302 | Found, temporary | No |
| 307 | Temporary, request method preserved | No |
| 308 | Permanent, request method preserved | Yes, the modern equivalent of 301 |
Permanent canonicalisation needs a 308 or a 301. The distinction is the whole point of this post, because 307 is what a lot of platform defaults hand you when a domain is attached but not chosen as primary. It looks like a redirect is configured. It is, and it is the wrong one.
What broke in the structured data?
This is the cost that mattered here, and it is the one most easily missed.
The site carries JSON-LD for an Organization and a Person. Both are anchored by @id values, and both @id values were fragments on https://hikaribuilds.com. Those identifiers are not decorative. They are the join key that lets a consumer of the markup say: the Person referenced as author on this article is the same Person described on the about page, and that Person is affiliated with this Organization.
Entity resolution depends on stable, resolvable identifiers. Ours were stable, they were not resolvable. Every graph node the site published pointed at a hostname that answered with a redirect rather than a document.
Two hostnames each carrying a full copy of the markup, with the authoritative identifier on the one that does not serve, is a weaker claim than one hostname carrying it once. We could not measure the damage and we are not going to guess at it. We could see the defect plainly, and that was enough to fix.
How do you check this on your own site?
Three commands, all curl.
Ask each hostname what it returns:
curl -sI https://example.com/ | head -n 1
curl -sI https://www.example.com/ | head -n 1
One of those should be 200. The other should be 308 or 301. If either is 307 or 302, that is the finding.
Then count the hops, because a redirect chain is a separate problem from a redirect code:
curl -sIL -o /dev/null -w '%{num_redirects} %{url_effective}\n' https://example.com/
The number should be 0 or 1. Anything higher means the hostname redirect and some other rule — a trailing slash, a locale prefix, an HTTP-to-HTTPS step — are stacking.
Then check that what the pages declare matches what the server serves:
curl -s https://example.com/sitemap.xml \
| grep -o '<loc>[^<]*' | sed 's/<loc>//' \
| while read -r url; do
printf '%s %s\n' "$(curl -sI -o /dev/null -w '%{http_code}' "$url")" "$url"
done
Every line should read 200. A 301 or 308 in that column means the sitemap is advertising URLs that are not the URLs. Do the same for the href in each canonical tag, then compare the hostname against the one you just confirmed returns 200.
We have no figure for how common this is. Ours failed silently, after a domain was added to a host and never revisited.
What order do the fixes go in?
DNS first. The platform flip second. Getting that backwards makes things worse before it makes them better.
Our DNS was managed at Namecheap, not at Vercel, which is the detail that let the fault survive. The apex A record pointed at an IP address Vercel no longer accepted. The dashboard said so: apex flagged "DNS Change Required", www showing "Valid Configuration". The platform had been reporting the problem for months, in a panel nobody opened.
Flipping the primary domain to the apex before repairing that A record would have pointed both hostnames at a host the platform considered misconfigured. The site would have gone from a working configuration with a bad signal to a broken configuration with the same bad signal. So the order was: correct the apex A record at the registrar, confirm the apex resolved and the dashboard agreed, then change which hostname the project treats as primary.
Sequencing is the part of this work that does not show up in a checklist. Each step is trivial. The order is not.
Why did renaming the domain fail?
Because the target name was already taken by the same project.
The instinct was to rename www.hikaribuilds.com to hikaribuilds.com and be done. Vercel refused: "Cannot add hikaribuilds.com since it's already in use by one of your projects." Both hostnames were already attached, so there was nothing to rename into.
The correct move was smaller. Set www to "Redirect to Another Domain", target the apex, status 308. Not a rename, a redirect. The www hostname stays attached and keeps working for anyone who has it bookmarked or linked, it simply stops claiming to be the site.
That is the general shape of the fix. You are not removing a hostname. You are deciding which one is the site, and telling the other one to say so permanently.
What did we verify after the change?
Every claim below was checked with curl after the deploy, not inferred from a dashboard.
| Check | Result |
|---|---|
https://hikaribuilds.com/ | 200 |
https://www.hikaribuilds.com/ | 308 to apex |
www → /services/saas-products | path preserved, one hop |
www → a /blog/ path | path preserved, one hop |
| Every canonical URL | 200, no redirect |
| Every sitemap URL | 200 |
Path preservation is worth checking rather than assuming. A hostname redirect that drops the path sends every deep link to the homepage, which is a worse outcome than the fault you started with. Both a service page and a blog post carried through intact.
The hop count matters for the same reason. Exactly one redirect, on every path tested. Not one plus a trailing-slash correction, not one plus an HTTPS upgrade.
The sitemap holds 30 <loc> entries — 8 static pages, 7 services, 7 blog posts, 8 case studies. All 30 returned 200 from the apex, with no redirect in between.
What is still unresolved?
The redirect flipped in a single deploy last week. Nothing has moved in search results, and nothing could have.
Recrawling and reindexing run on their own schedule, and a hostname consolidation is exactly the kind of change that takes weeks to settle rather than hours. We have no before-and-after ranking data and no coverage comparison to publish. A canonical fix credited with a ranking movement inside a week did not cause it.
What we do not know yet:
How the inbound links divide. Some links pointing here name www, others name the apex. We have not counted them. [NEED: referring domains split by hostname]
How long the site was in this state. The apex was named in every identity signal we inspected, and when the 307 arrangement started is not something we have established. [NEED: date the apex stopped serving]
Whether the entity graph reconsolidates. The @id values now resolve to documents that return 200. Whether the Organization and Person entities settle around those identifiers, and on what timescale, is not observable from our side.
Which external profiles use which hostname. Directory listings, review profiles and social bios were written at different times, and we have not audited them for consistency. The 308 handles them correctly. Consistency would still be better than correctness after a bounce. [NEED: audit of outbound profile links]
The www redirect stays in place permanently. There is no cleanup step where we remove it, because that hostname will keep receiving traffic from links written years ago, and answering those with one permanent hop is the entire job.
What we can report is the configuration. The consequence is a coverage report six weeks out, listing either one hostname or two. That is the number we will publish, and it is the only one that settles this.