Goal: Want to rewrite all URLs of type
https://www.example.com/page/1234/?/blog/foo/bar/
to
https://www.example.com/page/1234/
In .htaccess I tried many variations along the line
RewriteEngine On
RewriteBase /
RewriteRule ^page/(\d+)/(.*)$ /page/$1 [R=301,L]
Using an .htaccess tester I see that at least the matching pattern is valid.
I would expect that the rewrite would not include anything after $1, but it does, and show the complete original URL.
What am I missing?
https://www.mypage.com/page/1234/?/blog/foo/bar/
Everything after the first ? is the query string part of the URL. By default, Apache passes the query string unaltered from the request to the target URL (unless you create a new query string yourself on the RewriteRule substitution). This explains why you are seeing the same query string on the target URL, without seemingly doing anything with it.
Incidentally, the RewriteRule pattern only matches against the URL-path only - this notably excludes the query string. To match the query string in mod_rewrite you need an additional condition that checks the QUERY_STRING server variable.
On Apache 2.4+ you can use the QSD (Query String Discard) flag to remove the query string from the target URL. Or, specify an empty query string on the substitution - by including a trailing ? (the ? itself does not appear on the resulting URL).
For example (on Apache 2.4):
RewriteCond %{QUERY_STRING} .
RewriteRule ^page/(\d+)/ /page/$1/ [QSD,R=301,L]
The RewriteCond directive checks for the presence of a query string, which is necessary to prevent a redirect loop.
The trailing (.*)$ on the RewriteRule pattern was superfluous.
You had omitted the trailing slash on the end of the substitution (that is present on the example URL). This would have also prevented a redirect loop, but as mentioned, this is not as per your example. (Alternatively, you could include the slash in the captured backreference.)
If you are still on Apache 2.2 then you would need to include a trailing ? instead of the QSD flag. For example:
RewriteRule ^page/(\d+)/ /page/$1/? [R=301,L]
You will need to clear your browser cache before testing, as 301 (permanent) redirects are cached persistently by the browser. For this reason, it is often easier to first test with 302 (temporary) redirects.
Related
I need to redirect all URLs like this:
example.com/podcasts//rebt
to
example.com/podcasts
I am trying to adjust this code to do both but I can't get it to work:
RewriteCond %{REQUEST_METHOD} !=POST
RewriteCond %{REQUEST_URI} ^(.*?)(/{2,})(.*)$
RewriteRule . %1/%3 [R=301,L]
To remove //<something> at the end of the URL-path (eg. /podcasts//rebt to /podcasts, try the following instead at the top of the root .htaccess file:
RewriteEngine On
RewriteCond %{THE_REQUEST} \s([^?]+?)//
RewriteRule . %1 [R=301,L]
THE_REQUEST server variable contains the first line of the initial request headers (eg. GET /podcasts/rebt HTTP/1.1) and does not change when the request is internally rewritten (unlike REQUEST_URI).
The regex \s([^?]+?)// captures the part of the URL-path before the first instance of a double slash. Anything after and including the double slash, are discarded. This regex also ensures we do not inadvertently match against the query string (if any).
The %1 backreference contains the captured subpattern (ie. everything before the first double slash in the URL-path) from the preceding CondPattern.
Aside: Note that this will not work properly if the preceding URL-path maps to a physical directory, since it will result in two redirects. eg. /directory//something to /directory to /directory/ (by mod_dir). In this case, you should avoid removing the first trailing slash.
You should test first with a 302 (temporary) redirect to avoid any potential caching issues and only change to a 301 (permanent) redirect when you are sure it's working as intended. You should clear your browser cache before testing.
A look at your existing rule...
RewriteCond %{REQUEST_METHOD} !=POST
RewriteCond %{REQUEST_URI} ^(.*?)(/{2,})(.*)$
RewriteRule . %1/%3 [R=301,L]
This code is intended to reduce multiple slashes to single slashes in the URL-path, not remove the double slash and remaining path entirely. eg. /podcasts//rebt to /podcasts/rebt. However, since it checks against the REQUEST_URI server variable (which can change throughout the request) it may not work as intended.
Also, the condition that checks against the REQUEST_METHOD would seem to be redundant, unless you are erroneously POSTing to double-slashed URLs internally? A 301 redirect removes any POST data (since the browser converts it to GET) - hence why the check may be necessary in certain cases.
I am trying to redirect from an url to another url but keeping the queries.
For example, from
/oldurl?query1=yes&query2=yes&... (or any list of queries)
to
/newurl?fixedquery=yes&query1=yes&query2=yes&...
So in pratice it would redirect the old url and its queries to a new url, keeping the old queries, plus a fixed query.
This is what I have been trying to use (unsuccessfully) in the .htaccess:
RedirectMatch 301 /oldurl/?$ newurl/?fixedquery=yes&$1
I also tried before using Rewrite
RewriteCond %{QUERY_STRING} ^fixedquery=yes$
RewriteRule ^oldurl/?$ newurl/? [R=301,L]
But this simply redirects if for /oldurl (adding fixedquery) and gives a 404 in case I pass a query to oldurl (e.g. /oldurl?var1=1).
Where am I wrong?
You need to use mod_rewrite in order to manipulate the query string. Try the following instead:
RewriteRule ^oldurl/?$ /newurl?fixedquery=yes [QSA,R=301,L]
Your example URL omits the trailing slash on the target URL, so I omitted it here. However, in your directives, you are including it?
The QSA flag appends/merges the original query string from the request. So the resulting URL is /newurl?fixedquery=yes&query1=yes&query2=yes&..., where query1=yes&query2=yes&... were passed on the initial request.
UPDATE: Note that this rule needs to go near the top of the file, before any existing rewrites. The order of directives in the .htaccess file can be important. Test first with 302 (temporary) redirects to avoid potential caching issues. And you will need to ensure the browser cache is cleared before testing.
A look at your attempts...
RedirectMatch 301 /oldurl/?$ newurl/?fixedquery=yes&$1
The RedirectMatch (mod_alias) directive matches against the URL-path only, but you have no capturing subgroup in the regex, so the $1 backreference is always empty.
RewriteCond %{QUERY_STRING} ^fixedquery=yes$
RewriteRule ^oldurl/?$ newurl/? [R=301,L]
This matches a request for /oldurl?fixedquery=yes and redirects to newurl/ - removing the query string entirely. However, this is also reliant on RewriteBase being set, otherwise this will result in a malformed redirect, exposing your directory structure.
I'm a noob when it comes to regex. What I'm trying to accomplish is:
https://www.example.com/shop/product-floating-front-rotor-kit/
should redirect to
https://www.example.com/shop/product-matching-front-rotor/
product should be the name of the product, I have to do this for multiple products.
Edit: This is what I have so far, am I even close?
RewriteEngine On
RewriteRule ^/shop/([a-z]+)-floating-front-rotor-kit/ ^/shop/$1-matching-front-rotor/
RewriteRule ^/shop/([a-z]+)-floating-front-rotor-kit/ ^/shop/$1-matching-front-rotor/
This is close, except that...
In .htaccess the URL-path matched by the RewriteRule pattern (first argument) does not start with a slash.
The substitution string has an erroneous ^ prefix. This should be an "ordinary" string, not a regex.
[a-z] does not match hyphens/dashes, which you state could occur in a product name.
You have not included an end-of-string anchor ($) on the end of the RewriteRule pattern, so any trailing string will be successful and discarded. (Is that the intention?)
This is an internal "rewrite", not a "redirect" as stated. You need to include the R flag. (An internal rewrite is unlikely to work here, since the target URL requires further rewriting.)
Try the following instead. This should go at the top of the .htaccess file, immediately after the RewriteEngine directive.
RewriteRule ^shop/([a-z-]+)-floating-front-rotor-kit/$ /shop/$1-matching-front-rotor/ [R=302,L]
This is a 302 (temporary) redirect.
I know that there are many post in here about RewriteRule regex question, but my one is difference.
I follow this tutorial (https://www.visualscope.com/seo-friendly-urls.html) to make a seo friendly url and it work except when user change language.
URL (this works):
/wedding-venue-details/abc-hotel/ -> /wedding-venue-details/?seoURL=abc-hotel
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^wedding-venue-details/(.*)$ ./wedding-venue-details/?seoURL=$1
</IfModule>
but if the user tries to change the language in this page, the URL will become:
/wedding-venue-details/abc-hotel/?lang=en
I just don't know how to replace ?lang=en to &lang=en and append to the end, I tried but still can't get it work.
RewriteRule ^wedding-venue-details/(.*)$ ./wedding-venue-details/?seoURL=$1
You need to use the QSA (Query String Append) flag to append the lang=en parameter onto the end of the query string on the rewritten URL. Apache then handles the merging of the URL parameters and correctly formats the query string.
You are also missing the L flag (important if you add any more directives).
For example:
RewriteRule ^wedding-venue-details/(.*) wedding-venue-details/?seoURL=$1 [QSA,L]
You should remove the ./ prefix from the substitution string (it has no place here - get gets resolved away later).
This will rewrite /wedding-venue-details/abc-hotel/?lang=en to /wedding-venue-details/?seoURL=abc-hotel/&lang=en. Note that, despite your example stating otherwise, this will copy the trailing slash to the seoURL parameter value.
Additional:
Although /wedding-venue-details/?seoURL=abc-hotel/ isn't strictly a valid end-point. This is still reliant on other Apache modules/directives to route the request to a file (or "front-controller") eg. index.html (or index.php) to actually handle the request. For instance, this should probably be /wedding-venue-details/index.html?seoURL=abc-hotel/ (or /wedding-venue-details/index.php?seoURL=abc-hotel/ if using PHP).
I want to remove the string
?mobile=1
out from different URLs with .htaccess. So:
https://www.example.com/?mobile=1 should become https://www.example.com/
and
https://www.example.com/something/?mobile=1 should become https://www.example.com/something/
I tried the following
RewriteEngine On
RewriteRule ^(.+)?mobile=1 /$1 [R=301,L,NC]
But that does not seem to work. Any ideas?
RewriteRule ^(.+)?mobile=1 /$1 [R=301,L,NC]
The RewriteRule pattern matches against the URL-path only, which notably excludes the query string. So the above would never match. (Unless there was a %-encoded ? in the URL-path, eg. %3F)
To match the query string you need an additional condition (RewriteCond directive) and match against the QUERY_STRING server variable.
The regex .+ (1 or more) will not match the document root (ie. your first example: https://www.example.com/?mobile=1). You need to allow for an empty URL-path in this case. eg. .* (0 or more).
For example, try the following near the top of your root .htaccess file:
RewriteCond %{QUERY_STRING} =mobile=1
RewriteRule (.*) /$1 [QSD,R=301,L]
This matches the query string mobile=1 exactly, case-sensitive (as in your examples). No other URL parameters can exist. The = prefix on the CondPattern makes this an exact match string comparison, rather than a regex as it normally would.
And redirects to the same URL-path, represented by the $1 backreference in the substitution string that contains the URL-path from the captured group in the RewriteRule pattern.
The QSD (Query String Discard) flag removes the query string from the redirect response.
Test first with a 302 (temporary) redirect and and only change to a 301 (permanent) - if that is the intention - once you have confirmed this works as intended. 301s are cached persistently by the browser so can make testing problematic.