3

I'm trying to setup a WebSocket endpoint on my Rails API using Nginx and Puma.

What I have (working but ugly)

The following Nginx configuration works fine, however I feel like I could use something more clever to avoid duplication on both @puma and @puma_ws named locations:

upstream puma {
    server unix:///path/to/socket.sock;
}

server {
    listen 80;
    server_name example.com;

    root /var/www/public;

    location / {
        try_files $uri/index.html $uri @puma;
    }

    location ~ ^/api/websocket {
        try_files $uri/index.html $uri @puma_ws;
    }

    location @puma {
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Host $http_host;

       proxy_redirect off;

       proxy_pass http://puma;
   }

   location @puma_ws {
       # These two lines are the only difference compared to @puma
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";

       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header Host $http_host;

       proxy_redirect off;

       proxy_pass http://puma;
   }
}

Note: I'm using a regex for the WS location (~ ^/api/websocket) because in my actual use case I need to have more than one WS endpoints. I simplified for the sake of this post's simplicity.

Initial idea

My first idea was to have only one named location @puma, which would have a nested location with the regex that would only add the two needed proxy_set_header.

That way I would have only one try_files with the only @puma named location, which would add the headers by itself using a nested location

However afaik it is not possible to have nested location block in a named location.

Do you have a better idea to add those headers based on a test on the actual URI?

Thanks!

  • I prefer your existing approach ("beauty is in the eye of the beholder"). The only change I would suggest is changing the regular expression location block to a prefix location block: location /api/websocket { ... } which would be slightly more efficient. – Richard Smith Jan 25 '17 at 14:39
  • Thanks for the input Richard! I'm not quite used to Nginx yet, and this felt weird at first (I don't like to repeat myself). Regarding the regex, I actually left it like this for this ServerFault question, because in my real use case I need to provide more than one WS endpoint: location ~ ^/api/(ws|echo) { – Jordan Becker Jan 25 '17 at 14:42
1

According to Richard from his comment on the original post, the original solution I came up with is not that bad.

However, since I like to avoid repeating myself, I've opted to include a file which contains the common configuration part between @puma and @puma_ws.

So I'm ending up with something like this:

/etc/nginx/puma_proxy.conf

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;

proxy_redirect off;

Host config file

upstream puma {
    server unix:///path/to/socket.sock;
}

server {
    listen 80;
    server_name example.com;

    root /var/www/public;

    location / {
        try_files $uri/index.html $uri @puma;
    }

    location ~ ^/api/websocket {
        try_files $uri/index.html $uri @puma_ws;
    }

    location @puma {
       include /etc/nginx/puma_proxy.conf

       proxy_pass http://puma;
   }

   location @puma_ws {
       include /etc/nginx/puma_proxy.conf

       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";



       proxy_pass http://puma;
   }
}

Posting this here in case someone might actually like it that way.

Please tell me if you have a better way to handle this situation, I'd love to have your optinion!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.