mod_rewrite and rewritecond - apache

I am attempting to run a rewrite based on a condition in the URI. In summary, if the uri has a string, and does not contain another string, execute the redirect. I have an error now, but need some help getting this to work.
RewriteCond %{REQUEST_URI} (.*)usb-3-hard-drive(.*) [NC]
RewriteCond %{REQUEST_URI} !(*)start$
RewriteRule .* /en/manuals/usb-3-hard-drive/start [L]
Request URL: http://www.domain.tld/dir1/usb-3-hard-drive/*
New URL: http://www.domain.tld/dir1/usb-3-hard-drive/start
Of course, if the conditions do not match, subsequent rewrites should still be honored.
thank you...

What kind of error? You should look into Apache's error log for detailed error message.
In any case -- I suspect that Apache complains on bad regex syntax. If it is not a typo on copy-paste (somehow), then the error is here: !(*)start$ -- there is no such thing as * on it's own -- you missed the dot . before it -- most likely you meant !(.*)start$.
Here is more optimized and fully working rule:
RewriteCond %{REQUEST_URI} /usb-3-hard-drive/ [NC]
RewriteCond %{REQUEST_URI} !/start$
RewriteRule .* /en/manuals/usb-3-hard-drive/start [L]
BTW -- it does not "execute redirect" as you have mentioned -- it only does silent rewrite (internal redirect) when URL will remain unchanged in browser. If you want to have proper redirect (3xx code) when URL changes in browser's address bar, then add ,R=301 (or whatever redirect code you prefer) next to the [L], i.e. [L,R=301].

Related

.htaccess RewriteRule from long url to show short url

Im trying to rewrite url from long to short but cant wrap my head around this.
My survey rewrite works wonderfully but after completing my survet php redirects to www.example.com/survey_thank_you.php?survey_id=1
but I would like to show url like www.example.com/thank_you
Im not even sure if this is possible.
Im new with .htaccess and i have tried almost everthing
.htaccess
Options +FollowSymLinks
Options -MultiViews
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^survey_thank_you.php?survey_name=([0-9a-zA-Z]+)/?$ Thank_you [L,NC,QSA]
RewriteRule ^([0-9a-zA-Z]+)/?$ survey_form.php?survey_name=$1 [L,NC,QSA] #works like charm.
Any help or directions will be highly appreciated.
Solution:
Options +FollowSymLinks
Options -MultiViews
RewriteEngine on
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^survey_id=([0-9a-zA-Z]+)/?$
RewriteRule ^survey_thank_you\.php$ /%1/thank_you [R,L,QSD]
RewriteRule ^([0-9a-zA-Z]+)/thank_you$ survey_thank_you.php?survey_id=$1 [L,NC,QSA]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^([0-9a-zA-Z]+)/?$ survey_form.php?survey_name=$1 [L,NC,QSA]
but after completing my survet php redirects to www.example.com/survey_thank_you.php?survey_id=1
You need to "correct" the URL that PHP is redirecting you to after the survey. If the desired URL is /thank_you (or /Thank_you?) then PHP should be redirecting to that URL.
You then use mod_rewrite in .htaccess to internally rewrite /thank_you back into the URL that your application understands. ie. /survey_thank_you.php?survey_id=1. However, therein lies another problem, where does the 1 (survey_id) come from in the query string? Presumably you don't want to hardcode this? So this would need to passed in the requested URL. eg. /1/thank_you or perhaps /thank_you/1?
However, is this really necessary? The resulting "thank you" page is not a page that should be indexed or a page that is normally navigated to by the user, so implementing a user-friendly URL here doesn't seem to be a worthwhile exercise?
RewriteRule ^survey_thank_you.php?survey_name=([0-9a-zA-Z]+)/?$ Thank_you [L,NC,QSA]
RewriteRule ^([0-9a-zA-Z]+)/?$ survey_form.php?survey_name=$1 [L,NC,QSA] #works like charm.
You are using a survey_name URL parameter (referencing an alphanumeric value) in your directives, but a survey_id ("numeric"?) URL parameter in your earlier example? So, which is it? Or are these rules unrelated?
You state that the second rule "works like charm", but how? What URL are you requesting? That would seem to rewrite /Thank_you to survey_form.php?survey_name=Thank_you - but that does not look correct?
As mentioned in comments, the RewriteRule pattern matches against the URL-path only. To match against the query string you need an additional condition that matches against the QUERY_STRING server variable. This would also need to be an external 3xx redirect, not an internal rewrite (in order to change the URL that the user sees). Therein lies another problem... if you don't change the URL that your PHP script is redirecting to then users will experience two redirects after submitting the form.
You also need to be careful to avoid a redirect loop, since you are internally rewriting the request in the opposite direction. You need to prevent the redirect being triggered after the request is rewritten. ie. Only redirect direct requests from the user should be redirected.
So, to answer your specific question, it should be rewritten something like this instead:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^survey_name=[0-9a-zA-Z]+/?$
RewriteRule ^survey_thank_you\.php$ /Thank_you [QSD,R,L]
The check against the REDIRECT_STATUS environment variable ensures that only direct requests are processed, not internally rewritten requests by the later rewrite. REDIRECT_STATUS is empty on the initial request and set to the string 200 (as in 200 OK status) after the first successful rewrite.
The QSD flag (Apache 2.4) is necessary to discard the original query string from the redirect response.
So the above would redirect /survey_thank_you.php?survey_name=<something> to /Thank_you.
But this is losing the "survey_name" (or survey_id?), so should perhaps be more like the following, in order to preserve the "survey_name":
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^survey_name=([0-9a-zA-Z]+)/?$
RewriteRule ^survey_thank_you\.php$ /%1/Thank_you [QSD,R,L]
Where %1 is a backreference to the value of the survey_name URL parameter captured in the preceding CondPattern.
However, you would then need to modify your rewrite that turns this back into an understandable URL.
(But you should probably not be doing this in the first place without first changing the actual URLs in the application.)

htaccess Redirect URL with GET Parameters

I have a URL that is in the format http://www.example.com/?s=query
I want to redirect this URL to http://www.example.com/search/query
I have the following .htaccess but I wanted to check if there is anything wrong with this. My RewriteRule looks a little wonky and I don't know if it will cause problems for other URLs.
RewriteEngine on
RewriteCond %{QUERY_STRING} ^s=(.*)$ [NC]
RewriteRule ^$ /search/%1? [NC,L,R]
I ran a test Here and it seems to redirect to the correct URL.
RewriteCond %{QUERY_STRING} ^s=(.*)$ [NC]
RewriteRule ^$ /search/%1? [NC,L,R]
You will likely need the NE (noescape) flag on the RewriteRule directive if you are receiving a %-encoded URL parameter value, otherwise the target URL will be doubly-encoded. The QUERY_STRING server variable is not decoded by Apache.
It also depends on how you are rewriting /search/query back to /?s=query (or presumably more like /index.php?s=query?) - presumably you are already doing this later in the config? You only want this redirect to apply to direct requests and not rewritten requests (otherwise you'll get a redirect loop). An easy way to ensure this is to check that the REDIRECT_STATUS env var is empty.
For example:
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^s=(.*) [NC]
RewriteRule ^$ /search/%1 [NE,QSD,R,L]
Other points:
The QSD flag would be preferable (on Apache 2.4) to appending ? to the end of the susbtitution string in order to remove the query string.
The regex ^s=(.*) (the trailing $ was superfluous) does assume that s is the only URL parameter at the start of the query string. As it stands, everything is assumed to be part of this value. eg. s=foo&bar=1 will result in /search/foo&bar=1.
The NC flag on the RewriteRule directive is superfluous.
Should you also be checking for /index.php?s=<query>? (Or whatever file/DirectoryIndex is handling the request.)

htaccess: Can match one slash, but not double slashes

I am unable to write a rule that matches double slashes.
In my .htacess file:
#RULE 1:
RewriteCond %{REQUEST_URI} ^.*hi1.*$
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
#RULE 2:
RewriteCond %{REQUEST_URI} ^.*hi2/.*$
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
#RULE 3:
RewriteCond %{REQUEST_URI} ^.*hi3//.*$
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
RESULTS:
https://www.example.com/hi1//
successfully redirects to google
https://www.example.com/hi2//
successfully redirects to google
https://www.example.com/hi3//
fails to redirect to google
Third url yields the following:
Sorry, this page doesn't exist.
Please check the URL or go back a page.
404 Error. Page Not Found.
EDIT # 1:
Interestingly:
#RULE 4:
RewriteCond %{REQUEST_URI} ^.*hi4/.*/.*$
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
RESULTS:
https://www.example.com/hi4/abc/
successfully redirects to google
https://www.example.com/hi4//
fails to redirect to google
EDIT # 2:
My original post seems to have created confusion. I will try to be clearer: I need a rule that will match a url ending in double slash, and will not match a url that does not end in double slash. Currently, my .htaccess file contains only the following:
RewriteEngine on
RewriteRule yoyo https://www.cnn.com/ [R=301,L]
RewriteCond %{THE_REQUEST} //$
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
Results:
https://www.example.com/about-us//
fails to redirect to google, and yields 404 error
(The first rule (yoyo) is only to ensure no caching.)
EDIT # 3:
I see that the confusion continues. So, my .htaccess file contains only:
RewriteEngine on
RewriteCond %{THE_REQUEST} //$
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
Results:
https://www.example.com/about-us//
fails to redirect to google, and yields 404 error
This time, I think we can rule out caching, because I used the .htaccss on a website of mine that previously had no .htaccess file.
Simply, my efforts to match a url ending with double-slash are failing.
You need not to write 3 rules when you could catch similar kind of URIs with regex patterns so that we need not to write multiple patterns, this also takes cares of multiple occurrences of / coming in the end. Could you please try following, please make sure you clear your browser cache after placing these rules into your htaccess file.
RewriteEngine ON
RewriteCond %{REQUEST_URI} ^/hi[0-9]+/{2,}?$ [NC]
RewriteRule ^(.*)$ https://www.google.com/ [R=301,L]
EDIT:
OK now I get it. Only match paths ending with two slashes.
I updated the answer. The request URI inside THE_REQUEST is not on the end, but is followed by a space and more after that, so matching //\s should work for you
AmitVerma mentioned the correct answer in his comment, but it is being snowed in by other comments. For all the other people like me who did not know about the THE_REQUEST parameter (thank you Amit) a more complete answer here.
The problem with the original rule is the use of the REQUEST_URI parameter. The value of this parameter will probably already have been cleaned by the webserver or other modules. Double slashes would have been removed.
The THE_REQUEST parameter contains the original unmodified request. Therefore the following will work as requested:
RewriteCond %{THE_REQUEST} //\s.*$
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
Regarding your updated question:
... I need a rule that will match a url ending in double slash
RewriteCond %{THE_REQUEST} //$
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
Aside: Your previous rules matched a URL containing a double slash anywhere in the URL-path (which would naturally catch a double slash at the end as well).
However, the above will not match a URL that ends with a double slash. In fact, it will never match anything because THE_REQUEST does not only contain the URL. THE_REQUEST server variable contains the first line of the HTTP request headers. For example, when you request https://example.com/about-us//, THE_REQUEST will contain a string of the form:
GET /about-us// HTTP/1.1
So, you can see from the above that a regex like //$ will never match. You will need to use a condition of the form:
RewriteCond %{THE_REQUEST} //\s
To match two slashes followed by a space. Which could only occur at the end of URL. (Although it could also occur at the end of the query string, but cross that bridge when we come to it.)
However, since the other suggestions (eg. ^.*hi3//.*$) don't appear to have worked, then this is not going to work either.
You need to clear your browser cache before testing and please test with 302 (temporary) redirects, otherwise, you can easily go round in circles chasing caching issues. You should also test this with the Browser "Inspector" open on the "Network" tab and check the "Disable cache" option. For example, in Chrome:
(UPDATE) Debugging...
This does not seem to be a question about regex, as the earlier answers/comments (and code snippets in the question itself) should already have produced the desired results. So "something else" would seem to be going on here.
To debug and see the value of THE_REQUEST, you can do something like the following (at the very top of your .htaccess file):
RewriteCond %{QUERY_STRING} !^the-request=
RewriteRule ^ /?the-request=%{THE_REQUEST} [R,L]
And then request /about-us//. You should then be redirected to a URL of the form:
/?the-request=GET%20/about-us//%20HTTP/1.1
(Where the %20 are naturally the URL encoded spaces.)
Please report back exactly what you are seeing.
Here's what finally worked to match double slashes (nothing else worked for me):
RewriteEngine on
RewriteCond %{THE_REQUEST} //
RewriteRule ^.*$ https://www.google.com/ [R=301,L]
(And, as I wrote, I was careful to prevent caching, so caching never was an issue.)
PLOT TWIST:
Even this solution, which is the only solution that works on one of my websites, does not work on the website I have been testing on for most of this discussion. In other words, there is not one single solution for matching double-slash on that server!

Add parameter using htaccess on condition

This will be a simple for those familiar with Apache rules.
Situation
Using Alipay for a payment platform, the return URL cannot feature any of your own URL parameters (be it GET or POST). However, I am using Joomla and specifically Akeeba subscriptions. This component expects a parameter in the URL in accordance with the payment platform in question.
I want to detect (through one of Alipay's URL parameters) when a return page is hit and add the extra parameter.
Example (domain and page redacted)
http://...?
currency=HKD&
total_fee=2.00&
out_trade_no=211&
trade_no=2014040100276615&
trade_status=TRADE_FINISHED
Desired outcome
http://...?
currency=HKD&
total_fee=2.00&
out_trade_no=211&
trade_no=2014040100276615&
trade_status=TRADE_FINISHED&
paymentmethod=alipay
The simple addition of a &paymentmethod=alipay
Problem
I can't seem to get Apache to pick up the rule; here are a couple of attempts so far. Please note, I definitely can use .htaccess and don't need to change RewriteBase.
-- Attempt 1 --
RewriteCond %{QUERY_STRING} out_trade_no=
RewriteRule ^out_trade_no paymentmethod=alipay&out_trade_no [R,L,QSA]
-- Attempt 2 --
RewriteCond %{QUERY_STRING} (^|&)out_trade_no=(&|$) [NC]
RewriteRule ^ %{REQUEST_URI}&paymentmethod=alipay [L,R=301,QSA]
Progress
Combining the two, I have made progress but, now seem to have the Rewrite part spamming "paymentmethod=alipay" which seems to cause an error.
RewriteCond %{QUERY_STRING} out_trade_no=
RewriteCond %{QUERY_STRING} !paymentmethod=
RewriteRule ^ %{REQUEST_URI}&paymentmethod=alipay [R,L]
Now getting a redirect chain until it automatically stops at a redirect limit
If you are just trying to match a query string from that URL with that rewritecond you need to match the first one(currency). Which is the easiest.
Try this. It will send all the parameters you want.
RewriteCond %{QUERY_STRING} ^\bcurrency=
RewriteRule ^(.*)$ /$1?paymentmethod=alipay [R,QSA,L]

How to add "everything else" rule to mod_rewrite

How can I make mod_rewrite redirect to a certain page or probably just throw 404 if no other rules have been satisfied? Here's what I have in my .htaccess file:
RewriteEngine on
RewriteRule ^\. / [F,QSA,L]
RewriteRule ^3rdparty(/.*)$ / [F,QSA,L]
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^((images|upload)/.+|style.css)$ $1 [L]
RewriteRule ^$ special [QSA]
RewriteRule ^(special|ready|building|feedback)/?$ $1.php [QSA,L]
RewriteRule ^(ready|building)/(\d+)/?$ show_property.php?type=$1&property_id=$2 [QSA,L]
RewriteRule . error.php?code=404 [QSA,L]
This is supposed, among other things, to send user to error.php if he tries to access anything that was not explicitly specified here (by the way, what is the proper way to throw 404?). However, instead it sends user from every page to error.php. If I remove the last rule, everything else works.
What am I doing wrong?
What is happening is that when you are doing a rewrite, you then send the user to the new URL, where these rewrite rules are then evaluated again. Eventually no other redirectoin rules will be triggered and it will get to the final rule and always redirect to the error.php page.
So you need to put some rewrite conditions in place to make this not happen.
The rewrite engine loops, so you need to pasthrough successful rewrites before finally rewriting to error.php. Maybe something like:
RewriteCond %{REQUEST_URI} !^/$
RewriteCond %{REQUEST_URI} !^/(special|ready|building|feedback|show_property)\.php
RewriteCond %{REQUEST_URI} !^/((images|upload)/.+|style.css)$
RewriteRule ^ error.php?code=404 [QSA,L,R=404]
Each condition makes sure the URI isn't one of the ones your other rules have rewritten to.
The R=404 will redirect to the error.php page as a "404 Not Found".
Unfortunatelly, it didn't work - it allows access to all files on the server (presumably because all conditions need to be satisfied). I tried an alternate solution:
Something else must be slipping through, eventhough when I tested your rules plus these at the end in a blank htaccess file, it seems to work. Something else you can try which is a little less nice but since you don't actually redirect the browser anywhere, it would be hidden from clients.
You have a QSA flag at the end of all your rules, you could add a unique param to the query string after you've applied a rule, then just check against that. Example:
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^((images|upload)/.+|style.css)$ $1?_ok [L,QSA]
then at the end:
RewriteCond %{QUERY_STRING} !_ok
RewriteRule ^ error.php?code=404&_ok [QSA,L,R=404]
In theory if none of the rules are matched (and the requested URL does not exist), it's already a 404. So I think the simplest solution is to use an ErrorDocument, then rewrite it:
RewriteEngine On
ErrorDocument 404 /404.php
RewriteRule ^404.php$ error.php?code=404 [L]
# All your other rules here...
You can do the same for any other HTTP error code.
The problem here is that after the mod_rewrite finishes rewriting the URL, it is resubmitted to the mod_rewrite for another pass. So, the [L] flag only makes the rule last for the current pass. As much better explained in this question, mod_rewrite starting from Apache version 2.3.9, now supports another flag - [END], that makes the current mod_rewrite pass the last one. For Apache 2.2 a number of solutions are offered, but since one of them was a bit clumsy and another didn't work, my current solution is to add another two rules that allow a specific set of files to be accessed while sending 404 for everything else:
RewriteRule ^((images|upload)/.+|style.css|(special|ready|building|feedback|property).php)$ - [QSA,L]
RewriteRule .* - [QSA,L,R=404]
I think your last rule should be
RewriteRule ^(.*)$ error.php?code=404&query=$1 [QSA,L]
You could leave out the parenthesis and the $1 parameter, but maybe it's useful to know, what the user tried to achieve.
Hope, this does the trick!