Using the following rule: RewriteCond %{QUERY_STRING} (.+) RewriteRule ^(.*\.html)$ $1@%1 [T=text/html] To convert some urls like this: http://example.com/index.html?image.jpg to access files like this (produced by a batch downloading tool): index.html@image.jpg the rewrite rule needs the T option because otherwise apache will send a jpg mime type, based on the file name. The log for this rewrite is: [rid#aa1218/initial] (3) [per-dir /tmp/www/] strip per-dir prefix: /tmp/www/test/index.html -> test/index.html [rid#aa1218/initial] (3) [per-dir /tmp/www/] applying pattern '^(.*\.html)$' to uri 'test/index.html' [rid#aa1218/initial] (2) [per-dir /tmp/www/] rewrite test/index.html -> test/index.html@image.jpg [rid#aa1218/initial] (3) [per-dir /tmp/www/] add per-dir prefix: test/index.html@image.jpg -> /tmp/www/test/index.html@image.jpg [rid#aa1218/initial] (2) [per-dir /tmp/www/] remember /tmp/www/test/index.html@image.jpg to have MIME-type 'text/html' [rid#aa1218/initial] (2) [per-dir /tmp/www/] strip document_root prefix: /tmp/www/test/index.html@image.jpg -> /hcStore/8x10/index.html@image.jpg [rid#aa1218/initial] (1) [per-dir /tmp/www/] internal redirect with /hcStore/8x10/index.html@image.jpg [INTERNAL REDIRECT] [rid#ab0208/initial/redir#1] (3) [per-dir /tmp/www/] strip per-dir prefix: /tmp/www/test/index.html@image.jpg -> test/index.html@image.jpg [rid#ab0208/initial/redir#1] (3) [per-dir /tmp/www/] applying pattern '^(.*\.html)$' to uri 'test/index.html@image.jpg' [rid#ab0208/initial/redir#1] (1) [per-dir /tmp/www/] pass through /tmp/www/test/index.html@image.jpg Note the lack of a log entry indicating that the forced mime type is actually being set, which should be in the redir#1 block. livehttpheaders confirmed the forced mime type did not find its way to the browser. A couple of extra log points in hook_mimetype showed that it wasn't finding the force mime type note for the request. This is probably because we are on a new request second time through (the internal redirect), and so we should be using r->prev->notes instead of r->notes. Potentially the forced mime type could be a couple of redirects back, so all previous requests should probably(?) be checked until one is found, or we have been through them all. I'd suggest something like this as a possible fix (works fine for me): static int hook_mimetype(request_rec *r) { const char *t; request_rec *p = r; /* now check if we have to force a MIME-type */ while (p) { t = apr_table_get(p->notes, REWRITE_FORCED_MIMETYPE_NOTEVAR); if (t != NULL) { rewritelog(r, 1, "force filename %s to have MIME-type '%s'", r->filename, t); ap_set_content_type(r, t); return OK; } p = p->prev; } return DECLINED; } Richard.
Whether this is a bug or not depends on the point of view. It may be desired that the internal redirect is run without a memory. I'd leave the behavior as is, because otherwise it could break many existing stuff out there. I suggest you setup a second rule, which matches for the 'html@' stuff and forces the mime type then (without changing the url so no redirect occurs). Note that your problem doesn't happen, when the rules are executed in server context, because there no redirect happens at all.
From the point of view of mod_rewrite working as documented it is definitely a bug. This memory effect would only be for setting the mime type based on a previous rewrite having taken place. And only where a user supplied rewrite rule has explicitly requested this mime type override. It doesn't cause any memory for any other part of an internal redirect processing. Can you suggest anywhere this change could be a problem? I can't think of one, though I obviously don't know it as well as you do. If you still think the risk in fixing this is too high, perhaps it could be changed in 2.1? Or even failing that this bug should be passed to Documentation because the manual is wrong. Even the example php source rewrite rule in the doc doesn't work if used in a .htaccess file. If this feature is not available and needs to be worked around for per directory config it needs to be explained in the documentation.
> If you still think the risk in fixing this is too high, perhaps it could be > changed in 2.1? The problem is still present in version 2.2.2. Any idea how this issue could be resolved? We'd either need a fix of code, or a fix of documentation stating that if the Rewrite Rule changes the file name or URI, leading to an internal redirect, the forced MIME-Type will be lost.
Would it be possible to leave T|type and add a FT|ForceType that is remembered after redirection? It would be really handy, since we store uploads as UUID strings and keep meta-data, like original filename and mimetype, in a database. Having the ability to do a rewrite to the UUID file whilst preserving the mimetype and original filename would be a valuable feature.
I'm according with Richard Antony Burton, from the point of view of mod_rewrite working as documented it is definitely a bug. Additionally the solution André Malo's solution does not work for me: <Directory /> RewriteEngine On RewriteRule (.+\.html)$ $1.gz RewriteRule . - [T=text/html,L] </Directory> Result: Content-Type: application/x-gzip Expected result: Content-Type: test/html Last RewriteLog line: force filename redirect:/dir/file.html.gz to have MIME-type 'text/html'
Changed to Version 2.2.3 - Problem still exists here too (Red Hat) Testcase: AddHandler php5-script .css RewriteEngine On RewriteRule ^(.+\.css)$ $1 [T=text/css] Result: Content-Type: text/html Expected: Content-Type: text/css
This bug is also trouble me. If I use the following rule: RewriteCond %{REQUEST_URI} \.t$ RewriteRule ^(.*)\.t$ $1 [T=text/html] The flag T will never work. But when I modify it to the following rule: RewriteCond %{REQUEST_URI} \.t$ RewriteRule ^(.*)\.t$ $1 RewriteRule . - [T=text/html] Then, the Content-Type in header is set properly. But now, I need a more flexible rewrite rule that I can set the MIME-Type in the request url, so I modify it again to: RewriteCond %{REQUEST_URI} \.t$ RewriteRule ^([a-zA-Z]+/[a-zA-Z-]+)/(.*)\.t$ $2 [E=MIMETYPE:$1] RewriteRule . - [T=%{ENV:MIMETYPE}] But it doesn't work anymore. According to your explanation above, the last rule should not lead to a internal redirect because it changes nothing but the mimetype like the previous one. But they behave quite differently!
> If I use the following rule: > RewriteCond %{REQUEST_URI} \.t$ > RewriteRule ^(.*)\.t$ $1 [T=text/html] > > The flag T will never work. But when I modify it to the following rule: That's because you're in per-directory context and by changing the URL you've asked Apache to recalculate all of this. > RewriteCond %{REQUEST_URI} \.t$ > RewriteRule ^(.*)\.t$ $1 > RewriteRule . - [T=text/html] > > Then, the Content-Type in header is set properly. > > But now, I need a more flexible rewrite rule that I can set the MIME-Type in > the request url, so I modify it again to: > RewriteCond %{REQUEST_URI} \.t$ > RewriteRule ^([a-zA-Z]+/[a-zA-Z-]+)/(.*)\.t$ $2 [E=MIMETYPE:$1] > RewriteRule . - [T=%{ENV:MIMETYPE}] The condition looks completely unnecessary, which makes me think you'd benefit from discussing your config, with a rewritelog, on the users list. > > But it doesn't work anymore. > > According to your explanation above, the last rule should not lead to a > internal redirect because it changes nothing but the mimetype like the previous > one. But they behave quite differently! It could be failing to do what you want for any number of reasons. Primarily, you need to make sure that T= rule runs in a round of rewrite processing that on the whole does not do a substitution -- not just in the one rule with the T=.
This was already documented in trunk and is now documented in 2.2.x. I enhanced it a bit. Configuration assistance is available via the users mailing list, not bugzilla.