Replacing one of the parameters in the URL having a query string and retaining the other parameters - apache

I'm trying to replace a URL parameter value with the Apache rewrite.
RewriteCond ℅{QUERY_STRING} market=value [NC]
RewriteRule ^/page.html /page.html?market=new-value [R=301,L,NC]
The URL pattern is https://example.com/page.html?market=value&store=value
I need to replace only the market parameter value and retain the store parameter as is.

RewriteRule ^/page.html /page.html?market=new-value [R=301,L,NC]
Note that when used in .htaccess, the URL-path matched by the RewriteRule pattern does not start with a slash, so the regex ^/page.html used above will never match in this context.
Try the following instead:
RewriteCond %{QUERY_STRING} ^(.*&)?(market=)value(&.*)?$
RewriteRule ^/?page\.html$ %{REQUEST_URI}?%1%2new-value%3 [NE,R=301,L]
This looks for the market=value URL parameter name/value pair anywhere in the query string. We capture the parts of the query string that surround the market URL parameter value (including the parameter name itself). These are saved in the following backreferences:
%1 / pattern (.*&)? - the part of the query string before the market URL parameter (if any). This includes the & delimiter before the market URL parameter name.
%2 / pattern (market=) - captures the literal text market=. This must match to be successful. This simply saves repetition later.
%3 / pattern (&.*)? - the part of the query string after the market URL parameter (if any). This includes the & delimiter after the market URL parameter value.
The REQUEST_URI server variable in the substitution string contains the full root-relative URL-path. We follow this with the query string that we rebuild using the backreferences as explained above, injecting new-value as the value of the market URL param (replacing the value this URL parameter had originally).
The NE (noescape) flag is required to prevent the & (URL param delimiter) being URL-encoded in the response.
Note that I removed the NC (nocase) flag from both the RewriteCond and RewriteRule directives. URL parameter names are not normally case-insensitive and this rule (as written) will preserve the case used in the request.
Test first with a 302 (temporary) redirect to avoid potential caching issues, even if you change this to a permanent redirect later. And you will need to clear your browser cache before testing, since the earlier 301 (permanent) redirect will have been cached by the browser.

Related

Replace ? with / in rewrite rules

I need to achieve this map given below :
https://www.domain1.com/open.html?cuSDyh&utm_medium=rep-email
to
https://www.domain2.com/c/cuSDyh?utm_medium=rep-email
I write a rewrite rule to achieve this which seems wrong as "?" is not replaced in the end result.
RewriteRule ^/open.html?(.*)$ https://www.domain2.com/c/$1 [NC,R=301,L]
Can anyone help me to find the right rewrite rule for this one?
RewriteRule ^/open.html?(.*)$ https://www.domain2.com/c/$1 [NC,R=301,L]
There are a couple of problems here that prevents the rule from working:
The RewriteRule pattern (1st argument) matches against the URL-path only, which notably excludes the query string. To match the query string you need an additional condition (RewriteCond directive) and match against the QUERY_STRING server variable.
The URL-path matched by the RewriteRule pattern does not start with a slash.
Note that the RewriteRule pattern is a regex. The unescaped ? in the above regex is a special meta character (a quantifier) that makes the preceding token optional. So, in your example, the l is optional. (Although you match everytihng that follows anyway, so the ? is entirely redundant.)
FROM: /open.html?cuSDyh&utm_medium=rep-email
TO: /c/cuSDyh?utm_medium=rep-email
I'm assuming the first URL-parameter (eg. cuSDyh in your example) consists of letters only (lower or uppercase) and is not a name=value pair (as per your example). There can be 0 or more URL parameters that follow the first URL parameter.
Try the following instead:
RewriteCond %{QUERY_STRING} ^([a-z]+)(?:&|$)(.*) [NC]
RewriteRule ^open\.html$ /c/%1?%2 [NE,R=302,L]
The %1 and %2 backreferences match the captured subgroups in the preceding CondPattern. ie. %1 matches the first URL parameter (eg. cuSDyh in your example) and %2 matches all subsequent URL params, excluding the delimiter.
The NE flag (not NC) is required on the RewriteRule directive to prevent the query string part of the URL being doubly URL-encoded in the redirect response. (The QUERY_STRING variable is already URL-encoded.)
If there are no additional URL parameters after the first then the above rule will essentially append an empty query string (denoted by the trailing ?). However, the trailing ? is removed automatically by Apache in the redirect response.
To avoid potential caching issues, always test first with a 302 (temporary) redirect and only change to a 301 (permanent) redirect - if that is the intention - once testing is complete.

How to mod_rewrite query string which includes path and parameters?

My website uses a rather complicated query string parameter: Its value is a path including parameters.
For SEO (Search Engine Optimization) etc. I'm now attempting to mod_rewrite shortened versions...
example.com/path/c1/d1/e1.html?x=x1&y=y1
example.com/path/c2/d2/e2.html?x=x2&y=y2
example.com/path/c2/d3/e4.html?x=x5&y=y6
...to the currently required...
example.com/path/?param=a/b/c1/d1/e1?x=x1&y=y1
example.com/path/?param=a/b/c2/d2/e2?x=x2&y=y2
example.com/path/?param=a/b/c2/d3/e4?x=x5&y=y6
So the goal is to...
get rid of the fixed part (?param=a/b/) to shorten the address and
don't have two ? in the visible address
preserve the query string value's necessary variable path components (like c1/d1/e1 or c2/d2/e2 or c2/d3/e4)
add .html to the final part before the query string value's ? to make the folder structure appear 1 level less deep
preserve the query string value's necessary variable parameters (like ?x=x1&y=y1 or ?x=x2&y=y2 or ?x=x5&y=y6)
After hours of research and attempting lots of things that did not work, I signed up here to request your advice on how to solve this mess. Would you please be so kind to assist?
Edit / additional infos:
After the fixed string /path/?param=a/b/ it is always 3 variable path segments like c1/d1/e1.
These variable segments can contain alphanumerical characters a-z A-Z 0-9, dash symbol - and bracket symbols ( and ).
Same applies to the parameter values (x1, y1). Additionally, y1 can contain percent symbol % due to URL-encoding.
Using two question marks (one to start the query string and the other as part of the parameter value) looks invalid but works.
The actual file that handles the request is /path/index.php.
Try the following at the top of your .htaccess file, using mod_rewrite:
RewriteEngine on
# REDIRECT: /path/?param=a/b/c1/d1/e1?x=1&y=y1
RewriteCond %{THE_REQUEST} ^[A-Z]{3,7}\s/path/(?:index\.php)?\?param=a/b/([^/]+/[^/]+/[^/]+)\?(x=[^&]+&y=[^&]+)\s
RewriteRule ^(path)/(?:index\.php)?$ /$1/%1.html?%2 [R=302,L]
# REWRITE: /path/c1/d1/e1.html?x=x1&y=y1
RewriteCond %{QUERY_STRING} ^(x=[^&]+&y=[^&]+)$
RewriteRule ^(path)/([^/]+/[^/]+/[^/]+)\.html$ $1/index.php?param=a/b/$2?%1 [L]
The first rule redirects any direct requests for the "old" URL of the form /path/?param=a/b/c1/d1/e1?x=1&y=y1 (index.php is optional) to the "new" canonical URL of the form /path/c1/d1/e1.html?x=x1&y=y1. This is for the benefit of search engines and any third party inbound links that cannot be updated. You must, however, have already changed all your internal links to the "new" canonical URL.
By matching against THE_REQUEST (as opposed to the QUERY_STRING) we avoid a redirect loop by preventing the rewritten URL from being redirected. THE_REQUEST contains the first line of the request headers and is not changed by other rewrites. For example, THE_REQUEST would contain a string of the form:
GET /path/?param=a/b/c1/d1/e1?x=1&y=y1 HTTP/1.1
This is currently a 302 (temporary) redirect. Only change this to a 301 (permanent) redirect once you have tested that this works OK, in order to avoid potential caching issues.
The second rule internally rewrites requests for the "new" canonical URL, eg. /path/c1/d1/e1.html?x=x1&y=y1, back to the original/underlying URL-path, eg. /path/index.php?param=a/b/c1/d1/e1?x=1&y=y1. The & before the last URL parameter is intentional un-escaped (ie. URL decoded) as discussed in comments.
The $1 and $2 backreferences refer back to the captured groups in the RewriteRule pattern. Whereas the %1 and %2 backreferencs refer to the captured groups in the preceding CondPattern.
These variable segments can contain alphanumerical characters a-z A-Z 0-9, dash symbol - and bracket symbols ( and ).
I've used a more general (and shorter) subpattern in the regex above which will match more characters, but is arguably easier to read. ie. [^/]+ - matches anything except a slash and [^&]+ - matches anything except a &.
If you specifically wanted to match only the allowed characters then you could change the above subpatterns to [a-zA-Z0-9()%-]+ or [\w()%-]+ which also matches underscores (_).
UPDATE: x and y are just examples for parameter names, but in reality there can be lots of different parameter names.
the parameters have more than a single character. They consist of letters a-z, A-Z and in the future maybe digits 0-9. There can be more than the two parameters x and y.
Maybe just match any query string (providing there is a query string).
Try the following instead:
# REDIRECT: /path/?param=a/b/c1/d1/e1?x=1&y=y1
RewriteCond %{THE_REQUEST} ^[A-Z]{3,7}\s/path/(?:index\.php)?\?param=a/b/([^/]+/[^/]+/[^/]+)\?([^\s]+)
RewriteRule ^(path)/(?:index\.php)?$ /$1/%1.html?%2 [R=302,L]
# REWRITE: /path/c1/d1/e1.html?x=x1&y=y1
RewriteCond %{QUERY_STRING} ^(.+)$
RewriteRule ^(path)/([^/]+/[^/]+/[^/]+)\.html$ $1/index.php?param=a/b/$2?%1

.htaccess: Add optional language parameter to all pages

I have a .htaccess file rewriting all URLs. For example:
# Special urls
RewriteRule ^(article)/([^/]*)(?:/[^/]*)?$ /index.php?page=$1&keyword=$2 [L,QSA,NC]
RewriteRule ^(privacy)$ /index.php?page=legal&type=$1 [L,QSA,NC]
RewriteRule ^(imprint)$ /index.php?page=legal&type=$1 [L,QSA,NC]
# All urls
RewriteRule ^([0-9a-zA-Z\s]+)$ /index.php?page=$1 [QSA]
Now I want to integrate a language parameter in my URL. For example
example.com/test ➡ /index.php?page=test
example.com/en/test ➡ /index.php?lang=en&page=test
How would I accomplish this without having to edit all RewriteRules? Is there a way to check if the part after example.com matches a regex, append a query parameter and handle all future rules normally?
Yes, this is possible. Add the following rule before your existing rules:
# Add optional language URL param if present in the first path segment
RewriteRule ^(\w\w)/(.*) $2?lang=$1 [QSA,DPI]
UPDATE: The DPI flag discards the original path-info that would otherwise be appended to the URL-path after the rewrite. This would result in the directives that immediately follow from failing to match, until the next round of processing. (See update below that goes into more detail.)
This assumes that the language code is always 2 characters.
The URL is rewritten to remove the language path segment from the URL-path and appends this as a lang URL parameter. The L flag is specifically omitted so the following rules are left to match the URL-path without the language code.
Since the following rules already have the QSA flag then the lang= URL parameter is appended.
Note, however, that the lang=en parameter is appended at the end of the query string, not prefixed to the beginning, as in your example.
UPDATE: It seems like not only the language gets appended, but also the page name. For example: example.com/de/index results in index?lang=de/index
Solved by adding DPI to [QSA] ➡ [QSA,DPI]
Hhhmmm, yes and no... given only the directives as stated in the question this should still "work" without the DPI flag (although not as efficiently). In fact, index?lang=de/index does not seem to be possible as a resulting URL (there's no page URL parameter)? It's possible that other directives are perhaps resulting in the path-info being appended to the query string? One thing of note is that the last rule stated in the question is missing the L flag, so any directives that follow are perhaps being unnecessarily processed (and even conflicting). However, the DPI flag is certainly an improvement here and should be added.
In detail...
Given a request of the form /de/index, and /de does not exist as a physical directory, then the /index part on the end of the requested URL-path is additional pathname information (path-info) and this is appended to the URL-path after the rewrite above (which is indeed undesirable). So, a request of the form /de/index is rewritten to index?lang=de, which becomes index/index?lang=de after the path-info is re-appended (note that it's not appended to the query string).
The resulting URL-path index/index (with appended path-info) fails to match the RewriteRule directives that follow. However, the rewrite engine then starts over, at which point the path-info that was (unnecessarily) added is then naturally discarded before the next round of processing. This results in the "correct" index?lang=de URL being used as the input for the second round of processing. This matches the last rule stated in the question and the request is finally rewritten to /index.php?page=index&lang=de.
So, the DPI flag shouldn't strictly be necessary here for it to "work". However, it is certainly recommended as it avoids the unnecessary 2nd pass through the rewrite engine. With the DPI flag, the path-info is not appended after the first rewrite, so the URL-path would match the appropriate rule on the first pass.

Checking if query_string has a value or else redirect it

I am learning .htaccess
My URL string is
http://abc.bcd.com/company/abc
I do apply to redirect my page if the company name is abc, xyz etc. and my rewrite rule is
RewriteRule ^/company/(.*?)$ /hhhhh/ll/test_page.html?company_letter=$1 [L,PT]
Sometimes my url change to
http://abc.bcd.com/company/abc?locale=en
What will be query string condition to accommodate both the url and should work properly ?
I have tried this but not helping .
RewriteCond %{QUERY_STRING} ^locale=(.*)$
The rewrite condition should help me like
if(locale="something")
/hhhhh/ll/test_page.html?company_letter=abc&locale=something
else
/hhhhh/ll/test_page.html?company_letter=abc
You just need to add QSA flag in your rule:
RewriteRule ^/?company/(.*)$ /hhhhh/ll/test_page.html?company_letter=$1 [L,QSA]
QSA (Query String Append) flag preserves existing query parameters while adding a new one.
The query string part of the incoming URL is a very specific thing. First you should know that classical rewriteRules are not managing the query string.
So, for example, you cannot make a RewriteRule with a check for a query string parameter value. Query strings parameters could be repeted several times, could appear in any order, and are not url-decoded (the location part of the url is url-decoded when mod_rewrite works on it).
This explains why some RewriteCond are sometimes used on the %{QUERY_STRING}, it cannot be done in RewriteRule but could be tested in rewriteCond, with all the previous probelsm ( repetition, order, url-encoding, etc).
But some rewriteRule tags can be applied for query string managment. Currently your tags are [L,PT], which also be writtent [last,passthrough].
You can add a qsappend or QSA tag which explicitly tells mod_rewrite to combine the original query string and the generated one.
So with
RewriteRule ^/company/(.*?)$ /hhhhh/ll/test_page.html?company_letter=$1 [last,passthrough,qsappend]
This:
http://abc.bcd.com/company/abc
Will go to
/hhhhh/ll/test_page.html?company_letter=abc
And this:
http://abc.bcd.com/company/abc?locale=en
Will go to
/hhhhh/ll/test_page.html?company_letter=abc&locale=en

Rewrite to append to query string

I don't understand why I always have such a massive problem with rewrite rules, but I simply want to append to the query string if it exists and add a ? if it does not. I actually don't care if the URL is changed in the browser or not -- it just has to load the correct target page.
RewriteRule /cia16(.*)\?(.*) /cia$1?$2&CIA=16
RewriteRule /cia16(.*) /cia/$1?CIA=16
If I go to /cia16/steps.php?page=1 it actually gets rewritten to /cia/steps.php?CIA=16 -- that is it seems accept the query string part is not even considered part of the URL for the purposes of the rewrite.
What do I have to do to get the rewrite to work properly with an existing query string?
You can't match against the query string within a RewriteRule, you need to match against the %{QUERY_STRING} variable in a RewriteCond. However, if you want to just append the query string, you can just use the QSA flag:
RewriteRule /cia16(.*) /cia/$1?CIA=16 [QSA]
The URI: /cia16/steps.php?page=1 would get rewritten to /cia/steps.php?CIA=16&page=1. If for some reason, you need the page=1 before the CIA=16, then you can do something like this:
RewriteRule /cia16(.*) /cia/$1?%{QUERY_STRING}&CIA=16