I want to have a seemingly simple outcome:
- website in two languages with URL structure like
/en/blog/article1and/de/blog/seite1 - content of the page is visible to search engines for SEO purposes
It’s not much, but it turns out to be incredibly difficult to achieve.
Problem
- I’m getting urls like this in SSR (Server-side rendering) output:
/blogand/blog/article1 - When react starts working in the browser, those URLs are corrected into
/de/blogand/de/blog/setie1. So to users and to me during development it looks like everything is fine - However, since google sees
/blogand/blog/article1, it tries to visit those /blogis redirected to/de/blogwhich is problematic but works. The real issue is with/blog/article1which is redirected to default locale/de/blog/article1and throws404, since in German the correct URL is/de/blog/seite1(article1 post doesn’t exist there).
Stack
Stack is Laravel 11 + Inertia React with SSR enabled. This setup works without issues until you start with localization.
For localization I’ve installed https://github.com/mcamara/laravel-localization.
Hacky fix
I found that incorrect URLs come from this route function definition in ssr.jsx:
import { Ziggy } from '@/ziggy';
global.route = (name, params, absolute, config = Ziggy)
=> route(name, params, absolute, config);
After days of trying to sort it out, I managed to pull localized URLs into SSR by pulling them from the props where they are put from HandleInertiaRequest middleware:
$ziggy = new Ziggy($group = null, $request->url());
return [
// Add in Ziggy routes for SSR
'ziggy' => $ziggy->toArray(),
...
]
And in ssr.jsx:
const ziggyConfig = { ...props.initialPage.props.ziggy };
global.route = (name, params, absolute)
=> route(name, params, absolute, ziggyConfig);
This made URLs localized, but only worked properly on the home page, because all URLs suddenly became relative. Even setting absolute to true did not help. URLs became e.g. /en/contact/en/blog if I was looking at the blog link while being on the /en/contact page.
So to make URLs absolute I introduced this hack – I’m overwriting the “current URL” in ziggy to / to make URLs absolute:
const ziggyConfig = { ...props.initialPage.props.ziggy };
ziggyConfig.url = ziggyConfig.url.split('/').slice(0, 3).join('/');
global.route = (name, params) => route(name, params, true, ziggyConfig);
Which is obviously a dangerous hack and will probably have side effects. However, this currently works and all URLs seem to be correct.
Questions
The main question is how do I make this correctly without this hack?
My other problem is that I can’t really trust the SSR output anymore. What I see on the page locally during development and what I see on production is NOT the same that search engines see. This is really dangerous. How can one test this SSR setup? How can I trust that the website is working if I can’t even see how search engines see it? Any advice here would be super helpful.
I have considered not using react for “public” pages, but would like to avoid this if possible, since that would lead to having to duplicate layout/components.