Rewrite rule catch all + QSA not working - php

I'm trying to show a default view (home) when the user lands in the root of my site, this is what I use
RewriteCond %{REQUEST_URI} ^/$
Rewriterule ^(.*)$ index.php?view=home [L]
With this other rule, I'm trying to catch everything that has variables (since all my paths are rewritten, I should never have something like mydomain.com?somevar=true, so i look for & or = or ?), and pass the query string to a page
RewriteCond %{REQUEST_URI} ^([\&|\=|\?]+)$
RewriteRule ^(.*)$ badstuff.php [L,QSA]
What I don't understand is that mydomain.com/?test=true is redirected to index.php?view=home instead of going to badstuff.php
What am i doing wrong? If I comment the 1st cond + rule, the second rule is never matched...
Thanks you.
Edit: Added more rules for better understanding. Forgot to mention, Apache 2.4
This is what I have now
#standard views
Rewriterule ^home$ index.php?view=home [L] (works)
Rewriterule ^about$ index.php?view=about [L] (works)
#default view
RewriteCond %{REQUEST_URI} ^/$
Rewriterule ^(.*)$ index.php?view=home [L] (works)
#bad stuff
Rewriterule ^[\?=&]$ badstuff.php [L,QSA] (doesnt work)

Your chief issue here is that you are attempting to match a query string inside REQUEST_URI, but that variable does not contain the query string.
From the Apache docs:
REQUEST_URI
The path component of the requested URI, such as "/index.html". This notably excludes the query string which is available as its own variable named QUERY_STRING.
So instead, you just need to match any non-empty character inside %{QUERY_STRING}, and you must place that rule before the rule matching / because /?abc=123 would still match the / rule.
# Match a non-empty query string (. is any one character)
RewriteCond %{QUERY_STRING} .
# Don't apply this if the view= has already been applied
RewriteCond %{QUERY_STRING} !view=
# Rewrite to badstuff.php and append the params.
RewriteRule ^.*$ badstuff.php [L,QSA]
# Then rewrite the root
RewriteCond %{REQUEST_URI} ^/$
Rewriterule ^(.*)$ index.php?view=home [L]
Better version:
I would simplify your first rule a bit to eliminate the RewriteCond since only the path needs to be matched by RewriteRule. I've also removed all the () capture groups because you are not actually using the values they capture.
RewriteCond %{QUERY_STRING} .
RewriteCond %{QUERY_STRING} !view=
# Rewrite to badstuff.php and append the params.
RewriteRule .* badstuff.php [L,QSA]
# Match a non-empty path into view=
RewriteRule ^([^/]+)$ index.php?view=$1 [L]
# Then a simpler rule that matches ^$ (no path)
RewriteRule ^$ index.php?view=home [L]
If you applied this in a VirtualHost config instead of .htaccess, the RewriteRule would include the leading /, so you'd need to use instead:
RewriteRule ^/$ index.php?view=home [L]
But / is not used in .htaccess.
Now, if the !view= is too broadly preventing the query string from matching, you may resort to matching in THE_REQUEST for query params.
# Match any query string in the raw request
RewriteCond THE_REQUEST \?[^=]+
RewriteRule .* badstuff.php [L,QSA]
#... Then the other rules

Related

How to fix: RewriteRule not working in .htaccess

Please consider the content in my .htaccess:
##
Options +FollowSymlinks -MultiViews
RewriteEngine on
RewriteBase /
## Allow a few SEO Files direct access.
RewriteRule ^robots.txt?$ robots.txt [L]
RewriteRule ^ads.txt?$ ads.txt [L]
RewriteRule ^sellers.json?$ sellers.json [L]
## Avoid rewriting rules for the admin section
RewriteRule ^(admin|resources)($|/) - [L]
## Set Ajax Request File
RewriteRule ^(kahuk-ajax)/?$ kahuk-ajax.php? [L,QSA]
## Set controller with id
RewriteRule ^([^/]+)/([0-9]+)/?$ index.php?con=$1&id=$2 [L,QSA]
## Set controller with slug
RewriteRule ^([^/]+)/([^/]+)/?$ index.php?con=$1&slug=$2 [L,QSA]
## For paging
RewriteRule ^([^/]+)/page/([0-9]+)/?$ index.php?con=$1&page=$2 [L,QSA]
RewriteRule ^([^/]+)/([^/]+)/page/([0-9]+)/?$ index.php?con=$1&slug=$2&page=$3 [L,QSA]
## Set controller for only one parameter
RewriteRule ^page/([^/]+)/?$ index.php?con=page&slug=$1 [L,QSA]
RewriteRule ^([^/]+)/?$ index.php?con=$1 [L,QSA]
## Set home page
RewriteRule ^/?$ index.php?con=home [L]
Whenever I try to browse http://example.com/kahuk-ajax/?prefix=manage-story-vote, this opened the index.php instead of kahuk-ajax.php!
What am I doing wrong?
## Set Ajax Request File
RewriteRule ^(kahuk-ajax)/?$ kahuk-ajax.php? [L,QSA]
:
## Set controller for only one parameter
:
RewriteRule ^([^/]+)/?$ index.php?con=$1 [L,QSA]
The request is first rewritten to kakuk-ajax.php by the first rule, but the second to last rule then rewrites this to index.php during the second pass by the rewrite engine.
You need to prevent that second rule from rewriting requests of the form kakuk-ajax.php. If these URLs do not contain dots in the first path segment (that ordinarily delimits the file extension) then you could simply include a dot in the negated character class. For example:
## Set controller for only one parameter
:
RewriteRule ^([^/.]+)/?$ index.php?con=$1 [L,QSA]
Alternatively, exclude specific file extensions:
RewriteCond %{REQUEST_URI} \.(txt|json|php)$
RewriteRule ^([^/]+)/?$ index.php?con=$1 [L,QSA]
Or, any URL that looks like it has a file extension:
RewriteCond %{REQUEST_URI} \.\w{2,5}$
RewriteRule ^([^/]+)/?$ index.php?con=$1 [L,QSA]
Aside:
RewriteRule ^(kahuk-ajax)/?$ kahuk-ajax.php? [L,QSA]
This rule is a little contradictory. You are removing the query string with the trailing ?, but then appending it again with the QSA flag. If you wish to preserve the query string then you don't need either. And since you are capturing the URL-path, you might as well make use of this in the substitution string. For example:
RewriteRule ^(kahuk-ajax)/?$ $1.php [L]
Also, be wary of allowing an optional trailing slash here. This promotes duplicate content which could potentially cause SEO issues. Consider redirecting one to the other instead.
## Allow a few SEO Files direct access.
RewriteRule ^robots.txt?$ robots.txt [L]
RewriteRule ^ads.txt?$ ads.txt [L]
RewriteRule ^sellers.json?$ sellers.json [L]
The ? at the end of the regex makes the preceding character (t and n in the above examples) optional, which does not really make sense here. Also, you don't need to rewrite to the same file - you don't want any rewrite/substitution to occur here. So, the above is the same as:
RewriteRule ^(robots\.txt|ads\.txt|sellers.json)$ - [L]
The - (hyphen) explicitly indicates "no substitution".
However, having included a "dot" in the (negated character class) directive above, this directive is redundant, except to "fail early".

Remove and Redirect index.php from CodeIgniter

I got an old project and it has some bizarre issue. Its CodeIgniter project (version 2.X).
Google is indexing our URLs with index.php?. Below is an example
https://www.website.com/index.php?/my-seo-friendly-uri
I can see my pages from above url and with 2 more variants as below
https://www.website.com/index.php/my-seo-friendly-uri
https://www.website.com/my-seo-friendly-uri
We have used https://www.website.com/my-seo-friendly-uri throughout the site.
My question is how I can redirect https://www.website.com/index.php?/my-seo-friendly-uri and https://www.website.com/index.php/my-seo-friendly-uri to https://www.website.com/my-seo-friendly-uri?
The thing I already did
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^index.php/(.*)$ /$1 [R=302,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [L]
</IfModule>
This redirects index.php to normal URLs, but index.php? version of url is still not redirecting
In my config.php, I have the following settings
$config['index_page'] = '';
$config['uri_protocol'] = 'REQUEST_URI';
https://www.example.com/index.php?/my-seo-friendly-uri
This URL contains a query string and so requires a slightly different rule in order to match it. The RewriteRule pattern matches against the URL-path only (just index.php in this case). The query string is available in its own variable.
Add the following, before your existing directives (in addition to the directive that matches /index.php/my-seo-friendly-url - which is passed as path-info):
# Redirect URLs of the form "/index.php?/my-seo-friendly-uri"
# And "/?/my-seo-friendly-uri"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^(/.*)
RewriteRule ^(index\.php)?$ %1 [QSD,R=302,L]
The query string is captured (2nd condition), and the backreference (%1) is used to construct the redirect.
The first condition that checks against the REDIRECT_STATUS environment variable is required in order to prevent a redirect loop, since you appear to be using the query string method to route the codeigniter URLs in the later rewrite. The REDIRECT_STATUS env var is empty on the initial request, but set to "200" (as in 200 OK HTTP status) after the first successful rewrite.
The QSD flag (Apache 2.4+) is required to discard the original query string from the redirected request. If you are still on Apache 2.2 then append a ? (empty query string) to the substitution string instead. ie. %1?
By making the match for index.php optional (ie. ^(index\.php)?$) it will also canonicalise URLs that omit index.php, but still include the query string (that may or may not currently be a problem). eg. /?/my-seo-friendly-uri.
Note that this is currently a 302 (temporary) redirect (as is your existing redirect). Only change this to a 301 (permanent) redirect once you have confirmed it works OK. 301s are cached persistently by the browser so can make testing problematic.
Summary
Your .htaccess file should look like this:
RewriteEngine On
# Query string...
# Redirect URLs of the form "/index.php?/my-seo-friendly-uri"
# And "/?/my-seo-friendly-uri"
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^(/.*)
RewriteRule ^(index\.php)?$ %1 [QSD,R=302,L]
# Path-Info...
# Redirect URLs of the form "/index.php/my-seo-friendly-uri"
# Also handles "/index.php" only
RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteRule ^index.php(?:/(.*))?$ /$1 [R=302,L]
# CodeIgniter Front-controller
# (NB: Using query string method to pass the URL)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*) index.php?/$1 [L]
Additional notes...
The <IfModule> wrapper is not required.
(.*) is the same as ^(.*)$ since regex is greedy by default.
I've modified your existing path-info redirect (ie. /index.php/foo) to also redirect requests for /index.php only. This now requires an additional condition to avoid a redirect loop.
Your CodeIgniter front-controller is using the query string method to pass /my-seo-friendly-url to the backend (as used in the question). However, you have set $config['uri_protocol'] = 'REQUEST_URI'; - which contradicts with this (although shouldn't necessarily be a problem). However, if you are using the REQUEST_URI method then you can remove the ?/$1 part from the end of the final RewriteRule substitution string. For example:
For example, from this:
RewriteRule (.*) index.php?/$1 [L]
To this:
RewriteRule . index.php [L]
Use .htaccess................
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
<IfModule mod_php5.c>
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>
<IfModule !mod_php5.c>
RewriteRule ^(.*)$ index.php?/$1 [L]
</IfModule>
</IfModule>

htaccess move to mamp

I'm setting up a working copy of a site, and copied the entire site to a mamp. But I have problem setting up a working version of .htaccess.
Working directory on localhost is "folder1".
This is the live .htaccess:
RewriteEngine On
RewriteBase /
# Rewrite site site.eu to site.eu/en/
RewriteCond %{QUERY_STRING} !language=(sv|en|ru|jp)
RewriteRule ^$ en/ [L,R=301]
# Add trailing slash if necessary
RewriteRule ^(sv|en|ru|jp)$ $1/ [R=301,L]
# Add language part to old bookmarks/links
RewriteCond %{REQUEST_URI} !^/(sv|en|ru|jp)/
RewriteCond %{QUERY_STRING} !language=(sv|en|ru|jp)
RewriteRule (sale|brands|latest_in_stock)(.*) en/$1$2 [R=301,L]
# Remove www
RewriteCond %{HTTP_HOST} ^www\.site\.eu$ [NC]
RewriteRule (.*) http://site.eu/$1 [R=301,L]
RewriteRule ^(sv|en|ru|jp)/(.*)$ $2?language=$1 [L,QSA]
RewriteRule ^sale sale.php [L,QSA,NC]
RewriteRule ^brands/([^/]+) /designer.php?idnr=$1 [L,QSA,NC]
RewriteRule ^brands /labelList.php [L,QSA,NC]
...
On the local copy I changed to:
RewriteBase /folder1/
I tried to removed the part with "Add language code to old... " and the part "Remove www".
I also tried different changes to the last RewriteRule for exampel adding "folder1" and remove it from RewriteBase.
RewriteRule ^brands /folder1/labelList.php [L,QSA,NC]
The php-files and the index-file is working fine, but none of the /brands for example. I get a 500 error, too many redirects.
The variable %{REQUEST_URI} will contain the part behind the domain name, regardless of which .htaccess file it is in. What will happen is the following:
We start with localhost/en/brands/asdf. It matches the following rule:
RewriteCond %{REQUEST_URI} !^/(sv|en|ru|jp)/
RewriteCond %{QUERY_STRING} !language=(sv|en|ru|jp)
RewriteRule (sale|brands|latest_in_stock)(.*) en/$1$2 [R=301,L]
The first condition is true, because ^/(sv|en|ru|jp)/ does not match /folder1/en/.... The second condition is true, because we haven't reached the rule yet that sets the language in the query string. Finally it will match the last one, because (sale|brands|latest_in_stock)(.*) will match en/brands/asdf ($1 will contain brands and $2 will contain /asdf). This will redirect to localhost/folder1/en/brands/asdf after re-adding the directory-prefix.
You have several options:
1. adding folder1 to the condition
If you add folder1 to the condition with %{REQUEST_URI}, this problem does not occur:
RewriteCond %{REQUEST_URI} !^/folder1/(sv|en|ru|jp)/
2. adding a negative look-ahead
If you add a negative look-ahead to the rule, and remove the condition, things work out correctly, because the first argument of RewriteRule will match against what you expect. The pro of this is that you do not need to know in which directory this is:
#Remove this: RewriteCond %{REQUEST_URI} !^/(sv|en|ru|jp)/
RewriteCond %{QUERY_STRING} !language=(sv|en|ru|jp)
RewriteRule ^(?!(sv|en|ru|jp)/)[^/]*/?(sale|brands|latest_in_stock)(.*) en/$1$2 [R=301,L]
You might need to alter the capture group references. I can't test them here.
3. Moving rules around
Moving the following rule:
RewriteRule ^(sv|en|ru|jp)/(.*)$ $2?language=$1 [L,QSA]
just above the rule I just pointed out should theoretically solve this problem. I would strongly recommend against this solution, because intermixing redirects and rewrites makes it very hard to read.

.htaccess mod rewrite help needed

I have written this little piece of code. I am very new to this so i am not sure it is all correct. but basically it lets me access urls with the php extension. When people get on the site they are being redirected from the geo ip page to the correct language which like looks like this
main.php?lang=uk or nl or en or eu etc.
right now i can also use it like this
main/?lang=uk or nl or en or eu etc.
I would like to be able to to also remove the variable in the url ?lang=uk.
How would i do this. My .htaccess code is below
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
DirectorySlash On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{SERVER_NAME}/$1 [R=301,L]
# remove trailing slash
RewriteRule ^(.*)\/(\?.*)?$ $1$2 [R=301,L]
RewriteRule ^([\w\/-]+)(\?.*)?$ $1.php$2 [L,T=application/x-httpd-php]
</IfModule>
Thanks too anyone willing to help.
The first argument of RewriteRule does match anything after the domain name and prefix* and before any query string if that exists. In http://localhost/this/is/my/test?lang=123 with a .htaccess file in the this/is/ directory it would match my/test. To match a query string, you have to use the %{QUERY_STRING} variable.
If the second argument (the rewritten url) of RewriteRule does not contain a query string, it will automatically append the query string of the original url to the new url.
In the code below I use %{THE_REQUEST}. This is the string that is used to make the request for a page. It is in the form of GET /my/resource?query=string HTTP/1.1. It does not change when rewriting things, which makes it useful to prevent infinite loops.
In your php file make sure that the language is read from a cookie instead of a get variable, then do the following:
#Set cookie for language
RewriteCond %{QUERY_STRING} ^(.*?)&?lang=([^&]+)&?(.*?)$
RewriteRule ^ %{REQUEST_URI}?%1&%3 [CO=lang:%2:127.0.0.1:1:/:0:1,R,L]
#Remove potential prefix or suffix & from query string
RewriteCond %{QUERY_STRING} ^&(.*?)$ [OR]
RewriteCond %{QUERY_STRING} ^(.*?)&$
RewriteRule ^ %{REQUEST_URI}?%1 [R,L]
#External requests with \.php should be without that.
RewriteCond %{THE_REQUEST} \.php
RewriteRule ^(.*)\.php$ $1 [R=301,L]
#Try to load php page if resource does not alreay have an extension
RewriteCond %{REQUEST_URI} !\.[a-z]+$
RewriteRule ^(.*)$ $1.php [L]

Not Found when rewriting using same rule as another rewrite

So I have a rewrite doing article?id=10 which goes to article/10 and I'm trying to do another like this.
I want to go from site?=website.com to site/website.com. Alright, seems like an easy task. So I do
RewriteRule ^site/([0-9]+)$ site.php?id=$1
Which is the same rule as the article rewrite basically. Now I go to site/10 for example and I get
Not Found
The requested URL /site/sdfsd was not found on this server.
Why would this happen if it's the same thing as the other rewrite which works fine?....
Options -MultiViews
DirectoryIndex index.php
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^article/([0-9]+)$ article.php?id=$1 [L,QSA,NC]
RewriteRule ^site/([0-9]+)$ site.php?id=$1
# rewrite from /dir/file/ to /dir/file.php
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1\.php -f [NC]
RewriteRule ^(.+?)/?$ /$1.php [L]
Try
RewriteRule ^site/(.+)$ site.php?id=$1 [L]
[0-9]+ only matches numbers.
Basically your rewriting rule is bad
RewriteRule ^site/([0-9]+)$ site.php?id=$1 will only match numbers
You'll could use instead
RewriteRule ^site/(.*)$ site.php?id=$1
(wildcards)
And I'll have to say that the expression "site/whatever.com" points in reality to
site.php?id=whatever.com - reverse statement is false
You rule does not match because the role only allow numbers. In other words your rule say:
Start with "site/" following a number (0-9).
Your rule must be
RewriteRule ^site/([0-9a-zA-Z]+)$ site.php?id=$1
You seem to have two problems.
[0-9] only matches numbers, so it won't work with letters.
Also you need to add the [L] flag to your rules to make sure it stops at the first match.
RewriteRule ^site/(0-9a-zA-Z+)$ site.php?id=$1 [L]

Categories