Bug 54518

Summary: mod_rewrite not adding Vary header
Product: Apache httpd-2 Reporter: Daniel Lescohier <daniel.lescohier>
Component: mod_rewriteAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: NEW ---    
Severity: normal    
Priority: P2    
Version: 2.4.3   
Target Milestone: ---   
Hardware: PC   
OS: Linux   

Description Daniel Lescohier 2013-02-01 22:09:12 UTC
mod_rewrite is supposed to append to the Vary HTTP response header any HTTP request headers used in RewriteCond statements.

This doesn't seem to work.

Relevant conf:

<Directory ${docroot}/js>
  ExpiresActive On
  ExpiresDefault "access plus 30 minutes"

  SetEnvIf Via AkamaiGHost VIA_AKAMAI_EDGE # ch.8 of Edge Server Config Guide
# Only set Edge-Control if via Akamai Edge server, so as to
# not increase size of response to non-Akamai clients
  Header set Edge-Control max-age=1800 env=VIA_AKAMAI_EDGE

  FileETag MTime Size

  RewriteEngine On
  RewriteCond %{HTTP:Accept-Encoding} (^|,)\s*(x-)?gzip(?!\s*;\s*qs\s*=\s*0([.]0{0,3})?(\s|,|$))
  RewriteRule (.*)[.]js$ $1.js.gz
</Directory>

And the response headers I get back:

HTTP/1.1 200 OK
Date: Fri, 01 Feb 2013 21:41:30 GMT
Server: Apache/2.4.3
Last-Modified: Wed, 09 Jan 2013 16:57:50 GMT
ETag: "1a27-4d2ddf660ff80"
Accept-Ranges: bytes
Content-Length: 6695
Cache-Control: max-age=1800
Expires: Fri, 01 Feb 2013 22:11:30 GMT
P3P: CP="CAO DSP COR CURa ADMa DEVa PSAa PSDa IVAi IVDi CONi OUR OTRi IND PHY ONL UNI FIN COM NAV INT DEM STA"
Content-Type: application/javascript
Content-Encoding: application/gzip


I have a workaround of adding these two lines to the Directory section:

SetEnvIf Accept-Encoding (^|,)\s*(x-)?gzip(?!\s*;\s*qs\s*=\s*0([.]0{0,3})?(\s|,|$)) VARY_ACCEPT_ENCODING
Header append Vary Accept-Encoding env=VARY_ACCEPT_ENCODING

Then I get back:

HTTP/1.1 200 OK
Date: Fri, 01 Feb 2013 21:42:53 GMT
Server: Apache/2.4.3
Last-Modified: Wed, 09 Jan 2013 16:57:50 GMT
ETag: "1a27-4d2ddf660ff80"
Accept-Ranges: bytes
Content-Length: 6695
Cache-Control: max-age=1800
Expires: Fri, 01 Feb 2013 22:12:53 GMT
Vary: Accept-Encoding
P3P: CP="CAO DSP COR CURa ADMa DEVa PSAa PSDa IVAi IVDi CONi OUR OTRi IND PHY ONL UNI FIN COM NAV INT DEM STA"
Content-Type: application/javascript
Content-Encoding: application/gzip

But adding these extra two lines of config should not be necessary, because mod_rewrite is supposed to do it. I see the code in the module (high-level part pasted below), but it doesn't seem to be doing it.

        ctx->vary = NULL;
        rc = apply_rewrite_rule(p, ctx);

        if (rc) {
            /* Regardless of what we do next, we've found a match. Check to see
             * if any of the request header fields were involved, and add them
             * to the Vary field of the response.
             */
            if (ctx->vary) {
                apr_table_merge(r->headers_out, "Vary", ctx->vary);
            }

The rewrite rule itself is working; the response has the content-encoded application/gzip file of 6695 bytes.  When I do a request with no Accept-Encoding: gzip header, I get the uncompressed file and response:

HTTP/1.1 200 OK
Date: Fri, 01 Feb 2013 21:41:06 GMT
Server: Apache/2.4.3
Last-Modified: Wed, 09 Jan 2013 16:57:48 GMT
ETag: "523d-4d2ddf6427b00"
Accept-Ranges: bytes
Content-Length: 21053
Cache-Control: max-age=1800
Expires: Fri, 01 Feb 2013 22:11:06 GMT
P3P: CP="CAO DSP COR CURa ADMa DEVa PSAa PSDa IVAi IVDi CONi OUR OTRi IND PHY ONL UNI FIN COM NAV INT DEM STA"
Content-Type: application/javascript

It's just the adding of the Vary header which is not working in the mod_rewrite module.
Comment 1 Eric Covener 2013-02-01 22:53:02 UTC
In directory context, action flags AND a substitution together don't work because it is not carried forward for the "internal redirect" used to change the URL.

Output headers specifically are not preserved across an "internal redirect".

Your best bet is configuring this outside of <Directory> context.
Comment 2 Daniel Lescohier 2013-02-04 20:37:00 UTC
I moved all my RewriteRules (and RewriteConds) out of Directory context, and edited them for the server context, and it now works.

I see now that Directory context runs in a totally different phase of request processing; the fixup phase instead of the uri2name phase, and that the fixup phase processing does an ap_internal_redirect if you rewrite the url, and therefore the request parsing phase is done over again in the subrequest, and from what you said, response headers are reset for the subrequest.

I would guess that this would mean that in Directory context, not only setting the Vary header from RewriteCond's %{HTTP:header} processing wouldn't work, but probably also the cookie|CO flag and perhaps the type|T flag would not work.

I think it'd be a good idea to add documentation about this to:

 1. RewriteCond's %{HTTP:header} documentation.
 2. RewriteRule's "Per-directory Rewrites" documentation.
 3. RewriteRule's flags documentation for each flag that won't work in Directory context.