1

I have found several questions concerning multilingual websites and .htaccess rewrite rules. Unfortunately I am only dealing with my website once a year and I am unable to interpret those answers to my use case. So here is another rewrite rule question:

I have https://www.example.com which is in german and only has two pages: index.html and showreel.html. I have now added an english version to the site at https://www.example.com/en with a translated pair of index.html and showreel.html pages sitting in the /en/ subfolder.

From doing research I have changed my .htaccess file in the root folder to this:


RewriteOptions inherit
RewriteEngine on

RewriteCond %{HTTP_REFERER} !^http://example.com/.*$      [NC]
RewriteCond %{HTTP_REFERER} !^http://example.com$      [NC]
RewriteCond %{HTTP_REFERER} !^https://www.example.com/.*$      [NC]
RewriteCond %{HTTP_REFERER} !^https://www.example.com$      [NC]
RewriteCond %{HTTP_REFERER} !^https://example.com/.*$      [NC]
RewriteCond %{HTTP_REFERER} !^https://example.com$      [NC]
RewriteCond %{HTTP_REFERER} !^https://www.example.com/.*$      [NC]
RewriteCond %{HTTP_REFERER} !^https://www.example.com$      [NC]
RewriteCond %{REQUEST_URI} !^/\.well-known/cpanel-dcv/[0-9a-zA-Z_-]+$
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/(?:\ Ballot169)?
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule .*\.(jpg|jpeg|gif|png|bmp)$ - [F,NC]
RewriteCond %{HTTP_HOST} ^example\.com$
RewriteCond %{REQUEST_URI} !^/\.well-known/cpanel-dcv/[0-9a-zA-Z_-]+$
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/(?:\ Ballot169)?
RewriteCond %{REQUEST_URI} !^/\.well-known/pki-validation/[A-F0-9]{32}\.txt(?:\ Comodo\ DCV)?$
RewriteRule ^/?$ "https\:\/\/www\.example\.com\/" [R=301,L]

# language is ES (spanish)
RewriteCond %{HTTP:Accept-Language} ^es [NC]
RewriteRule ^$ /en/ [L,R=301]

# language starts with DE
RewriteCond %{HTTP:Accept-Language} ^de [NC]
RewriteRule ^$ / [L,R=301]

# language starts with FR
RewriteCond %{HTTP:Accept-Language} ^fr [NC]
RewriteRule ^$ /en/ [L,R=301]

# language starts with IT
RewriteCond %{HTTP:Accept-Language} ^it [NC]
RewriteRule ^$ /en/ [L,R=301]

# language starts with EN
RewriteCond %{HTTP:Accept-Language} ^en [NC]
RewriteRule ^$ /en/ [L,R=301]

# else redirect to the English version
RewriteRule ^$ /en/ [L,R=301]

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType img/jpg "access plus 1 year"
ExpiresByType img/jpeg "access plus 1 year"
ExpiresByType img/gif "access plus 1 year"
ExpiresByType img/x-icon "access plus 1 year"
ExpiresByType img/png "access plus 1 year"
ExpiresByType css/css "access plus 1 month"
ExpiresByType js/x-javascript "access plus 1 month"
ExpiresDefault "access plus 2 days"
</IfModule>

On top of that, both index.html and showreel.html have these hreflang links in the HEAD tag:

<link rel="alternate" hreflang="de" href="/" />
<link rel="alternate" hreflang="en" href="/en/" />
<link rel="alternate" href="/en/" hreflang="x-default" />

In short: This didnt work. The german, "root" page didnt come up anymore for browsers et in german.

What should happen is, that german-set browsers will be served https://www.example.com. All other visitors will be served https://www.example.com/en

Would anybody be able to point me to where I am going wrong here?

0
2
# language starts with DE
RewriteCond %{HTTP:Accept-Language} ^de [NC]
RewriteRule ^$ / [L,R=301]

... The german, "root" page didnt come up anymore for browsers et in german.

Presumably you are seeing a "redirect loop"? Because you can't redirect from A to A (...conditionally based on a constant, ie. the Accept-Language header - which is the same on every request). But since you are already on A there is no need to redirect to A - you should simply do nothing.

For example:

# language starts with DE - do nothing, stay on the root URL
RewriteCond %{HTTP:Accept-Language} ^de [NC]
RewriteRule ^$ - [L]

german-set browsers will be served https://www.example.com. All other visitors will be served https://www.example.com/en

In that case I'm not sure why you are checking for other languages (ES, FR, IT, ...) in your rules? If you only have two languages and the default is EN then you could implement this in a single rule:

# Language is not DE then redirect from root to /en/
RewriteCond %{HTTP:Accept-Language} !^de [NC]
RewriteRule ^$ /en/ [R=302,L]

The redirects the root to /en/ when the Accept-Language header does not start with de (case-insensitive). The ! prefix on the CondPattern negates the regex.

Any redirect of this nature should be a 302 (temporary) redirect, not a 301 (permanent). (However, you probably shouldn't be implementing a language "redirect" in the first place - see below.)

Also, what about /showreel.html? Assuming you have just 4 URLs: / and /showreel.html for German and /en/ and /en/showreel.html otherwise then you can modify the above redirect to the following to cover both:

# Language is not DE then redirect from root to /en/
RewriteCond %{HTTP:Accept-Language} !^de [NC]
RewriteRule ^(showreel\.html)?$ /en/$1 [R=302,L]

Don't implement a language "redirect"

However, you probably shouldn't implement a language "redirect". Instead, allow the user to change the language and perhaps offer a "suggestion" if they appear to be viewing the "wrong" language.

Redirecting based on what you perceive to be the preferred language has a number of issues:

  • Users can be using a shared computer (or their computer is not configured correctly) so they can be stuck on one language they do not understand (or do not "prefer"). Users need to be given the choice to switch between languages. As noted in the MDN Web Docs:

    This header is a hint to be used when the server has no way of determining the language via another way, like a specific URL, that is controlled by an explicit user decision. It is recommended that the server never overrides an explicit decision. The content of the Accept-Language is often out of the control of the user (like when traveling and using an Internet Cafe in a different country); the user may also want to visit a page in another language than the locale of their user interface.

  • The Accept-Language header does not necessarily contain the preferred language at the start of the value. It is more complex and ideally needs to be parsed. So, checking just the presence of a language code at the start of the value is unreliable in order to determine the "preferred" language.

    Reference: RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content - Accept-Language:

    Note that some recipients treat the order in which language tags are listed as an indication of descending priority, particularly for tags that are assigned equal quality values (no value is the same as q=1). However, this behavior cannot be relied upon. For consistency and to maximize interoperability, many user agents assign each language tag a unique quality value while also listing them in order of decreasing quality. Additional discussion of language priority lists can be found in Section 2.3 of RFC4647.

  • Googlebot does not send an Accept-Langauge header (or is sometimes set to en-US?) so Googlebot will never be able to crawl the German language version of your site - it will always be redirected to the "default" language.

    the crawler sends HTTP requests without setting Accept-Language in the request header.

    Source: https://developers.google.com/search/docs/advanced/crawling/locale-adaptive-pages

    The workaround here might be to include an exception for Googlebot, however, this cannot reliably be done in .htaccess and is against Google's own recommendations (see the above link).

Further reading:


Aside:

<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType img/jpg "access plus 1 year"
ExpiresByType img/jpeg "access plus 1 year"
ExpiresByType img/gif "access plus 1 year"
ExpiresByType img/x-icon "access plus 1 year"
ExpiresByType img/png "access plus 1 year"
ExpiresByType css/css "access plus 1 month"
ExpiresByType js/x-javascript "access plus 1 month"
ExpiresDefault "access plus 2 days"
</IfModule>

All your mime-types look wrong?! If mod_expires is enabled then all your resources are probably being cached for the default 2 days, not 1 year or 1 month as would seem to be the intention?

You need to check the mime-types (ie. Content-Type HTTP response header) that your server is responding with for these resource types.

Reference: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types

3
  • Completely agree with "Don't implement a language redirect." Doing so doesn't work in a lot of cases and is really bad for SEO. Jun 11 at 15:29
  • 1
    thank you so much for elaborating in detail! In that case, I will definitely add a language switcher element to the site instead of the language redirect. about the mime-types. These are old entries and I cannot say at this point how they got in there. I shall looks at those as well.
    – Bravewart
    Jun 17 at 7:53
  • @Bravewart You're welcome. An additional aside... you should also review your canonical non-www to www redirect (you are only redirecting the "homepage") and your "hotlink protection" blocks Google (if that is a concern) and potentially real users who have suppressed the Referer header (this could also be greatly simplified).
    – MrWhite
    Jun 17 at 11:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.