I have a folder structure like this /img/products/{product name}/ and then the sub folders hi, low, and thumb.
I want to use htacess to force-download any files in a 'hi' or 'low' subfolder (but not 'thumb').
I was hoping something like this would work:
<FilesMatch "\(.*)(\/hi|\/low)(.*)">
ForceType applicaton/octet-stream
</FilesMatch>
Now I'm not great with regex, but that seems to work in regex testers against paths like
/img/products/active/low/something.jpg
However it's not working on the site.
Any suggestions?
Thanks
Pete
This probably should have been a ServerFault question based on what I think that you're trying to do, but since you actually can't do what you're trying to do (the way I think you're trying to do it), I'll provide two alternatives; one that likely won't work, and another that involves a PHP script (which should hopefully be alright for you, since your question history shows you asking something about PHP before).
The Problem:
First, what I think you're trying to do, so you can correct me if I'm wrong:
# Apply ForceType to anything that's in a path that matches this
<FilesMatch "img/products/[^/]+/(hi|low)/[^/]+$">
ForceType applicaton/octet-stream
</FilesMatch>
However, this won't work, because FilesMatch only examines the filename, under the assumption that you could either appropriately place the .htaccess file, or combine the directive with a Directory statement in the server or virtual server configuration.
In your case though, this isn't possible (Well, I assume anyway, maybe you do have access to the necessary configurations, but since your question is tagged .htaccess I'm guessing probably not), given that copying a .htaccess file to every folder isn't realistic.
The Solutions:
As it turns out, mod_rewrite, along with performing all sorts of voodoo in the way of filename resolution, also gives you extensions of other Apache functionality that you would not necessarily have been able to use otherwise. Case in point, we can set the MIME type using the T flag, making the easiest solution this:
RewriteEngine On
# Force the MIME-type, but don't actually perform a rewrite
RewriteRule ^img/products/[^/]+/(hi|low)/[^/]+$ - [T=application/octet-stream]
This actually works pretty well, but chances are good that your Apache installation thinks that it knows better than you, and includes a mimes.types file in the main configuration that maps the jpg extension to image/jpeg. This value takes precedence over the RewriteRule, making it ineffective in that case.
The other solution is to create a small script that acts as the go-between, passing the appropriate headers and image data from the server to the client. You would then use mod_rewrite to pass the request on to that script:
RewriteEngine On
# For an added bit of sanity, make the test pattern even more restrictive
RewriteRule ^img/products/[A-Za-z._-]+/(hi|low)/[A-Za-z._-]\.[A-Za-z]+$ imageDownloader.php
As for the script itself, to keep this answer from getting ridiculously long, I suggest taking a look at this answer or one of the other questions on this topic, keeping in mind that it's imperative that you screen the filenames that can be downloaded for reasons of security. Note that you would be able to get the original request path from $_SERVER['REQUEST_URI'], and could use that to locate the proper image.
Related
I'm kind of noob in the world of web so my apologies... I tried many things found on SO and elsewhere, but I didn't manage to do what I want. And the Apache documentation is... well too much complete.
Basically what I want to do is redirect my domain to a subfolder. I found easy solutions for this (many different actually).
http://www.foo.com/
http://foo.com/
should redirect to /bar and appear as http://foo.com/
Using the following I got the expected result :
RewriteEngine on
Options +FollowSymLinks
RewriteCond %{HTTP_HOST} ^www\.foo.com$
RewriteRule ^/?$ "http\:\/\/foo.com" [R=301,L]
RewriteRule ^((?!bar/).*)$ bar/$1 [NC,L]
But I also want the subfolder as well as filenames not to appear when explicitly entered, i.e :
http://www.foo.com/index.html
http://foo.com/index.html
http://wwww.foo.com/bar
http://foo.com/bar
http://wwww.foo.com/bar/index.html
http://foo.com/bar/index.html
Should all appear as
http://foo.com/
Is this possible ?
Obviously using .htaccess, since I'm on a virtual host.
Thanks
As Felipe says, it's not really possible, because you lose information when you do that R=301 redirect: a hard redirect like this starts a whole new request, with no memory of the previous request.
Of course, there are ways to do similar things. The easiest is to put the original request in the query string (here's a good rundown on how mod_rewrite works with query strings). Sure, the query string does show up in the URL, but most modern browsers hide the query string in the address bar, so if your goal is aesthethics, then this method would be workable.
If you really don't want to show any of the original query in the URL, you might use cookies by employing the CO flag (here are some very good examples about cookie manipulation). At any rate, the information about the original request must somehow be passed in the hard redirect.
But anyhow, and most importantly, why would you want to do something like this? It's bound to confuse humans and robots alike. Great many pages behaved like this back when frames were fashionable, and it was pretty terrible (no bookmarking, no easy linking to content, Google results with the snippet "your browser cannot handle frames", no reloading, erratic back button, oh boy, those were the days).
Speaking of which, if your content is html, you may just use a plain old iframe to achieve the effect (but I'd sincerely advise against it).
Short Version:
I wrote the question, and realized most people wouldn't want to read that much text. Consider the below reference, here's the TL;DR:
I need to 301 redirect this url http://app.com/search/foo-bar/
to this url http://app.com/#!/search/foo-bar/
and send this: /foo-bar/, or anything else past /search/ to a server side script. In this case, it's written in php.
Edit for clarity:
Current answers seem to focus on the rewrite to hashbang. That part is not the problem. The problem is that I lose any associated data when rewriting to a hashbang url, as the server side will see app.php as the location, not app.php/#!/foo-bar/ - So I need to capture foo-bar, and send it to the server somewhere other than in the URL. The rewrite works, and is not the issue. Thanks for your answers though!
Long Version:
Ok, so I have an interesting issue that has been tough for me to figure out.
The Scenario:
I have a backbone.js app that uses the hashbang for state:
app.com/#!/search/search-term/key-value/foo-bar/
In addition, I have google traffic coming to the site from the previous version that will be hitting "pretty url" style urls:
app.com/search/search-term/key-value/foo-bar/
I use an .htaccess mod_rewrite to swap the old url out for a hashbanged one if a user hits the legacy url.
I recently introduced a javascript-less bootstrapped version of the site that the site will be built on top of to gracefully downgrade and support crawlers. This is written using php.
For the php site to work, I need to pass in the values past the hashbang to the server side script, so I can figure out what to display.
The Problem:
When I transform a url and add an anchor, everything past the anchor (hashbang) is no longer sent to the request, so I don't have access to it in php.
RewriteRule search/?(.*) #!/search/$1 [R=301,NC,L]
My options for sending things to the server side then are reduced to:
1. Query String
2. Environment Variables
3. Headers
So, I tried sending things via the query string
RewriteRule search/?(.*) #!/search/$1?filter=$1 [R=301,NC,L]
Obviously that didn't work (the query is behind the anchor), so I tried it in front of the hashbang
RewriteRule search/?(.*) ?filter=$1/#!/search/$1 [R=301,NC,L]
That works, but is hideous and redundant to the end user. So, I thought I might try using environment variables.
RewriteRule search/?(.*) /!#/search/$1 [R=301,NC,L,E=FILTER:$1]
That failed, because environment variables aren't preserved through a redirect (duh). I turned to using headers:
RewriteRule search/?(.*) /#!/search/$1 [R=301,NC,L,E=FILTER:$1]
Header set filterParams "%{FILTER}e"
But for some reason, the headers aren't received by the page through the redirect. That seemed to make sense (although I've now stepped well outside of my comfort level with apache directives), so I tried echoing the header, in hopes that it would be passed, received by the second rewrite (that didn't find search), and echoed out.
RewriteRule search/?(.*) /#!/search/$1 [R=301,NC,L,E=FILTER:$1]
Header set filterParams "%{FILTER}e"
Header echo filterParams
Nada - the filter doesn't exist, so although it makes it to the server, it is null. My next thought was to attempt to employ some sort of conditional. Here was my attempt:
RewriteRule search/?(.*) legacy.php/#!/search/$1 [R=301,NC,L,E=FILTER$1]`
<FilesMatch "legacy.php">
Header set filterParams "%{FILTER}e"
</FilesMatch>
Header echo filterParams
That didn't seem to work either, so I'm stumped. I realize that I've spent so long on this that I probably have the solution within my grasp and I'm just tired of looking at it, or it's not even remotely possible, even with gross header hacking.
Anyone have a clue how to to this?
rfc1738.txt says # is not a valid url character
additionally the apache docs says # signals a comment in apache config files.
short answer is your solution is broken not your implementation
AFAIK, there's no good way to preserve variables through redirect without sticking them in the query string...
I want to redirect every post 301 redirect, but I have over 3000 posts.
If I list
Redirect permanent /blog/2010/07/post.html http://new.blog.com/2010/07/23/post/
Redirect permanent /blog/2010/07/post1.html http://new.blog.com/2010/07/24/post1/
Redirect permanent /blog/2010/07/post2.html http://new.blog.com/2010/07/25/post2/
Redirect permanent /blog/2010/07/post3.html http://new.blog.com/2010/07/26/post3/
Redirect per......
for over 3000 url redirect command in .htaccess would this eat my server resource or cause some problem? Im not sure how .htaccess work but if the server is looking at these lists each time user requests for page, I would guess it will be a resource hog.
I can't use RedirectMatch because I added date variable in my new url. Do you have any other suggestions redirecting these posts? Or am I just fine?
Thanks!
I am not an Apache expert, so I cannot speak to whether or not having 3,000 redirects in .htaccess is a problem (though my gut tells me it probably is a bad idea). However, as a simpler solution to your problem, why not use mod_rewrite to do your redirects?
RewriteRule ^/blog/(.+)/(.+)/(.+).html$ http://new.blog.com/$1/$2/$3/ [R=permanent]
This uses a regex to match old URLs and rewrite them to new ones. The [R=permanent] instructs mod_rewrite to issue a 301 with the new URL instead of silently rewriting the request internally.
In your example, it looks like you've added the day of the post to the URL, which does not exist in the old URL. Since you obviously cannot use a regexp to divine the day an arbitrary post was made, this method may not work for you. If you can drop the day from the URL, then you're good to go.
Edit: The first time I read your question, I missed the last paragraph. ("I can't use RedirectMatch because I added date variable in my new url.") In this case, you can use mod_rewrite's RewriteMap to lookup the day component of a post.
You have two options:
Use a hashmap to perform fast lookups in a static file. This means all your old URLs will work, but any new posts cannot be accessed using the old URL scheme.
Use a script to grab the day.
In option one, create a file called posts.txt and put:
/yyyy/mm/pppp dd
...for each post where yyyy is the year of the post, mm is the month, and pppp is the post name (without the .html).
When you're done, run:
$ httxt2dbm -i posts.txt -o posts.map
Then we add to to the server/virtual server config: (Note the path is a filesystem path, not a URL.)
RewriteMap postday dbm:/path/to/file/posts.map
RewriteRule ^/blog/(.+)/(.+)/(.+).html$ http://new.blog.com/$1/$2/${postday:$1/$2/$3}/$3/ [R=permanent]
In option two, use pgm:/path/to/script/lookup.whatever as your RewriteMap. See the mod_rewrite documentation for more info about using a script.
Doing the lookup in mod_rewrite is better than just redirecting to a script which looks up the date and then redirects to the final destination because you should never redirect more than once. Issuing a 301 or 302 incurs a round trip cost, which increases the latency of your page load time.
If you have some way in code to determine the day of a post, you can generate the rewrite on the fly. You can setup a mod_rewrite pattern, something like .html and set up a front controller pattern to calculate the new url from the old and issue the 301 header.
With php as an example:
$_SERVER['REQUEST_URI']
will contain the requested url and
header("Location: http://new.blog.com/$y/$m/$d/$title/",TRUE,301);
will send a redirect.
That's... a lot of redirects. But the first thing I would tell you, and probably the only thing I can tell you without qualification, is that you should run some tests and see what the access times for your blog are like, and also look at the server's CPU and memory usage while you're doing it. If they're fairly low even with that giant list of redirects, you're okay as long as your blog doesn't experience a sudden increase in traffic. (I strongly suspect the 3000 rewrites will be slowing Apache down a lot, though)
That being said, I would second josh's suggestion of replacing the redirects with something dynamic. Like animuson said, if you're willing to drop the day from the URL, it'll be easy to set up a RewriteRule directive to handle the redirection. Otherwise, you could do it with a PHP script, or generally some code in whatever scripting language you (can) use. If you're using one of the popular blog engines, it probably contains code to do this already. Basically you could do something like
RewriteRule .* /blog/index.php
and just let the PHP script sort out which post was requested. It has access to the database so it'll be able to do that, and then you can either display the post directly from the PHP script, or to recover your original redirection behavior, you can send a Location header with the correct URL.
An alternative would be to use RewriteMap, which lets you write a RewriteRule where the target is determined by a program or file of your choice instead of being directly specified in the configuration file. As one option, you can specify a text file that contains the old and new URLs, and Apache will handle searching the file for the appropriate line for any given request. Read the documentation (linked above) for the full details. I will mention that this isn't used very often, and I'm not sure how much faster it would be compared to just having 3000 redirects.
Last tip: Apache can be significantly faster if you're able to move the configuration directives (like Redirect) into the server or virtual host configuration file, and disable reading of .htaccess entirely. I would guess that moving 3000 directives from .htaccess into the virtual host configuration could make your server considerably faster. But even moving the directives into the vhost config file probably wouldn't produce as much of a speedup as using a single RewriteRule.
It's never a good idea to make a massive list of Redirects. A better programming technique is to simply redirect the pages without that date variable then have a small PHP snippet that detects if it's missing and redirects to the URL with it included. The long list looks tacky and slows down Apache because it's checking that URL (any every other URL that might not even be affected by this) against each line. If it were only 5 or so, I'd say fine, but 3,000 is a definite NO.
Although I'm not a big fan of this method, a better choice would be to redirect all those URLs normally using a single match statement, redirecting them to the page without the date part, or with a dash or something, then include a small PHP snippet to check if the date is valid and if not, rewrite the path again to the correctly formed URL.
Honestly, if you didn't have that part there before, you don't need it now, and it will probably just confuse the search engines changing the URL for 3,000 posts. You don't really need a date in the URL, a good title is much more meaningful not only to users, but also to search engines, than a bunch of numbers.
I have a number of pages in my site, as one would expect.
For example:
index.php
submit.php
view.php?id=blah
I want these rewritten like
index/
submit/
view/blah
Whats the best way of doing this?
The ways of handling it through .htaccess Rewrite can generate a bit of a headache. It seems like a basic answer, but unless you're up on your regular expressions, you're going to be lost.
There's a few ways of handling it, however. I'm assuming that you only have index.php, submit.php, and view.php with an id associated.
RewriteEngine On
RewriteRule ^(index|submit|view)/(\d+)$ /$1.php?id=$2
RewriteRule ^(index|submit|view)/(\d+)$ /$1.php
Here's how it works: You tell .htaccess to turn on the Rewrite Engine. Step 2, you give the site the parameter that tells it how it's done. The parameter in this case reads: At the beginning of the url, after the domain name, check for index, submit, or view. If those exists, it'll look for the id. If both those exists, it will return the value into PHP as /(index, submit, or view)?id=$id.
The second one is in case the ID isn't viable.
This is a simple way of handling it. A more complex way of handling it would be...
RewriteEngine On
RewriteRule ^([a-z]+)/?(\d+)?$ /$1.php?id=$2
This will load whatever is written in regular alphabetical characters of upper and lower case letters only, use that as the filename, then detects if id is even necessary--it will load without.
You should be sure to include some safeguards on your $_GET lines to return errors if the names are erroneous or doesn't return anything of worth.
You should play around with it, research Regular Expressions over a pot of coffee and something alcoholic (I believe that regexp is the #1 cause of alcoholism in modern programmers, but I could be wrong ;-P ...) til you find a scheme that fits comfortably into your system.
As a side-note, you can have as many RewriteRules as you need, but they always get processed from the top one first. I realize this sounds like common sense, but it's important to know when debugging.
I want to a rewrite rule such that if a user goes to the URL example.org/stuff/junk.jpg the rule will process and end up at re-writer.php but if the user goes to example.org/stuff/hackingisawesome/junk.jpg the rule will not be triggered and they will get a standard 404 (or a page, if one should exist).
I can't tell, based on the environmental variables, if this is possible without some fairly fancy regex.
So does anyone know of either:
a) a way this is already built into the mod_rewrite syntax, or
b) a good, reliable way of handling this with regular expressions?
Links to documentation or tutorials welcome. I'm just feeling clueless on where to go next.
Oh, and I can imagine the ways I could simply have the script that the rule redirects to simply deliver the 404, but I'd rather only use the rule when the conditions exist.
Try this:
RewriteRule ^stuff/[^/]+$ re-writer.php
This will rewrite all requests to /stuff/… with only one additional path segment to re-writer.php.