I would like to use a rewrite rule that is executed when a symlink exists but is broken.
So the scenario's would be:
Symlink does not exist: normal 404/403 error.
Symlink exists but is broken: generate-cache.php is called.
Symlink exists and is working: target file is loaded normally.
For example:
## Symlink does not exist.
GET /links/cache/secret.jpg
404 Not Found
## Symlink is broken.
GET /links/cache/secret.jpg
Links to /images/cache/secret.jpg
Because it's broken, rewrites to: generate-cache.php?path=cache/secret.jpg
200 OK
## Symlink works.
GET /links/cache/secret.jpg
Links to /images/cache/secret.jpg
200 OK
Update: I want to avoid using PHP to do these checks, because it causes a performance bottleneck. Outputting the file through PHP if it exists causes PHP to lock. Also I have no option to use multiple PHP threads or install additional apache modules.
I don't know of a way of testing for a broken symlink in mod_rewrite (-l checks for the existence of a symlink, but doesn't attempt to follow it), which may mean you'd need to write some kind of callback in PHP (or some other language).
An alternative approach would be to rewrite all requests, and build this logic in PHP:
if the file exists in the cache directory, set appropriate headers and use readfile() to output the data
if the symlink exists (or just an empty file with the right name in a "control" directory; I presume you have some other process creating the symlinks, so this could be amended to touch files instead), do appropriate generation
if the symlink/control file doesn't exist, send a 404 header and immediately exit
Another variation, slightly more efficient, would be to let Apache serve the cached image directly if it exists, and rewrite to PHP for steps 2 and 3. Something like this:
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteRule /links/cache/(.*) generate-cache.php?path=$1
And in PHP
if ( ! file_exists('cache_control/' . $_GET['path'] )
{
header('HTTP/1.1 404 Not Found');
exit;
}
else
{
// Control file exists, so this is an allowable file; carry on...
generate_file_by_whatever_magic_you_have( 'links/cache/' . $_GET['path'] );
header('Content-Type: image/jpeg'); // May need to support different types
readfile( 'links/cache/' . $_GET['path'] );
exit;
}
Assuming you can replace the symlinks with control files, and the names match up directly (i.e. the target of your symlink can be "guessed" from its name), you could move the control file check into mod_rewrite as well:
# If the requested file doesn't exist (if it does, let Apache serve it)
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
# Match the basic path format and capture image name into %1
RewriteCond %{REQUEST_FILENAME} /links/cache/(.*)
# Check if a cache control file exists with that image name
RewriteCond %{DOCUMENT_ROOT}/cache_control/%1 -f
# If so, serve via PHP; if not, no rewrite will happen, so Apache will return a 404
RewriteRule /links/cache/(.*) generate-cache.php?path=$1
Related
Background:
I've recently created a dynamic image resizing script in PHP so that I can give it an image path somewhere within the server + get parameter of width, and it'll return this image, resized according to the query parameter and throw the image into a cache folder for future use.
Afterwards, if the same image, with the same width is requested, it goes to that PHP script, and the script checks if the file already exists, and if it does, it'll just output it. (It will do file_get_contents(), and then echo it with the appropriate header)
The challenge:
What I want to do is to bypass the PHP script if the file already exists. I want .htaccess to check if the file exists in the cache folder, and then simply go to that file, instead of the PHP script. That would be simple enough, except the filename includes the modified width.
So, for instance:
If the file relative path is images/products/36sh542dhs.jpg, and I want it modified with width 100px, the request URL will look like this si/images/products/36sh542dhs.jpg?d=100. The file will be stored as follows: resized_images/cache/images/products/36sh542dhs---D100.jpg.
Is there any conceivable way to get .htaccess to take in si/images/products/36sh542dhs.jpg?d=100, break it up, remove si, add resized_images/cache instead, AND modify the filename to stick the D---100 right before the "dot extension" part?
The best algorithm that comes to my mind regarding your issue is to: always use the /resized_images/cache/images/products/36sh542dhs---D100.jpg path.
In .htaccess you can have a line ErrorDocument 404 /404-error-handler.php. This will catch all 404 errors.
In 404-error-handler.php file you check the value of $_SERVER['REQUEST_URI']. If it contains your path, you include the logic of image generation and saving else you return a custom 404 error page.
This way you will obtain what you have asked: htaccess check if file exists, if no it goes up the 404 custom php script which will check if the URL matches your image-generation-url and either generate and cache the image, either show a 404 page. If the file exists, it will be served to client.
Another way (kind of the way you were suggesting)...
Link to:
/si/images/products/36sh542dhs---D100.jpg
Cached images stored in:
/resized_images/cache/images/products/36sh542dhs---D100.jpg
Script that actually resizes images:
/scripts/image-resizer.php?image=36sh542dhs&size=100
By linking to a different URL you can keep your cache directory and image script "private" - which can be easily moved to different locations if you wish.
Using mod_rewrite in .htaccess:
RewriteEngine On
# Serve cached image if it exists
RewriteCond %{DOCUMENT_ROOT}/resized_images/cache/$1 -f
RewriteRule ^si/(images/products/\w---D\d{2,4}\.jpg)$ resized_images/cache/$1 [L]
# Otherwise send request to PHP image resizing script
RewriteRule ^si/images/products/(\w)---D(\d{2,4})\.jpg$ scripts/image-resizer.php?image=$1&size=$2 [L]
Alternatively, you link directly to the cached image (as mentioned in #besciualex's answer) and rewrite the request to your image resizer script when it doesn't exist, rather than relying on the error handler:
# Send request to PHP image resizing script when cached image does not exist
RewriteCond %{REQIEST_FILENAME} !-f
RewriteRule ^resized_images/cache/images/products/(\w)---D(\d{2,4})\.jpg$ scripts/image-resizer.php?image=$1&size=$2 [L]
Only requests that look-like cached images are checked.
I'm trying to rewrite all requests directed at files/folders in the root directory to a subdirectory called "Web". This simple .htaccess code:
Options +FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !/Web
RewriteRule ^(.*)$ Web/$1 [QSA,L]
is working fine for most scenarios, but, unexpectedly, php files directly accessed through the browser are still being looked for in the root directory, even though the RewriteRule does not discriminate between file types (it get's weirder though, see below). With this, I'm getting the following results...
Working:
Directories: domain.com/test/ will correctly rewrite to domain.com/Web/test/ independent of whether the folder exists in either the root or the Web directory or both, as expected
All tested non-php file types, e.g. domain.com/test.txt correctly rewrite to domain.com/Web/test.txt, again independent of whether the file exists in either the root or the Web directory or both, as expected (i.e. either displaying the file that I placed in the Web directory or throwing the expected 404 error message for not finding the file in the subfolder if it does not exist)
HOWEVER, trying to open php files in the browser (e.g. domain.com/test.php) results in the following, unexpected behavior:
If the php file DOES exist in the Web subfolder but does NOT exist in the root directory (default scenario), I'm getting an unexpected 404 "The requested URL /test.php was not found on this server", i.e. the server is looking for the file in the root directory, ignoring the .htaccess rewrite, even though the rewrite clearly is working for other file types, see above
If the php file does NOT exist in the Web subfolder but DOES exist in the root directory, I'm getting a 404 "The requested URL /Web/ test.php was not found on this server.", i.e. NOW the server is trying to open the file from the expected subfolder-location.
If the php file DOES exist in the Web subfolder AND in the root directory, the server displays the test.php file version that was placed in the subfolder.
My conclusions are:
all non-php file types are behaving according to the .htaccess file, independently of whether the requested files/folders exist in the root directory and/or the subfolder - the behavior is always as expected
all requests for php files follow the htaccess rule ONLY IF a corresponding file exists in the root directory, even though the file residing in the root directory is never actually served to the browser, in accordance with the rewrite rule
I find this extremely weird, since there's no RewriteCond to specify absence or presence of the file. The Regex clearly triggers for php files, but only in a complicated, counterintuitive fashion. The one RewriteCond I do have only prevents infinite looping.
I have tried adding RewriteCond's that match both absence and presence of file, but it did not fix the file problem.
Does anyone have an idea? Why are php-files treated differently from all other types in this scenario?
I want to develop a new website for a customer who is currently running a TYPO3 website on his server.
I'd like to exclude a subdirectory from the TYPO3 RewriteEngine (realurl?) so I have normal access to that subdirectory.
www.domain.com/dev
for example.
I have access to that site but as soon as I put a .htaccess file in that certain directory to protect it with a htpasswd. I get a TYPO3 Error:
Reason: Segment "dev" was not a keyword for a postVarSet as expected!
In .htaccess find this
# Stop rewrite processing, if we are in the typo3/ directory or any other known directory
# NOTE: Add your additional local storages here
RewriteRule (?:typo3/|fileadmin/|typo3conf/|typo3temp/|uploads/|favicon\.ico) - [L]
and add your directory there
# Stop rewrite processing, if we are in the typo3/ directory or any other known directory
# NOTE: Add your additional local storages here
RewriteRule (?:typo3/|fileadmin/|typo3conf/|typo3temp/|uploads/|favicon\.ico|dev/) - [L]
I found the solution and it was anything but straightforward.
I needed to add this line to my .htaccess in the protected subdirectory:
ErrorDocument 401 "Not authorized"
Apparently there is a conflict between URL rewriting and Basic Auths in cases just like mine. The Server writes a "401 Unauthorized" Header and looks for an existing error document based on a pre-defined path which then gets rewritten.
There are a way, via .htaccess to check if .css or .js has your own minified version (.min.css and .min.js, respectively) and redirect to this file, but need to have the same modified time.
For instance:
FILE MODIFIED TIME
/file.css 01-15-2014T19:28:12
/file.min.css 01-15-2014T19:28:12
/file.js 01-15-2014T19:28:12
/file.min.js 01-15-2014T19:27:47
My idea is: when someone access the file.css, the .htaccess will check if file.min.css is available and have the same modified time. If yes, it'll returned (via rewrite, redirect), instead of file.css.
But, if I access file.js, even existing the file.min.js, the .htaccess will return file.js because file.min.js is outdated (not have same date/hour).
Is it possible only with .htaccess?
For your first requirement try this rule in your DOCUMENT_ROOT/.htaccess file:
RewriteEngine On
# if a min version is available then load it
RewriteCond %{DOCUMENT_ROOT}/$1.min.$2 -f
RewriteRule ^([^.]+)\.(css|js)$ /$1.min.$2 [L,NC]
However mod_rewrite cannot check modification timestamp of your css/js files to make decision based on that. You might need to handle that in your server side code.
Ok, I'm clueless here...
I need to rewrite a directory structure and all sub-directories within it to a directory within the same server, but a root that is before the directory.
For example:
http://www.mydomain.com/Themes/default/css/folder
and all directories called upon after folder. Such as folder/sub_folder or folder/afolder/anotherfolder, it needs to include ALL sub-directories within the folder directory.
should be redirected to this:
http://www.mydomain.com
How do I do this via a .htaccess file within the folder path http://www.mydomain.com/Themes/default/css/folder?
Please someone help.
Thanks guys :)
The files within the directory structure still need to be accessible for that structure when called via PHP, but I don't want people being able to browse to http://www.mydomain.com/Themes/default/css/folder and be shown all subdirectories within that folderpath and/or all files. Same thing for all sub-directories that follow that folder path.
I'd like to be able to place the .htaccess file within the http://www.mydomain.com/Themes/default/css/folder directory on the server, but don't know exactly what code to use for this.
ALSO, even more challenging... The domain name can change, so I'd rather not use the domain name within the .htaccess file, instead perhaps use .. or . to go up a directory or a different method of grabbing the domain name within the .htaccess file.
Create a .htaccess file in /Themes/default/css/folder and place these lines there (it requires mod_rewrite):
Options +FollowSymLinks -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ http://%{HTTP_HOST}/ [R=301,L]
It will redirect (301 Permanent Redirect) all requests to a folder to a homepage. If file is requested, it will allow it.
If you want to have it working for folders as well as files then remove the RewriteCond line -- it will redirect ALL requests (even for non-existing URLs) to a homepage.
If you will see "500 Internal Server Error" after creating such file, then it is your server configuration: mod_rewrite may not be enabled or it's directives (RewriteRule, RewriteCond, RewriteEngine) are not allowed to be placed in .htaccess. In any case -- check Apache's error log for exact error message (it will give you the exact reason).
http://www.besthostratings.com/articles/prevent-directory-listing.html
IndexIgnore *