Issue with htaccess redirection in WP Multisite

Every URL that has example.com/<non-existent-page> redirects to the main page: example.com/es/.

This is presumably being done by WordPress itself, as there is nothing in what you’ve posted (in .htaccess) that is doing this.

You probably need to add an additional “wildcard” redirect to redirect /<anything> (except for /es and /en) to /es/<anything>. For example:

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule !^(es|en)($|/) https://example.com/es%{REQUEST_URI} [R=301,L]

This will naturally encompass the root redirect (your first rule) as well. However, this redirect will need to go after the other two specific redirects that remove index.html and indexingles.html respectively.

The RewriteCond directive that checks against the REDIRECT_STATUS environment variable is necessary to ensure that only direct requests are redirected and not rewritten requests to index.php (the WP front-controller). This preventing a redirect loop. The REDIRECT_STATUS env var is not set on direct requests but set to “200” (as in “200 OK” HTTP response status) after the first successful rewrite. The alternative would be to include index.php in the alternation subpattern, however, that wouldn’t then canonicalise direct requests to index.php itself.

There is no need to backslash escape the slashes, dots and colons in the RewriteRule substitution since these characters have no special meaning here.

Also, there is no need for the conditions (RewriteCond directives) unless you have multiple domains. But even if you are, this can be simplified to a single condition. For example:

RewriteCond %{HTTP_HOST} ^(www\.)?example\.com [NC]

You should test first with 302 (temporary) redirects in order to avoid potential caching issues. And only change to a 301 (permanent) when you are sure this is working OK. However, language redirects like this should probably be temporary anyway.

So, in summary, you should replace your existing redirect directives with the following:

RewriteEngine On

# Remove "index.html" and redirect to "/es/"
RewriteRule ^index\.html$ https://example.com/es/ [R=301,L]

# Remove "indexingles.html" and redirect to "/en/"
RewriteRule ^indexingles\.html$ https://example.com/en/ [R=301,L]

# Everything else default to "/es/<anything>"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule !^(es|en)($|/) https://example.com/es%{REQUEST_URI} [R=301,L]

UPDATE: What its not working is: https://example.com/wp-admin/network/ redirected you too many times.

I think you’ll probably need to exclude any URL that starts /wp-admin (and /wp-login.php and /phpmyadmin) from the last redirect. Change the last rule to read:

# Everything else default to "/es/<anything>"
# With some exceptions...
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule !^(es|en|wp-admin|wp-login\.php|phpmyadmin)($|/) https://example.com/es%{REQUEST_URI} [R=301,L]

You could also consider excluding any URL that already maps to a physical file (ie. -f). And if more exclusions are required then consider adding additional RewriteCond directives instead. For example:

# Everything else default to "/es/<anything>"
# With some exceptions...
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{REQUEST_URI} !^/wp-login\.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule !^(es|en|wp-admin|phpmyadmin)($|/) https://example.com/es%{REQUEST_URI} [R=301,L]

Note that the REQUEST_URI server variable includes the slash prefix on the URL-path, but the URL-path matched by the RewriteRule pattern does not.