I can't figure out why this RewriteCond isn't working - apache

So I'm having trouble figuring out why my RewriteRules won't trigger. These rules are in an .htaccess file at the root directory of a subdomain of my website. I've turned on detailed logging for mod_rewrite in the VirtualHost but that isn't really helping me solve what's wrong, though the first three rules seem to be working simply by coincidence since their files exist at the requested location.
The goal of this set of rules is:
sub.domain.tld/ -> passthrough/serve actual file
sub.domain.tld/?q=test -> passthrough/serve actual file with query args intact
sub.domain.tld/.well-known/* -> passthrough/serve actual file (for letsencrypt)
sub.doamin.tld/* -> process.php?project=*
sub.domain.tld/*?q=test -> process.php?project=*&q=test while handling unlimited number of query args
And the current .htaccess is:
RewriteEngine on
#serve actual file if viewing main page or doing https renewal
RewriteCond %{REQUEST_URI} ^\?.+|\/$ [OR]
RewriteCond %{REQUEST_URI} ^\.well-known.*
RewriteRule (.*) - [L,QSA]
#redirect everything else to the processing script
RewriteCond %{REQUEST_URI} ^(\w+)
RewriteRule \/(\w+) process.php?project=$1 [NC,L,QSA]
Thank you for your help!

OK, This was actually a complex one and because most of the time, %{REQUEST_URI} tests are done using the RewriteRule itself, I got a bit confused and I'm sorry about that.
It turns out:
%{REQUEST_URI} contains the leading slash
the matching part of the RewriteRule doesn't
Also, keep in mind %{REQUEST_URI} doesn't contain the query string, as stated in the Apache manual:
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, a rule like RewriteCond %{REQUEST_URI} ^\?.+ is pretty much useless as you'll never have a question mark in %{REQUEST_URI}
Also, and this probably is the most confusing part, when requesting /, %{REQUEST_URI} will contain the actual index file that has been served. So, if your DirectoryIndex is set to index.php index.html (in that order) and you have an index.html file in the root folder, {REQUEST_URI} will be index.html. If you have an index.php file, it will be index.php, but never /.
That being said, we can simply your rules to:
RewriteEngine on
RewriteCond %{REQUEST_URI} !^/(\.well-known|index\.php$)
RewriteRule (.+) process.php?project=%{REQUEST_URI} [QSA]
Note that I added the $ inside the brackets to only match the end of string character after index\.php but not after \.well-known, so anything after \.well-known will also match.
You will need to replace index\.php with index\.html if you have an html index.
Finally, you don't need 2 rules for that. It's always better to have only one and exclude some URLs from it.
PS: you'll also notice you don't need to escape / as this is not considered as a regexp delimiter.

You just need this single rule in your .htaccess:
RewriteEngine on
# skip files, directories and anything inside .well-known/ directory
RewriteRule ^(?!index\.|process\.php|\.well-known)(.+)$ process.php?project=$1 [L,QSA,NC]

Related

In Apache how to do an external redirect to the slashless version of a URL with a subfolder .htaccess file

On Apache 2.4 I have an .htaccess (in a subfolder) which rewrites slashless requests inside that folder to appropriate index files:
DirectorySlash Off
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} !/$
RewriteCond %{REQUEST_FILENAME}/index.html -f
RewriteRule (.*) $1/index.html [L]
This works for the slashless version exactly as expected. Now I want to redirect the slashed version externally to the slashless version. I tried adding the lines:
RewriteCond %{REQUEST_FILENAME} -d
RewriteCond %{REQUEST_URI} /$
RewriteRule ^(.*)/ $1 [R=302,L]
However this does not work: The redirect is issued, however it does not go to the slashless URL, but to a URL with a system specific part injected.
So, for a sample URL http://example.com/path/to/dir/ the redirected URL looks like this http://example.com/fs9e/username/sub/public/path/to/dir instead of just http://example.com/path/to/dir.
How can I fix this? Many thanks for any pointers!
PS: The real case is a little bit more complicated because I do a subdomain-to-folder rewrite in the root .htacces, but I assume this is not relevant here.
RewriteRule ^(.*)/ $1 [R=302,L]
You are missing the slash prefix (/) on the substitution string (2nd argument) - to make the substitution root-relative. Or rather, /subfolder/ (since this .htaccess file is located in a subfolder). Since this is a relative substitution string (not starting with a slash or scheme+hostname), the directory-prefix*1 (which I assume is /fs9e/username/sub/public/path/) is added back (by default*2), resulting in a malformed redirect. (This is correct for internal rewrites, but not external redirects.)
It should be like this:
RewriteRule ^(.*)/$ /subfolder/$1 [R=302,L]
Note you were also missing the end-of-string anchor ($) on the RewriteRule pattern. (This also negates the need for the preceding condition that checks that REQUEST_URI ends in a slash.)
Note also that this "redirect" should go before the earlier "rewrite".
*1 The directory-prefix is the absolute filesystem path of the location of the .htaccess file.
*2 The alternative is to set a RewriteBase /subfolder - but that then affects all relative substitutions. You could also use an environment variable to apply a specific prefix only to some rules.

.htaccess with multiple slashes on the condition

I am trying to implement clean URL with the help of .htaccess in my project.
I got a 404 when i implimented a condition with multiple condition strings like keyword1/keyword2/param
all other conditions like RewriteRule ^home index.php [L,NC] works fine
My file structure be like
/subdirectory/
|-.htaccess
|-index.php
|-edit-user.php
|-new-user.php
my desired clean url is
mysite.com/subdirectory/user/edit/10
and it should translated into
mysite.com/subdirectory/edit-user.php?id=10
Some of the closest solutions i tried so far (but no luck)
RewriteRule (.*)/user/edit/([0-9]+)$ edit-user?id=$1 [L,NC]
RewriteBase /user/
RewriteRule ^user\/edit\/([0-9]+)$ edit-user.php?id=$1 [L,NC]
Any suggestions are highly appreciated.
RewriteRule (.*)/user/edit/([0-9]+)$ edit-user?id=$1 [L,NC]
Since the .htaccess file is inside the /subdirectory then you would need to write the directive like this:
RewriteRule ^user/edit/(\d+)$ edit-user.php?id=$1 [L]
And remove any RewriteBase directive.
\d is simply a shorthand character class for [0-9].
The RewriteRule pattern matches against the relative URL-path (no slash prefix). That is relative to the directory that contains the .htaccess file. You were also missing the .php extension on the filename you are rewriting to. You do not need the NC flag unless you really do want to allowed a mixed-case request, but that opens you up to potential "duplicate content" which would need to be resolved in other ways.
RewriteBase /user/
RewriteRule ^user\/edit\/([0-9]+)$ edit-user.php?id=$1 [L,NC]
Actually, you are very close here, but the RewriteBase directive would have caused this to fail. The sole purpose of the RewriteBase directive is to override the directory-prefix that is added back on relative path substitutions. The RewriteBase directive sets the "URL-path" (as opposed to filesystem path) that is added back.
So, in this example, RewriteBase /user/ would result in the request being rewritten to /user/edit-user.php?id=10 (relative to the root), which is clearly wrong based on the file structure you posted.
Without the RewriteBase defined then the directory-prefix is added back, which results in the rewrite being relative to the directory containing the .htaccess file.
Also, there's no need to backslash-escape slashes since there are no slash delimiters to the regex. (The spaces that surround the argument are the delimiters.)
all other conditions like RewriteRule ^home index.php [L,NC] works fine
Careful with this, as this will also match /homeanything and /home/something etc.
Finally found the issue.
My .htaccess was
RewriteRule ^home index.php [L,NC] RewriteRule ^([^\.]+)$ $1.php [NC]
RewriteRule ^user/edit/(\d+)$ edit-user?id=$1 [L]
(1st line to add .php to anything that comes in, and the 2nd line to convert the desired URL I needed)
What happened here is, when I try to access the URL mysite.com/subdirectory/user/edit/10
The first rule converts that into mysite.com/subdirectory/user/edit/10.php instead of mysite.com/subdirectory/edit-user.php?id=10
This causes the 404 error.
Now I changed the order and the new .htaccess file looks like,
RewriteRule ^admin/edit/(\d+)$ edit-admin.php?aid=$1 [L]
RewriteRule ^([^\.]+)$ $1.php [NC]
So, when a URL comes in, it will check into all other rules before its matches against the last rule(which appends .php) and translate into the desired result.
Lesson learned: Order matters a lot in .htaccess

Editing .htaccess file to modify URL

I'm trying to modify my .htaccess file to modify my URL and have tried many methods but cannot achieve exactly what I want. For example I have this URL:
http://mywebsite.com/FOLDER/index.php?id=5
Now I want it to look like:
http://mywebsite.com/FOLDER/5
or
http://mywebsite.com/FOLDER/ID/5
My .htaccess contains the following code:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^index/([0-9]+)/([0-9a-zA-Z_-]+) index.php?id=$1 [NC]
I cannot figure out what's wrong. Thanks.
You can use:
RewriteEngine on
# external redirect from actual URL to pretty one
RewriteCond %{THE_REQUEST} \s/+FOLDER/index\.php\?id=(\d+) [NC]
RewriteRule ^ /FOLDER/%1? [R=301,L,NE]
# internal forward from pretty URL to actual one
RewriteRule ^FOLDER/(\d+)/?$ FOLDER/index.php?id=$1 [L,QSA,NC]
The first argument of RewriteRule is what the incoming url without domain and without preceding paths (more on that later) is going to be matched against. This url is, in your case, http://mywebsite.com/FOLDER/5. Assuming that your .htaccess file is in your DocumentRoot, the regex will match against FOLDER/5.
You are currently trying to match FOLDER/5 with ^index/([0-9]+)/([0-9a-zA-Z_-]+), which is not going to work. A better regex would be ^(.*)/([0-9]+)$ or ^(.*)/ID/([0-9]+)$. You can then rewrite to $1/index.php?id=$2. I would recommend using the [L] flag to stop rewriting for this round to avoid common problems with multiple rules matching while you do not expect them to.
Besides this, make sure that your .htaccess files are being read (e.g. by checking that if you enter garbage, you get a 500 internal server error), that mod_rewrite is enabled, that you are allowed to override FileInfo. You also may need to turn AcceptPathInfo off.

Htaccess access specify file

I have .htaccess file:
DirectoryIndex index.php
RewriteEngine on
RewriteRule ^play/([^/\.]+) index.php?task=view&name=$1 [L]
Show page game.
My problem: When I need to load some file (or access by address bar) with path: /play/Assest/file-name.swf. This return 404 error.
How I can access file but don't change RewriteRule above?
I tried redirect code but it's not working:
RewriteRule ^/play/Assets/file-name.swf ^/games/Assets/file-name.swf [R=301,L]
Your RewriteRule is missing an anchor to the end of the URL, so partial matches still get rewritten. Add a $like this:
RewriteRule ^play/([^/.]+)$ index.php?task=view&name=$1 [L]
Shahaf's answer may also help you (although it means the file system gets polled twice for every request, which affects performance), but with this above you are saying "only match play/ with anything but dots or forward slashes following it" which seems to be what you mean. Without the dollar it can have anything after it and still match, as you have found.
I also removed the escaping of the dot which is not necessary in a character class.
Before the rewrite rule you should add conditions if it's not a file or directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

.htaccess and rewrite from subdirectories inside unknown parent directory

So I have an htaccess file in a subdirectory and whenever I try to rewrite the url, it redirects to the document_root and not the subdirectory where the htaccess resides. Now, under normal circumstances, I'd rewrite it with the path to the subdirectory with path/to/subdirectory, but I won't know what the exact path will be. Is there a way either, through an Apache environment variable or something else, to write out that path?
Edit:
Here's the .htaccess file so far.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
#RewriteRule (.*-file) a/b/c/$1.file
RewriteRule (.*-file) $1.file
So, I'm trying to, if the request contains the word file, I want to match the entire request prior to the word file and redirect there. This is so that if a request is to
example.com/a/b/c/file[any characters here].file
the request will be redirected to the right file. To reiterate, the problem is that I am trying to redirect within the subdirectory. So when I say Rewrite $1, I want that to include the entire request and not just what matched in the REQUEST_FILENAME. And the reason I need it to do that is because I can't simply put a/b/c/$1.file since I won't know for absolute certainty the a/b/c part.
Edit 2: Examples:
So, an example is that I'd send a request like:
example.com/a/b/c/fileacs.file
And want to redirect to:
example.com/a/b/c/file.file
Where I do not know a/b/c/. I have an actual regex and set of rules for the real-world use of this redirect, so don't mind the ridiculous nature of this example.
But currently it's redirecting to:
example.com/file.file
Which does not exist and even if it did, I do not want to redirect there. I've read about Rewrite Context, but can't find out anything substantial about it nor if it's the cause for this. Thank you, in advance.
You can use this rule to capture any path before fileacs.file and use that as bach-reference in RewriteRule:
RewriteEngine On
RewriteCond ^(.*)/file[^.]+\.file$ [NC]
RewriteRule ^ %1/file.file [L,R=302]
The solution is to use the a RewriteCond on the %{REQUEST_URI} (thanks anubhava!) that checks matches the entire request except for the %{REQUEST_FILENAME} without capturing it, using a lookahead. Write out this with the %1 followed by the desired filename. See the following:
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} (.*)-file(?!\.file) [NC]
RewriteRule ^ %1.file [L,R=301]
The %1 now holds the path up to the directory that the .htaccess is stored, plus any prefix to the filename. This doesn't match the remainder of the request, but rather looksahead to ensure you're not actually requesting the file you would like to redirect to (causing a loop).