I want to remove some headers that my webserver injects into the header response but provides no ability to remove
I only see helper functions to add headers, but no way to remove them in Grape and I cant seem to find the variable where headers are contained
I specifically want to strip out X-Powered-By which gets injected by my Passenger server, passenger only seems to provide the ability to remove the version number but not the ability to remove it completely... which seems insecure...
i'd like to do this in my before block
before do
## I see i can easily add headers, but not remove
header 'X-Robots-Tag', 'noindex'
## how do I remove headers here?
## ...
end
## ... some routes
get '/' do
## ...
end
Many libraries provide the ability to remove headers, for instance:
ExpressJS
app.use(function (req, res, next) {
res.header('Pragma', 'no-cache');
res.removeHeader('Pragma');
next();
});
Rails
response.headers['Connection'] = 'Closed'
remove_keys = %w(X-Runtime Cache-Control Server Etag Set-Cookie)
response.headers.delete_if{|key| remove_keys.include? key}
While a bit awkward in my opinion the Docs do show you can delete a header by just passing the key and not the value (or passing the value as nil)
For your Example:
before do
## I see i can easily add headers, but not remove
header 'X-Robots-Tag', 'noindex'
## how do I remove headers here?
header 'X-Powered-By'
end
I guess the concept is that a header set to a non value is not really a header at all.
I am using fiddler autoresponder to return a different JS file than the one loaded from my server originally. The adjusted file uses:
HTTP/1.1 200 OK
Cache-Control: private, max-age=31536000
Content-Type: application/javascript
...other headers
Content-Length: 37010
...the javascript code
At the top of the file, this Content-Length header is not automatically adjusted to the edited file though. So I have to try and load my changes, my app will crash because the Content-Length is wrong, but then I check fiddlers 'transformer' tab to see how many bytes my request body actually is, update that in my modified file, refresh again and then it works.
I have tried to change the encoding to chunked, so that I could leave out the Content-Length header, but I don't think my app knows how to decode chunked for some reason.
So my question is, is there any way to automatically update the Content-Length in the auto-responder?
You can simply use FiddlerScript in Fiddler classic to build your auto responder. That way the content-length is set automatically:
static function OnBeforeRequest(oSession: Session) {
// ... some other FiddlerScript code
// host is e.g. "localhost:3000"
if (oSession.HostnameIs("<host>") && oSession.uriContains("<file name>.js")) {
oSession.utilCreateResponseAndBypassServer();
oSession["ui-backcolor"] = "lime"; // Makes it more visible
if (!oSession.LoadResponseFromFile("<file path>.js")) {
throw new ApplicationException("LoadResponseFromFile Failed!! ");
}
// Just loads forever if Content-Length is not added
oSession.oResponse["Content-Length"] = oSession.responseBodyBytes.GetLength(0);
}
}
varnish-devicedetect lets me return different responses based on User-agent:
...
elsif (req.http.User-Agent ~ "(?i)ipad") { set req.http.X-UA-Device = "tablet-ipad"; }
elsif (req.http.User-Agent ~ "(?i)ip(hone|od)") { set req.http.X-UA-Device = "mobile-iphone"; }
...
and lets users opt into a different device experience by setting a cookie:
if (req.http.Cookie ~ "(?i)X-UA-Device-force") {
...
}
Do I need to add Vary: Cookie to get correct client caching behavior?
For example:
I set only Vary: User-agent
A user browses to example.com/beep
They opt into the desktop experience, setting X-UA-Device-force: pc.
They reload example.com/beep.
Will their browser use the cached mobile version, since only Cookies, not User-agent changed?
Yes, you need to set a Vary: Cookie header to get the correct behaviour since different cookies would retrive different output.
You'll need also to take care of the cookie on vcl_recv setting the appropriate X-UA-Device in order to hash items correctly in varnish.
If you vary cookie, then you might as well throw caching out the window since most users will have different cookies (especially so if you have any analytics on your site) so you will have a low hit rate and multiple copies of the same data in your cache.
Rather than vary cookie, do a hash on the specific cookie value like so:
if (req.http.cookie ~ "(?i)X-UA-Device-force" ) {
hash_data("deviceforce");
} else {
hash_data("nodeviceforce");
}
I have a rewrite rule which redirects to / if no Accept-Language header is present and someone attempts to visit ?lang=en. It works fine, except for the headers returned. Vary: Accept-Language is missing from the response.
RewriteCond %{HTTP:Accept-Language} ^$
RewriteCond %{QUERY_STRING} ^lang=en
RewriteRule ^$ http://www.example.com/? [R=301,L]
The Apache documentation specifies:
If a HTTP header is used in a condition this header is added to the Vary header of the response in case the condition evaluates to to true for the request. It is not added if the condition evaluates to false for the request.
The conditions are definitely matching and redirecting, so I don't understand why Apache isn't adding the language vary. One can see why this would be a real problem if a proxy were to cache that ?lang=en and always redirect to / regardless of the Accept-Language header sent.
After peeking into the seedy underbelly of Apache's request handling system, it turns out that the documentation is somewhat misleading...But before I get into the explanation, from what I can tell you're at the mercy of Apache on this one.
The Client Problem
First, the header name will not be added to the Vary response header if it is not sent by the client. This is due to how mod_rewrite constructs the value for that header internally.
It looks up the header by name using apr_table_get(), the request's header table, and the name that you provided:
const char *val = apr_table_get(ctx->r->headers_in, name);
If name is not a key in the table, this function will return NULL. This is a problem, because immediately after this is a check against val:
if (val) {
// Set the structure member ctx->vary_this
}
ctx->vary_this is used on a per-RewriteCond basis to accumulate header names that should be assembled into the final Vary header*. Since no assignment or appending will occur if there is no value, a referenced (but not sent) header will never appear in Vary. The documentation doesn't explicitly state this, so it may or may not have been what you expected.
*As an aside, the NV (no vary) flag and ignore-on-failure functionality is implemented by setting ctx->vary_this to NULL, preventing its addition to the response header.
However, it's possible that you sent Accept-Language, but it was blank. In this case, the empty string will pass the above check, and the header name will be added to Vary by mod_rewrite from what's described above. Keeping this in mind, I used the following request to diagnose what was going on:
User-Agent: Fiddler
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language:
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Host: 129.168.0.123
This doesn't work either, but why? mod_rewrite definitely sets the headers when the rule and condition match (ctx->vary is an aggregate of ctx->vary_this across all checked conditions):
if (ctx->vary) {
apr_table_merge(r->headers_out, "Vary", ctx->vary);
}
This can be verified with a log statement, and r->headers_out is the variable used when generating the response headers. Given something is definitely going wrong though, there must be trouble after the rules are executed.
The .htaccess Problem
Currently, you appear to be defining your rules in .htaccess, or a <Directory> section. This means that mod_rewrite is operating in Apache's fixup phase, and the mechanism it uses to actually perform rewrites here is very messy. Let's assume for a second there's no external redirection, since you had problem a even without it (and I'll get to the issue with the redirect later).
After you perform a rewrite, it's far too late in the request processing for the module to actually map to a file. What it does instead is assign itself as the request's "content" handler and when the request reaches that point, it performs a call to ap_internal_redirect(). This leads to the creation of a new request object, one that does not contain the headers_out table from the original.
Assuming that mod_rewrite causes no further redirects, the response is generated from the new request object, which will never have the appropriate (original) headers assigned to it. It is possible to get around this by working in a per-server context (in the main configuration or in a <VirtualHost>), but...
The Redirect Problem
Unfortunately, it turns out that it's largely irrelevant anyway, since even if we do use mod_rewrite in a server context, the path the response takes in the event of a redirect still causes the headers that the module set to be tossed out.
When the request is received by Apache, through a chain of function calls it makes its way to ap_process_request(). This in turn calls ap_process_request_internal(), where the bulk of the important request parsing steps occur (including the invocation of mod_rewrite). It returns an integer status code, which in the case of your redirect happens to be set to 301.
Most requests return OK (which has a value of 0), leading immediately to ap_finalize_request_protocol(). However, that's not the case here:
if (access_status == OK) {
ap_finalize_request_protocol(r);
}
else {
r->status = HTTP_OK;
ap_die(access_status, r);
}
ap_die() does some additional manipulation (like returning the response code back to 301), and in this particular case ends with a call to ap_send_error_response().
Luckily, this is finally root of the problem. Though it might seem like it, things are not "assbackwards", and this causes the destruction of the original headers. There's even a comment about it in the source:
if (!r->assbackwards) {
apr_table_t *tmp = r->headers_out;
/* For all HTTP/1.x responses for which we generate the message,
* we need to avoid inheriting the "normal status" header fields
* that may have been set by the request handler before the
* error or redirect, except for Location on external redirects.
*/
r->headers_out = r->err_headers_out;
r->err_headers_out = tmp;
apr_table_clear(r->err_headers_out);
if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
if ((location != NULL) && *location) {
apr_table_setn(r->headers_out, "Location", location);
}
//...
}
//...
}
Take note that r->headers_out is replaced, and the original table is cleared. That table had all of the information that was expected to show up in the response, so now it is lost.
Conclusion
If you don't redirect and you define the rules in a per-server context, everything does seem to work correctly. However, this is not what you want. I can see a potential workaround, but I'm not sure if it would be acceptable, not to mention the need to recompile the server.
As for the Vary: Accept-Encoding, I can only assume it comes from a different module that behaves in a way that allows the header to sneak through. I'm also not sure why Gumbo didn't have an issue when trying it.
For reference, I was looking at the 2.2.14 and 2.2 trunk source code, and I was modifying and running Apache 2.2.15. There doesn't appear to be any significant differences between the versions in the related code sections.
You may want to try something like the following as a workaround:
<LocationMatch "^.*lang\=">
Header onsuccess merge Vary "Accept-Language"
</LocationMatch>
To specifically set the Vary: Accept-Language HTTP response header on the redirect response only (which is what's expected here), you would need to set an environment variable (eg. VARY_ACCEPT_LANGUAGE) as part of the redirect rule and use this to set the header conditionally with the Header directive.
You also need to use the always condition (as opposed to the default onsuccess) with the Header directive in order to set this on the 3xx response (ie. non-200 reponses).
For example:
# Redirect requests that have an empty Accept-Language header and "lang=en" is present
RewriteCond %{HTTP:Accept-Language} ^$
RewriteCond %{QUERY_STRING} ^lang=en
RewriteRule ^$ /? [E=VARY_ACCEPT_LANGUAGE:1,R=301,L]
# Set/Merge "Vary" header on Accept-Language redirect
Header always merge Vary "Accept-Language" env=VARY_ACCEPT_LANGUAGE
HOWEVER, the Vary header shouldn't only be set on the redirect response (when the Accept-Language header is empty), it needs to be set on all responses to requests for /?lang=en, regardless of what the Accept-Language HTTP request header is actually set to. So, relying on Apache to set this header using only the redirect would not be sufficient anyway (even if it did set the header on the response as initially expected).
In order to set the appropriate Vary header on all responses to requests for /?lang=en, including the redirect then do it like this:
# Set env var if "/?lang=en" is requested
RewriteCond %{QUERY_STRING} ^lang=en
RewriteRule ^$ - [E=VARY_ACCEPT_LANGUAGE:1]
# Redirect requests that have an empty Accept-Language header and "lang=en" is present
RewriteCond %{HTTP:Accept-Language} ^$
RewriteCond %{QUERY_STRING} ^lang=en
RewriteRule ^$ /? [R=301,L]
# Set/Merge "Vary" header on all responses from "/?lang=en"
Header always merge Vary "Accept-Language" env=VARY_ACCEPT_LANGUAGE
Note, however, that if you have additional internal rewrite directives that cause the rewrite engine to start over then the env var VARY_ACCEPT_LANGUAGE is renamed to REDIRECT_VARY_ACCEPT_LANGUAGE and the above Header directive will not be successful. You'll probably need an additional directive to handle this. For example:
Header always merge Vary "Accept-Language" env=REDIRECT_VARY_ACCEPT_LANGUAGE
I wrote one script which is running on the linux machine.It fetches data from one url and displays the content on a page.
The problem I am facing is some time if I refresh the page 4-5 times it displays the old content and not the latest one.
The problem could be because of caching proxy which is still caching old content.
Please tell me what to write in the script which automatically delete the caching proxy.
You should try using the Cache-Control HTTP header in your request, to tell the proxy (if there is one) not to cache the result.
See RFC 2616 for an explanation.
Take a look here: http://publib.boulder.ibm.com/infocenter/wasinfo/v6r0/topic/com.ibm.websphere.express.doc/info/exp/ae/twbs_cookie.html
and set the following HTTP headers:
Expires with the value a hard coded GMT date in the past
Last-Modified with the value the current date in GMT formatted "EEE, d MMM yyyy HH:mm:ss"
Cache-Control with the following value 'no-store, no-cache, must-revalidate'
Cache-Control with the following value 'post-check=0, pre-check=0'
Pragma with the following value 'no-cache'