Rewrite files with certain extension to a specific directory - apache

So... I have a few million PNGs with unique names spread across a complicated structure of directories that goes 15-20 levels deep, and I want to move them into a directory /my/special/directory/
I can't figure out a way to do this with rewrites (not redirects)
I have tried:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/\.]+).png?$ /my/special/directory/$1.png [R]
But this works on the first level only (root). I have also tried
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([a-zA-Z0-9/]+)([^/\.]+).png?$ /my/special/directory/$2.png [R]
But it doesn't work at all. How should I write this?
I am checking for the image not to exist because there are other directories in this vhost that contain images and will remain as they are.
TIA

I don't really understand that pattern you try: ^([^/\.]+).png?$...
Why the ? at the end? And why the explicity denial of dots in file names? The reason this only works for "the first level" certainly is that you explicitly deny acceptance of paths with contained / which happens to be the folder delimiter...
Also just use [L] instead of [R] since you do not want a redirection.
This gives us:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.+)\.png$ /my/special/directory/$1.png [L]
In case you have to rewrite such that only the file name is captured (depends on the structure of your links, actually), this would probably be a good start:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule /?([^/]+)\.png$ /my/special/directory/$1.png [L]
Note: there is no leading ^ in the second variant, since you are not interested in what path is specified before the last folder separator...

Related

Apache mod_rewrite path to query string for specific path format

I've been working on this for weeks. I want to be able to use a friendly URL to pass variables to a PHP script. For example, if someone uses this URL:
https://example.com/foo/bar/who
I would like my PHP script to receive this:
https://example.com/index.php/?var1=foo&var2=bar&var3=who
The catch is that I ONLY want to do this rewrite when there are three vars in the path. If there are fewer or more than three, I do not want to rewrite the URL.
I've seen several other explanations related to this type of rewrite but nothing quite like this.
This is almost working, but not quite. I only want the rewrite done if there's something present for those first three variables. I know this is incomplete, but with this method at least the REQUEST_URI contains the values I can parse. But again, I only want to do this when there are three vars in the path.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule !^index\.php/([^/]*)/([^/]*)/([^/]*)$ index.php [L]
Thank you.
You were pretty close, have it this way. Please make sure you clear your browser cache before testing your URLs. You had already created back references only thing you needed to use them, which I have added those now.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]*)/([^/]*)/([^/]*)$ index.php?var1=$1&var2=$2&var3=$3 [L]

mod_rewrite - Removing index.php from URL (in subfolder)

I'm trying to get an API working (locally, for now), which lives in a subfolder. I've been trying every example I can find, but all are ending up in 404 errors.
I have this URL:
http://127.0.0.1/~owner/personal/api/v1/index.php/tasks
I want to be able to use this:
http://127.0.0.1/~owner/personal/api/v1/tasks
Eventually, it will become this:
http://api.mydomain.com/tasks
I just can't seem to get my .htaccess rules setup correctly. This keeps getting suggested, but does nothing:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/$1 [L]
What am I missing?
Update
I have setup my virtual hosts to use http://api.local/ instead of the previously used addresses.
Accessing http://api.local/index.php/tasks works.
Accessing http://api.local/tasks does not.
Maybe part of your problem is your development environment. You are using a local server to do this on with several subfolders deep and I think it's messing with your .htaccess depending on the location.
If your .htaccess is in the root of your website http://127.0.0.1/.htaccess then it's not going to work properly.
Make sure it's in the same directory as your index.php file and make sure /~owner/personal/api/v1/ is your document root for your dev environment as specified in your apache config file.
Then you can test out your htaccess rules and see how they work. Your current rule should be ok providing /tasks won't be a real directory in the root.
On another note,
I always suggest using your real domain name so you could see how it works as if it were in production. You can do that by modifying your HOST file on your PC and the site will only available to you and you can access via yoursite.com. This quick easy guide will show you.
Try specifying the rewrite base.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/$1 [L]
You are using the parameters of RewriteRule backwards. You need this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^/?(.*?)/index.php(.+)?$ /$1$2
This means:
if it's not a file
and it's not a directory
and it looks like / (optional), possibly some more characters, then /index.php, then possibly some more characters, go to the URL without the /index.php part.
Note that I deleted the [L] for now -- it means "if the URL matches this rule, don't apply any more rules." You say you want to keep transforming it into http://api.mydomain.com/tasks, so you shouldn't use [L].

Redirect all to index.php using htaccess

I am writing a simple PHP-based MVC-ish framework. I want this framework to be able to be installed in any directory.
My PHP script grabs the request uri and breaks it off into segments. It makes segment 1 the controller and segment 2 the action. This goes all fine when I do this:
http://www.example.com/mvc/module/test/
It will go to the specific module controller and method. Now I have a default controller, the home controller, which is in folder home.
Now when I access this folder directly http://www.example.com/mvc/home/
It will display a 403 forbidden , because this folder does exist, instead it should also go back to http://www.example.com/mvc/index.php
If I would have installed the framework in a different folder, lets say folder framework it has to redirect back to http://www.example.com/framework/index.php
I would like to redirect every folder and php file back to the index.php, leaving everything else the way it is.
My first problem I encountered was it never redirects to the right folder, always to the domain root folder.
This is what I tried :
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
Your rewrite rule looks almost ok.
First make sure that your .htaccess file is in your document root (the same place as index.php) or it'll only affect the sub-folder it's in (and any sub-folders within that - recursively).
Next make a slight change to your rule so it looks something like:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?path=$1 [NC,L,QSA]
At the moment you're just matching on . which is one instance of any character, you need at least .* to match any number of instances of any character.
The $_GET['path'] variable will contain the fake directory structure, so /mvc/module/test for instance, which you can then use in index.php to determine the Controller and actions you want to perform.
If you want the whole shebang installed in a sub-directory, such as /mvc/ or /framework/ the least complicated way to do it is to change the rewrite rule slightly to take that into account.
RewriteRule ^(.*)$ /mvc/index.php?path=$1 [NC,L,QSA]
And ensure that your index.php is in that folder whilst the .htaccess file is in the document root.
Alternative to $_GET['path'] (updated Feb '18 and Jan '19)
It's not actually necessary (nor even common now) to set the path as a $_GET variable, many frameworks will rely on $_SERVER['REQUEST_URI'] to retrieve the same information - normally to determine which Controller to use - but the principle is exactly the same.
This does simplify the RewriteRule slightly as you don't need to create the path parameter (which means the OP's original RewriteRule will now work):
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L,QSA]
However, the rule about installing in a sub-directory still applies, e.g.
RewriteRule ^.*$ /mvc/index.php [L,QSA]
The flags:
NC = No Case (not case sensitive, not really necessary since there are no characters in the pattern)
L = Last (it'll stop rewriting at after this Rewrite so make sure it's the last thing in your list of rewrites)
QSA = Query String Append, just in case you've got something like ?like=penguins on the end which you want to keep and pass to index.php.
To redirect everything that doesnt exist to index.php , you can also use the FallBackResource directive
FallbackResource /index.php
It works same as the ErrorDocument , when you request a non-existent path or file on the server, the directive silently forwords the request to index.php .
If you want to redirect everything (including existant files or folders ) to index.php , you can use something like the following :
RewriteEngine on
RewriteRule ^((?!index\.php).+)$ /index.php [L]
Note the pattern ^((?!index\.php).+)$ matches any uri except index.php we have excluded the destination path to prevent infinite looping error.
There is one "trick" for this problem that fits all scenarios, a so obvious solution that you will have to try it to believe it actually works... :)
Here it is...
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [L,QSA]
</IfModule>
Basically, you are asking MOD_REWRITE to forward to index.php the URI request always when a file exists AND always when the requested file doesn't exist!
When investigating the source code of MOD-REWRITE to understand how it works I realized that all its checks always happen after the verification if the referenced file exists or not. Only then the RegEx are processed. Even when your URI points to a folder, Apache will enforce the check for the index files listed in its configuration file.
Based on that simple discovery, turned obvious a simple file validation would be enough for all possible calls, as far as we double-tap the file presence check and route both results to the same end-point, covering 100% of the possibilities.
IMPORTANT: Notice there is no "/" in index.php. By default, MOD_REWRITE will use the folder it is set as "base folder" for the forwarding. The beauty of it is that it doesn't necessarily need to be the "root folder" of the site, allowing this solution work for localhost/ and/or any subfolder you apply it.
Ultimately, some other solutions I tested before (the ones that appeared to be working fine) broke the PHP ability to "require" a file via its relative path, which is a bummer. Be careful.
Some people may say this is an inelegant solution. It may be, actually, but as far as tests, in several scenarios, several servers, several different Apache versions, etc., this solution worked 100% on all cases!
You can use something like this:
RewriteEngine on
RewriteRule ^.+$ /index.php [L]
This will redirect every query to the root directory's index.php. Note that it will also redirect queries for files that exist, such as images, javascript files or style sheets.
Silly answer but if you can't figure out why its not redirecting check that the following is enabled for the web folder ..
AllowOverride All
This will enable you to run htaccess which must be running! (there are alternatives but not on will cause problems https://httpd.apache.org/docs/2.4/mod/core.html#allowoverride)
just in case you were still wondering how to redirect all request either if the directory exists (for core framework folders and files) to the framework index handler, after some error/success attempts just noticed I just needed to change the RewriteCond in the .htaccess file
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
the above condition states "not found files" and "not found directories", ok, what if just remove "not found" (!-d) line, and ended with something like the below:
RewriteEngine on
RewriteBase /framework/
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ /framework/index.php [L,QSA]
It worked for me like a charm
I just had to face the same kind of issue with my Laravel 7 project, in Debian 10 shared hosting. I have to add RewriteBase / to my .htaccess within /public/ directory. So the .htaccess looks a like
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^.*$ /index.php [L,QSA]
After doing that don't forget to change your href in,
home
Example:
.htaccess file
RewriteEngine On
RewriteRule ^about/$ /about.php
PHP file:
about

How do I get apache RewriteRule working correctly for a subdomain?

I just setup a subdomain with the following RewriteCond:
RewriteCond $1 !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^/]+)$ search.php?q=$1 [L,NS]
I'm using the same rewrite condition on my main domain and it works perfectly. However, when I set it up on the subdomain, it simply outputs "index.php" when going to http://sub.domain.com
Every page on the subdomain outputs the page name in the body instead of processing the code, except for the search page, which appears to be working correctly.
What can I do to correct this issue?
I haven't played with your exact regex with mod_rewrite, but if I was looking at writing that regex in another engine, I would have to escape the slash. Also, given that $ is used to indicate a back reference, would that need escaping too (would your $ symbols in the regex be necessary as there is likely to be more text in the URI and it is not matched at the end of a string)?
I would try
RewriteCond $1 !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^\/]+)$ search.php?q=$1 [L,NS]
One other thing. Normally $ at the end of a regex means "only match if this is the end of the string". So from that, if RewriteCond is matching on ^search.php$ but the URL is search.php?q=... then I would think that this wouldn't match because search.php is not the end of the string. So that would look like the following (assuming you don't need to change anything else from your original).
RewriteCond $1 !^search.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/?([^/]+)$ search.php?q=$1 [L,NS]
In the main config the path always begins with / and you need an absolute path:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^/([^/]+)$ %{DOCUMENT_ROOT}/search.php?q=$1 [L]
In an .htaccess you need a RewriteBase which is stripped from the url (no / in the Rule now) and the path is relative.
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !^search.php$
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([^/]+)$ search.php?q=$1 [L]
Several things come to mind here:
I have a few suggestions/comments/gotchas. Hopefully one of them is useful to you:
Make sure search.php isn't just echoing out its $_GET parameters. While this sounds obvious in retrospect, it's one of the more overlooked solutions.
RewriteRule works slightly differently when you specify it in a server configuration file than if you specify it in an .htaccess. Specifically, ^/ is wrong in a server config version as the entire URL is used (http://sub.domain.com/blah).
Make sure no other rewrite rules are being processed for this subdomain first, either in the main httpd.conf / apache2.conf or .htaccess.
Make sure RewriteEngine On appears in your configuration, as it is activated per-VirtualHost.
The NS flag will ignore redirects done using a relative Redirect or relative RewriteRule.
It sounds like the pattern '^/?([^/]+)$' may not be matching at all.
I'd activate RewriteLog, crank RewriteLogLevel to level 3 or above, and see if your pattern is matching at all. If not, start with a simpler pattern, and then work your way to a more complex pattern.
Or, something else is matching the pattern, so the request never gets to 'RewriteRule ^/?([^/]+)$' at all. You will see this in the RewriteLog.
I believe I recently had a problem where '^/' didn't match in certain cases on a Virtual Host. But '/' worked. The folks in the #httpd on Freenode.org helped me. If I can find this in my notes, I'll post it here.

RewriteRule checking file in rewriten file path exists

How can you use ModRewrite to check if a cache file exists, and if it does, rewrite to the cache file and otherwise rewrite to a dynamic file.
For example I have the following folder structure:
pages.php
cache/
pages/
1.html
2.html
textToo.html
etc.
How would you setup the RewriteRules for this so request can be send like this:
example.com/pages/1
And if the cache file exists rewrite tot the cache file, and if the cache file does not exists, rewrite to pages.php?p=1
It should be something like this: (note that this does not work, otherwise I would not have asked this)
RewriteRule ^pages/([^/\.]+) cache/pages/$1.html [NC,QSA]
RewriteCond %{REQUEST_FILENAME} -f [NC,OR]
RewriteCond %{REQUEST_FILENAME} -d [NC]
RewriteRule cache/pages/([^/\.]+).html pages.php?p=$1 [NC,QSA,L]
I can off coarse do this using PHP but I thought it had to be possible using mod_rewrite.
RewriteRule ^pages/([^/\.]+) cache/pages/$1.html [NC,QSA]
# At this point, we would have already re-written pages/4 to cache/pages/4.html
RewriteCond %{REQUEST_FILENAME} !-f
# If the above RewriteCond succeeded, we don't have a cache, so rewrite to
# the pages.php URI, otherwise we fall off the end and go with the
# cache/pages/4.html
RewriteRule ^cache/pages/([^/\.]+).html pages.php?p=$1 [NC,QSA,L]
Turning off MultiViews is crucial (if you have them enabled) as well.
Options -MultiViews
Otherwise the initial request (/pages/...) will get automatically converted to /pages.php before mod_rewrite kicks in. You can also just rename pages.php to something else (and update the last rewrite rule as well) to avoid the MultiViews conflict.
Edit: I initially included RewriteCond ... !-d but it is extraneous.
Another approach would be to first look if there is a chached representation available:
RewriteCond %{DOCUMENT_ROOT}/cache/$0 -f
RewriteRule ^pages/[^/\.]+$ cache/$0.html [L,QSA]
RewriteRule ^pages/([^/\.]+)$ pages.php?p=$1 [L,QSA]
To generalize the question: insert this above the rule that should not be matched if the file exists.
RewriteCond %{REQUEST_FILENAME} !-f
Sean Bright's answer provides a nice worked example for the caching question, but this line works more broadly. In my case, I have a link shortener where people can choose custom URLs and I didn't want it to be able to override existing files such as favicon.ico. Adding this line before the rewriterule fixed that issue.