Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nginx consumes Upgrade header after proxy_pass

So I have been banging my head against the wall for the better part of 2 days, please help.

I am attempting to establish a Websocket connection using this django-websocket-redis configuration. There are 2 instances of uwsgi running, one for the website and one for the websocket communication.

I used wireshark heavily to find out what exactly is happening, and apparently nginx is eating the headers "Connection: Upgrade" and "Upgrade: websocket".

here is the critical nginx config part:

upstream websocket {
    server 127.0.0.1:9868;
}

location /ws/ {
    proxy_pass_request_headers      on;
    access_log off;
    proxy_http_version 1.1;
    proxy_pass http://websocket;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Upgrade websocket;
}

As you can see on those 2 screenshots, tcpdump of internal communication shows that the handshake works nicely. but in my browser (second image) the headers are missing.

Any ideas are greatly appreciated. I am truly stuck here :(

Versions:

nginx - 1.7.4
uwsgi - 2.0.7

pip freeze: Django==1.7 MySQL-python==1.2.5 django-redis-sessions==0.4.0 django-websocket-redis==0.4.2 gevent==1.0.1 greenlet==0.4.4 redis==2.10.3 six==1.8.0 uWSGI==2.0.7 wsgiref==0.1.2

like image 688
DerShodan Avatar asked Oct 03 '14 17:10

DerShodan


People also ask

Does nginx pass headers?

Passing Request HeadersBy default, NGINX redefines two header fields in proxied requests, “Host” and “Connection”, and eliminates the header fields whose values are empty strings. “Host” is set to the $proxy_host variable, and “Connection” is set to close .

What does proxy_pass do in nginx?

The proxy_pass setting makes the Nginx reverse proxy setup work. The proxy_pass is configured in the location section of any virtual host configuration file. To set up an Nginx proxy_pass globally, edit the default file in Nginx's sites-available folder.

What does Proxy_set_header upgrade $Http_upgrade do?

proxy_set_header Upgrade $http_upgrade; Is actually doing what you want because $http_upgrade comes from the header sent by the client. So if the client doesn't request an upgrade, it doesn't get passed along. Save this answer.

Why use nginx reverse proxy?

Security and anonymity – By intercepting requests headed for your backend servers, a reverse proxy server protects their identities and acts as an additional defense against security attacks.


1 Answers

I would use gunicorn for deploying a django application, but anyway.

I remembered that I saw this on the gunicorn docs:

If you want to be able to handle streaming request/responses or other fancy features like Comet, Long polling, or Web sockets, you need to turn off the proxy buffering. When you do this you must run with one of the async worker classes.

To turn off buffering, you only need to add proxy_buffering off; to your location block:

In your location would be:

location /ws/ {
    proxy_pass_request_headers      on;
    access_log off;
    proxy_http_version 1.1;
    proxy_redirect off;
    proxy_buffering off;
    proxy_pass http://websocket;
    proxy_set_header Connection "upgrade";
    proxy_set_header Upgrade websocket;
}

Link to the guide of gunicorn for deploying in nginx. http://docs.gunicorn.org/en/latest/deploy.html?highlight=header

Hope this helps

like image 126
Marcs Avatar answered Oct 16 '22 01:10

Marcs