4

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!

Habovh
  • 271
  • 3
  • 12
  • 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) {` – Habovh Jan 25 '17 at 14:42

1 Answers1

2

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!

Habovh
  • 271
  • 3
  • 12