This is because of mod_dir and the DirectorySlash. When requesting a directory without the trailing slash, mod_dir attempts to "fix" the URL by appending a trailing slash with a 301 redirect. Unfortunately, in your scenario, the 301 redirect is occurring after the internal rewrite to /subfolder/... so the "subfolder" is exposed in the external redirect.
For example, requesting /wp-admin (no trailing slash)
- The root
.htaccess file internally rewrites the request to /subfolder/wp-admin /subfolder/wp-admin matches a physical filesystem directory so mod_dir issues a 301 external redirect to /subfolder/wp-admin/, exposing the subfolder to the client.
If, however, you request /wp-admin/ (with a trailing slash) then the root .htaccess rewrites the request to /subfolder/wp-admin/. This maps to a physical directory and already has a trailing slash so no further redirect occurs.
It's the same redirect as when requesting any filesystem directory without the trailing slash. (eg. request /subfolder/test directly and you will be redirected.) The trailing slash on directories is required by Apache in order to correctly process .htaccess files that might be in that directory and serve DirectoryIndex documents (also handled by mod_dir).
Solution
Manually append the trailing slash to the original request if it would map to a physical directory in /subfolder.
For example:
# /.htaccess (root .htaccess file) RewriteEngine On # Check if a request that omits the trailing slash maps to a directory in /subfolder # If yes then issue a redirect to append a trailing slash to the requested URL # The 2nd condition excludes requests that look-like files RewriteCond %{HTTP_HOST} ^(www\.)?example\.com\.?$ [NC] RewriteCond $1 !\.\w{2,4}$ RewriteCond %{DOCUMENT_ROOT}/subfolder/$1 -d RewriteRule (.+[^/])$ /$1/ [R=301,L] # Rewrite all requests to /subfolder RewriteCond %{HTTP_HOST} ^(www\.)?example\.com\.?$ [NC] RewriteRule (.*) subfolder/$1 [L]
Assuming you have multiple domains and only example.com should be rewritten to the /subfolder. Otherwise, the HTTP_HOST condition on both rules can be removed.
UPDATE (in response to comments): ^(www\.)?example\.com\.?$ - The regex in the first condition matches an optional trailing dot. This is to allow for fully-qualified-domain-names (FQDN) that explicitly include the trailing dot. When the trailing dot is omitted - as in most cases - a FQDN is assumed (in this case). Both resolve to the same resource on your server, since a FQDN is assumed. However, the Host header varies (with/without the trailing dot). You could simply remove the trailing \.?$ part of the regex, providing you have no conflicts with other domains. However, in your case, if you fail to account for an optional trailing dot then example.com. would potentially serve content from the root/main domain (since the rules would fail).
The 2nd condition (RewriteCond $1 !\.\w{2,4}$) is just an optimisation to avoid requests that look-like files (ie. have file extensions) from being tested. (Filesystem checks are relatively expensive.)
No need to check that the request does not already start with /subfolder/ since if the request did start with /subfolder/ then the mod_rewrite directives in the /subfolder/.htaccess file would (by default) catch the request and completely override the mod_rewrite directives in the parent directory.
No need for the RewriteBase directive, or <IfModule> wrapper since these directives are mandatory.
Aside (additional)
HOWEVER, this is still not complete. A user could potentially access /subfolder/ directly. To prevent this, you can add a rule/redirect to the top of the /subfolder/.htaccess file that redirects the user back to root (only if this directory has been requested directly). For example:
# /subfolder/.htaccess # Redirect any direct requests back to root RewriteCond %{ENV:REDIRECT_STATUS} ^$ RewriteRule (.*) /$1 [R=301,L] # Remaining WordPress directives follow... # :
The REDIRECT_STATUS environment variable is empty on direct requests and set to 200 (as in 200 OK HTTP status) when the request is internally rewritten from root to the subdirectory.
On casual glance, the RewriteRule (.*) /$1 directive might look like it is rewriting to back itself (a loop) until you realise this .htaccess file is in a subdirectory and the RewriteRule pattern matches a relative URL-path (relative to the directory that contains the .htaccess). So, it redirects /subfolder/<anything> to /<anything>, since only <anything> is captured in the $1 backreference.
Although, strictly speaking the WordPress code block should also be modified to prevent requests being "unnecessarily" rewritten back to /index.php in the document root (to then be forwarded again to the /subfolder). This involves removing the RewriteBase directive and the slash prefix on the RewriteRule substitution string. For example:
<IfModule mod_rewrite.c> RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] #RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . index.php [L] </IfModule>
However, if you are allowing WordPress to modify this section of the .htaccess file then you should probably avoid this last step. (Although it is arguably preferable to prevent WordPress from managing the file for you.)
/route→/index.php→/subfolder/index.php/subfolder/does not appear in the visible URL, to make it "look like" it is served from the root, since the OP did not explicitly state this in the opening description. Yes, the "double rewrite" is unnecessary (I see you've mentioned this in your answer; as have I) - only issue with manually correcting this is if they are allowing WordPress to manage this part of the.htaccessfile..htaccessare required. (If the Addon domain points to a subdirectory inside the main domain's document root then a canonical redirect might still be required.)