tornado
tornado copied to clipboard
web: Remove version from server header
Hi all
I would like to know how I can hide the http headers of Jupyterhub since it appears in Server: Tornado 5.1.1, I do not want the tornado version to appear
Thanks a lot
You can override set_default_headers()
in the RequestHandler sub-classes, and set or clear the Server response header there. This would have to be done in the Jupyter code base.
You could perhaps request that project to provide a config option, but they are probably not interested: https://github.com/jupyterhub/jupyterhub/issues/1674 - you probably want to put it behind a reverse-proxy: https://jupyterhub.readthedocs.io/en/stable/reference/config-proxy.html
set_default_headers
is awkward to use for this purpose because you'd need to use subclasses of StaticFileHandler, RedirectHandler, etc. If you care about the server header, you should really be running behind a proxy that gives you a centralized place to control it.
This request comes up periodically - security compliance checklists often say you should hide version numbers here. I think that's generally silly, but on the other hand there's no strong reason to include the version in the first place. I think we should probably at least remove the version from the server header, and maybe just remove it completely.
Use a custom tornado.web.OutputTransform
to customize or remove the default Server header. It will apply to all outgoing responses.
from tornado import ioloop, web
class ServerHeaderTransform(web.OutputTransform):
def transform_first_chunk(self, status_code, headers, chunk, finishing):
headers.pop('Server')
return status_code, headers, chunk
app = web.Application(transforms=[ServerHeaderTransform])
app.listen(8000)
ioloop.IOLoop.current().start()
$ curl -v localhost:8000
* Trying ::1:8000...
* Connected to localhost (::1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.73.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 404 Not Found
< Content-Type: text/html; charset=UTF-8
< Date: Tue, 17 Nov 2020 21:18:02 GMT
< Content-Length: 69
<
* Connection #0 to host localhost left intact
<html><title>404: Not Found</title><body>404: Not Found</body></html>
Oh, I had forgotten all about OutputTransforms. That would work, although OutputTransform is undocumented and I've never really considered them as part of the web module's public API so I wouldn't want to encourage this (the HTTPMessageDelegate interfaces would be a supported way to do the same thing). Better to just change the source to stop emitting the server header completely.
Hi,
I'm using Tornado 6.0.4 and I tried the solution above but I've noticed that my Content-Encoding: gzip
disappeared together with the Server
header:
class ServerHeaderTransform(OutputTransform):
def transform_first_chunk(self, status_code, headers, chunk, finishing):
headers.pop('Server')
return status_code, headers, chunk
class MyApplication(Application):
def __init__(self, *args, **kwargs) -> None:
kwargs['compress_response'] = True
kwargs['transforms'] = [ServerHeaderTransform]
super().__init__(*args, **kwargs)
app = MyApplication()
app.listen(8000)
ioloop.IOLoop.current().start()
Without the kwargs['transforms'] = [ServerHeaderTransform]
line I'm getting gzip
responses as expected (when asking for a compressed response.
Any idea why it doesn't work together with the compress_response
flag?
@itayB
Specifying the transforms
kwarg is overrides the compress_response
flag.
https://github.com/tornadoweb/tornado/blob/aa9d32d5e89b290dad69e0c6655b7497ea492e45/tornado/web.py#L2040-L2045
Use self.add_transform
instead.
https://github.com/tornadoweb/tornado/blob/aa9d32d5e89b290dad69e0c6655b7497ea492e45/tornado/web.py#L2128-L2129