Назад | Перейти на главную страницу

NGINX Proxy передает WebSocket, а PHP не работает с SSL

Я пытаюсь маршрут запросы в Nginx следующим образом:

  1. Запросы к / должен перейти к сценарию PHP (proxy.php, который сам по себе является прокси)
  2. Запросы к /websocket должен быть проксирован на http://localhost:4000/websocket
  3. Все остальные запросы следует передавать на http://localhost:4000/

Я мог получить 2. и 3. для работы со следующей конфигурацией:

server {
    listen 443 ssl;

    server_name proxy.domain.com;

    ssl_certificate /etc/nginx/ssl/proxy.domain.com/468446/server.crt;
    ssl_certificate_key /etc/nginx/ssl/proxy.domain.com/468446/server.key;

    location = /websocket/ {
        proxy_pass http://127.0.0.1:4000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location / {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host      $http_host;
        proxy_pass         http://127.0.0.1:4000;
    }

}

Затем я попытался придумать способ добавить 1. и придумал следующее:

server {
    listen 443 ssl;

    server_name proxy.domain.com;

    root /var/www/dowmain;

    ssl_certificate /etc/nginx/ssl/proxy.domain.com/468446/server.crt;
    ssl_certificate_key /etc/nginx/ssl/proxy.domain.com/468446/server.key;

    location = /websocket/ {
        proxy_pass http://127.0.0.1:4000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

    location / {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host      $http_host;
        proxy_pass         http://127.0.0.1:4000;
    }

    location = / {
        include fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME  $document_root/proxy.php;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
    }

}

Однако с этой конфигурацией 2. больше не работает. Запросы к /websocket обрабатываются скриптом PHP. Похоже на /websocket блок местоположения больше не работает.

Интересно, что если я переключу конфиг на http://, все нормально работает.

Есть идеи, что я делаю не так?

ОБНОВИТЬ

Я думаю, что могу исключить location /websocket { ... } как источник проблемы, потому что если я заменю конфигурационный файл PHP в location = / { ... } блок с правилами из location / { ... } блок, он работает нормально (но это не то, что мне нужно). Я подозреваю, что PHP все испортил.

ОБНОВЛЕНИЕ II

Он работает даже на моем локальном компьютере с сертификатами от mkcert, с точно такой же конфигурацией.

Так что единственная разница в том, Давайте зашифровать сертификат по сравнению с местным. Даже версии Nginx и PHP в основном одинаковы (PHP 7.2.7, Nginx 1.15.1 на моем локальном компьютере против PHP 7.2.13, Nginx 1.15.6)

Похоже, проблема с завершающей косой чертой в месте и, которое содержит директиву proxy_pass. Вы можете попробовать добавить его в местоположение своего веб-узла:

location /websocket/ {
    proxy_pass http://127.0.0.1:4000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Также из-за этот Я удалил / websocket / из директивы proxy_pass, поскольку вы заменяете часть URI «/ webconfig» на «/ webconfig /», что сбивает с толку

Если директива proxy_pass указана с URI, тогда, когда запрос передается на сервер, часть нормализованного URI запроса, соответствующая местоположению, заменяется URI, указанным в директиве.

На главный вопрос - исходя из Документация Nginx:

Если местоположение определяется строкой префикса, которая заканчивается символом косой черты, и запросы обрабатываются одним из proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, memcached_pass или grpc_pass, то выполняется специальная обработка. В ответ на запрос с URI, равным этой строке, но без косой черты в конце, постоянное перенаправление с кодом 301 будет возвращено на запрошенный URI с добавленной косой чертой.

Я полагаю, что в вашем случае местоположение веб-сокета не имеет завершающей косой черты, и это правило 301 перенаправляется в местоположение / по этому правилу.

Ваша конфигурация мне нравится, я дважды проверил ее в настройках nginx, и он делает то, что нужно. Я проверил, добавив заголовки в разные места:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name proxy.domain.com;
    root /var/www/dowmain;

    ssl_certificate /etc/nginx/ssl/proxy.domain.com/468446/server.crt;
    ssl_certificate_key /etc/nginx/ssl/proxy.domain.com/468446/server.key;

    ssl_protocols TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384;
    ssl_prefer_server_ciphers on;
    ssl_dhparam /etc/nginx/dhparams.pem;

    location /websocket {
        proxy_pass http://127.0.0.1:4000/websocket/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        add_header X-location websocket always;
    }

    location / {
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host      $http_host;
        proxy_pass         http://127.0.0.1:4000;
        add_header X-location wildcard always;
    }

    location = / {
        include fastcgi_params;
        fastcgi_param   SCRIPT_FILENAME  $document_root/proxy.php;
        fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
        add_header X-location root always;
    }
}

А затем проверьте заголовки:

$ curl -I https://proxy.domain.com
$ curl -I https://proxy.domain.com/anythingelse
$ curl -I https://proxy.domain.com/websocket

Вывод должен содержать:

X-location: websocket
Or:
X-location: wildcard
Or:
X-location: root

Значение X-location должно соответствовать тому, что вы установили в конфигурации nginx для этого блока.

Моя версия для nginx:

# nginx -v
nginx version: nginx/1.10.1