Search code examples
ubuntumod-rewriteapache2

URL Rewrite to change specific part of URL based on keyword


Whenever there is a request like this to the server

https://%{HTTP_HOST}/app/accounturi/services

I want to redirect it to

https://%{HTTP_HOST}/appLegacy/accounturi/services

here accounturi will be a dynamic one. I'm trying this in apache2.

I tried the below and stuck on the RewriteRule.

RewriteEngine On
RewriteCond %{REQUEST_URI} ^/(.*)services/?$ [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}/appLegacy/

Hey, I'm sorry that I messed up with my question. I need to rewrite not redirect.

So the user should still be able to see

https://%{HTTP_HOST}/app/accounturi/services

but the request should take the response from

https://%{HTTP_HOST}/appLegacy/accounturi/services

Please help with this. I'm directly writing this in the apache2 default configuration under

<VirtualHost *:443>

I already enabled HTTPS to redirect in below <VirtualHost *:80>

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}

Solution

  • RewriteCond %{REQUEST_URI} ^/(.*)services/?$ [NC]
    RewriteRule ^(.*)$ https://%{HTTP_HOST}/appLegacy/
    

    This would 302 (temporary) redirect any URL that ends with services or services/ to https://<hostname>/appLegacy/, losing the trailing <accounturi>/services part.

    You are also failing to check for /app at the start of the URL-path? (Which makes me think your .htaccess file is inside the /app subdirectory? But there is nothing else to suggest that.)

    If the .htaccess file is located inside the /app subdirectory then the rule should be like this instead:

    RewriteRule ^([^/]+/services)/?$ /appLegacy/$1 [R=302,L]
    

    I'm assuming the dynamic accounturi part of the URL-path consists of a single path-segment (as in your example).

    The preceding RewriteCond directive is not required. The $1 backreference contains the URL-path as captured from the RewriteRule pattern (this excludes any optional trailing slash that might be present on the request). Note that since the .htaccess file is inside the /app subdirectory, this is excluded from the URL-path that is captured.

    You don't need to specify an absolute URL as the target since you appear to redirecting to the same host. However, you might choose to include this in order to enforce HTTPS.

    If, however, the .htaccess file is in the document root then the directive would need to be like this instead and explicitly check for the /app subdirectory:

    RewriteRule ^app/([^/]+/services)/?$ /appLegacy/$1 [R=302,L]
    

    UPDATE#1:

    I want to rewrite not redirect . and I'm directly mentioning this in the default configuration page

    If this rule is being used directly inside the <VirtualHost: *:443> (as per your update) then you would need to adjust the rule as follows:

    RewriteRule ^/app/([^/]+/services)/?$ /appLegacy/$1 [L]
    

    Note the slash prefix on the RewriteRule pattern, since in a virtualhost context the RewriteRule pattern matches the full root-relative URL-path. And the removal of the R flag.

    This internally rewrites the request, as opposed to redirecting.


    Aside:

    I already enabled HTTPS to redirect in below <VirtualHost *:80>

    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
    

    You don't need to use mod_rewrite here. A simple mod_alias Redirect would suffice and be more efficient. And this should be a 301 (permanent) redirect, not a 302 (temporary) which this will default to. For example:

    Redirect 301 / https://example.com/
    

    You would, however, need to hardcode the canonical hostname in the rule.


    UPDATE#2:

    sometimes we don't pass 'accounturi' with the URL, so in that case it should rewrite to https://%{HTTP_HOST}/appLegacy/services So the accounturi is an optional one.

    To make the accounturi (2nd path segment) optional then change the rule like this:

    RewriteRule ^/app/(([^/]+/)?services)/?$ /appLegacy/$1 [L]
    

    It basically makes the [^/]+/ part (2nd path segment) optional, by surrounding it in parentheses followed by ?. Only the entire path segment is optional, not just the delimiting / (slash).

    Aside: This also allows an optional trailing slash (carried over from the initial "redirect" above). Since this is now a rewrite, the optional trailing slash should probably be removed if it's not required since it potentially promotes duplicate content (two different URLs returning the same resource). If you did need to allow URLs with and without a trailing slash then an additional rule should be added (before the rewrite) that redirects from one to the other (the canonical URL).

    tried the below. Seems it's working fine.

    RewriteRule ^/app/((.*)+/?services)/?$ /appLegacy/$1 [L]
    

    This "works" (kind of) but has a couple of big flaws:

    1. Very inefficient. The use of the (.*)+ subpattern doesn't make much sense (one or more of potentially nothing). The (unnecessary) capturing subgroup does not actually capture anything and makes the regex very inefficient as it results in an enormous amount of backtracking. This regex is the same as ^/app/(.*/?services)/?$, which is more efficient. But...

    2. Matches too much. By simply making the / (before services) optional this potentially matches far too much, since it matches /app/<anything>services, which includes URLs like /app/foo/bar/bazservices (note the multiple path segments and no slash before services).