apache.htaccesshttp-redirectjoomla

Joomla .htaccess, need to redirect whole site except a single URL


In a Joomla site I'm trying to redirect all URLs from an old domain to a new domain except for a single URL which is https://olddomain.com/subfolder/eliminar-cuenta.html

For that I've written this rule in .htaccess

RewriteCond %{REQUEST_URI} !^/subfolder/eliminar-cuenta.html
RewriteRule ^(.*)$ https://www.newsite.com.ar/ [R=301,L]

This is the whole .htaccess file

##
# @package    Joomla
# @copyright  (C) 2005 Open Source Matters, Inc. <https://www.joomla.org>
# @license    GNU General Public License version 2 or later; see LICENSE.txt
##

##
# READ THIS COMPLETELY IF YOU CHOOSE TO USE THIS FILE!
#
# The line 'Options +FollowSymLinks' may cause problems with some server configurations.
# It is required for the use of Apache mod_rewrite, but it may have already been set by
# your server administrator in a way that disallows changing it in this .htaccess file.
# If using it causes your site to produce an error, comment it out (add # to the
# beginning of the line), reload your site in your browser and test your sef urls. If
# they work, then it has been set by your server administrator and you do not need to
# set it here.
##

## No directory listings
<IfModule mod_autoindex.c>
  IndexIgnore *
</IfModule>

## Suppress mime type detection in browsers for unknown types
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
##
# Disable Federated Learning of Cohorts (FLoC)
# If you uncomment the below directive you have to allow this technology in the 
# Global Configuration of Joomla. Read more about this in the Post-Installation 
# message in the backend.
##
# Header always set Permissions-Policy "interest-cohort=()"
</IfModule>

## Can be commented out if causes errors, see notes above.
Options +FollowSymlinks
Options -Indexes

## Disable inline JavaScript when directly opening SVG files or embedding them with the object-tag
<FilesMatch "\.svg$">
  <IfModule mod_headers.c>
    Header always set Content-Security-Policy "script-src 'none'"
  </IfModule>
</FilesMatch>

## Mod_rewrite in use.

RewriteEngine On

## Begin - Rewrite rules to block out some common exploits.
# If you experience problems on your site then comment out the operations listed
# below by adding a # to the beginning of the line.
# This attempts to block the most common type of exploit `attempts` on Joomla!
#
# Block any script trying to base64_encode data within the URL.
RewriteCond %{QUERY_STRING} base64_encode[^(]*\([^)]*\) [OR]
# Block any script that includes a <script> tag in URL.
RewriteCond %{QUERY_STRING} (<|%3C)([^s]*s)+cript.*(>|%3E) [NC,OR]
# Block any script trying to set a PHP GLOBALS variable via URL.
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
# Block any script trying to modify a _REQUEST variable via URL.
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
# Return 403 Forbidden header and show the content of the root home page
RewriteRule .* index.php [F]
#
## End - Rewrite rules to block out some common exploits.

## Begin - Custom redirects
#
# If you need to redirect some pages, or set a canonical non-www to
# www redirect (or vice versa), place that code here. Ensure those
# redirects use the correct RewriteRule syntax and the [R=301,L] flags.
#

RewriteCond %{REQUEST_URI} !^/subfolder/eliminar-cuenta.html
RewriteRule ^(.*)$ https://www.newsite.com.ar/ [R=301,L]

## End - Custom redirects

##
# Uncomment the following line if your webserver's URL
# is not directly related to physical file paths.
# Update Your Joomla! Directory (just / for root).
##

RewriteBase /subfolder

## Begin - Joomla! core SEF Section.
#
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
#
# If the requested path and file is not /index.php and the request
# has not already been internally rewritten to the index.php script
RewriteCond %{REQUEST_URI} !^/index\.php
# and the requested path and file doesn't directly match a physical file
RewriteCond %{REQUEST_FILENAME} !-f
# and the requested path and file doesn't directly match a physical folder
RewriteCond %{REQUEST_FILENAME} !-d
# internally rewrite the request to the index.php script
RewriteRule .* index.php [L]
#
## End - Joomla! core SEF Section.

But the URL https://olddomain.com/subfolder/eliminar-cuenta.html keeps redirecting to the new domain.

Any ideas?


Solution

  • The problem is that the Joomla front-controller pattern rewrites the request to index.php and during the second pass by the rewrite engine, the URL is no longer /subfolder/eliminar-cuenta.html so the redirect occurs.

    You need to make sure the redirect rule only applies to the initial request and not the rewritten request.

    For example:

    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule !^subfolder/eliminar-cuenta\.html$ https://www.newsite.com.ar/ [R=301,L]
    

    The check against the REDIRECT_STATUS environment variable ensures we only test the initial request and not the rewritten request (when REDIRECT_STATUS is non-empty).

    There is no need for a separate condition to test a single URL since this can be checked in the RewriteRule directive itself.

    Alternatively, you can change the L flag on the last rule in the "Joomla! core SEF Section." (ie. RewriteRule .* index.php [L]) to use END instead. This stops all further rewrites and thus prevents a second pass by the rewrite engine. (The L flag simply stops the current pass.) This requires Apache 2.4+. However, if this is anything like WordPress, it may be unwise to manually edit the code in this section.

    You will need to clear your browser cache (and possibly intermediary caches) before testing, since the erroneous 301 (permanent) redirect will have been cached by the browser. Test first with a 302 (temporary) redirect to avoid these caching issues.

    And, if this URL (ie. page) links to other local assets (CSS, JS, images, etc.) then you need to permit access to (create exceptions for) these assets as well, otherwise they will be redirected, along with every other URL.

    Aside: By redirecting everything to the new site's "homepage" they will be seen as soft-404s by Google (and likely confuse users), so this is unlikely to have the SEO benefit you are perhaps expecting.

    UPDATE#1:

    the subfolder is another joomla site, the one that haves the .htaccess I published here, maybe the root (/) joomla .htacess is messing that up?

    In that case, you need to modify the above RewriteRule to remove subfolder/ from the matched pattern, since the URL-path that is matched here is relative to the directory that contains the .htaccess file. For example:

    # /subfolder/.htaccess
    
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule !^eliminar-cuenta\.html$ https://www.newsite.com.ar/ [R=301,L]
    

    Alternatively, the REQUEST_URI server variable is always root-relative, so using your original approach that used REQUEST_URI in a condition, this could also be written:

    # /subfolder/.htaccess
    
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteCond %{REQUEST_URI} !^/subfolder/eliminar-cuenta\.html
    RewriteRule ^ https://www.newsite.com.ar/ [R=301,L]
    

    (But this hardcodes the rule to the /subfolder, unless you generalize the regex.)

    You also don't need the RewriteBase /subfolder directive if the .htaccess file is inside the /subfolder directory. (You would only need that directive if the .htaccess file was in the root and /subfolder was not part of the visible URL, but Joomla was installed in that subdirectory.) So there's no need to even mention /subfolder in the /subfolder/.htaccess file - which makes the config that much more portable.

    And if this is a secondary Joomla installation in the /subfolder then the following condition in the "Joomla! core SEF Section" is incorrect:

    RewriteCond %{REQUEST_URI} !^/index\.php
    

    This condition is "only" an optimisation, so it still "works", but it is always successful, so it's not doing what it's supposed to do (ie. fail early when the request has been rewritten). It should read as follows:

    RewriteCond %{REQUEST_URI} !^/subfolder/index\.php
    

    Or, generalize it to remove the dependency on /subfolder:

    RewriteCond %{REQUEST_URI} !^/[^/]+/index\.php
    

    maybe the root (/) joomla .htacess is messing that up?

    By default, the mod_rewrite directives in the child (subdirectory) config completely override the parent. So, no, the root .htaccess is not messing anything up here. However, other directives (such as mod_headers) from the root .htaccess config would still be applied - so it's possible you have other directives being applied that perhaps you aren't expecting? For example, any caching directives (mod_expires or mod_headers) set in the root installation would also be applied here, which may or may not be the intention.


    UPDATE#2:

    Thanks a lot man! that worked, but now I have this other issue, as that url is only accessible to logged in users in joomla, it redirects to the user login, and for that the site gets redirected to newsite.com.ar. The redirected URL after hitting eliminar-cuenta.html is this /subfolder/component/users/?view=login&return=aW5kZXgucGhwP0l0ZW1pZD0xMjQz&Itemid=101 is there a way to include that URL to the list of NO REDIRECT urls?

    To make an exception, you could create an additional rule before the above redirect rule that prevents further directives (including the redirect) being processed. For example:

    # Exception for specific URL
    RewriteCond %{QUERY_STRING} ^view=login&return=\w+&Itemid=101$
    RewriteRule ^component/users/$ - [L]
    
    # Redirect rule follows
    :
    

    I'm assuming the value of the return URL parameter is variable, otherwise this value could be stated literally.

    The RewriteRule pattern only matches against the URL-path part of the URL. So to match against the query string (as in this case), we need an additional condition that matches against the QUERY_STRING server variable. (Alternatively, we could match against THE_REQUEST - see below.)

    An additional rule like this is OK if it's not a virtual/pretty Joomla URL, otherwise you may need to create a condition in the redirect rule itself. (This is because the Joomla front-controller still needs to be called, that appears later in the file.) However, in this case, you would need to match against THE_REQUEST instead since this is the only variable that contains both the URL-path and query string (although you could perhaps combine both REQUEST_URI and QUERY_STRING). This arguably results in a more complex regex. For example, instead of the "additional (preceding) rule" mentioned above, you could add a condition to the existing redirect like this:

    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteCond %{THE_REQUEST} !\s/[^/]+/component/users/\?view=login&return=\w+&Itemid=101\sHTTP
    RewriteRule !^eliminar-cuenta\.html$ https://www.newsite.com.ar/ [R=301,L]
    

    THE_REQUEST (the first line of the HTTP request header) contains a string of the form:

    GET /subfolder/component/users/?view=login&return=aW5kZXgucGhwP0l0ZW1pZD0xMjQz&Itemid=101 HTTP/1.1
    

    Although, as mentioned above, you will also need to create exceptions for any local assets (CSS, JS, images, etc.) this page might be using.