Nginx 阻止前端向后端发送 cookie

Nginx 阻止前端向后端发送 cookie

我在端口 12345 上运行 Golang 后端,在端口 8080 上运行 Angular 前端。它们在名为 /consultation 的页面上通过 websockets 进行通信。当我打开两个端口的防火墙并让它们通过 IP 和端口地址进行通信时,它们工作正常。但是如果它们之间有 Nginx,则失败,因为 Nginx 阻止将 cookie 从前端发送到后端。出于开发目的,我编写了后端代码,如果后端没有从前端收到 cookie,就会由于 nil map 错误而崩溃,而现在它确实收到了。我搞不清楚是什么阻止了后端接收 cookie。

Nginx 反向代理http://frontend.mydomain.mehttp://本地主机:8080根据 Nginx 文档和网上其他解决方案,conf 为:

server {
  listen 80;
  server_name frontend.mydomain.me;

location / {
  ...
}

location /consultation {

  # Tried with and without these.
  proxy_set_header Access-Control-Allow-Headers "*";
  proxy_set_header Access-Control-Allow-Methods "*";
  proxy_set_header Access-Control-Allow-Credentials "true";

  # Tried with and without these.
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $host;
  proxy_set_header X-NginX-Proxy true;

  # With or without these, http is upgraded to ws anyway.
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";

  proxy_pass http://localhost:8080/consultation;
}
}

正如我在上面代码中的评论中提到的那样,无论是否升级 Nginx conf 中的连接,http 仍然会升级到 ws,如响应标头中所示(显示在 Chrome 和 Firefox 的开发人员工具中的“网络”选项卡中):

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: WfOsnTuctT2GopMFe55WOC7Dwk4=

我不知道这是否表明不需要升级使用 Nginx 的连接,以及是否与问题有关。

我尝试将这些添加到 Nginx 配置中,但没有任何变化:

proxy_set_header Cookie $cookie_client;
proxy_set_header Cookie "$http_cookie;client=testvalue";

当客户端到达登陆页面时,Angular 前端会设置 cookie(不使用 HttpOnly 或 Secure):

this.cookie.set(this.cookieName, this.cookieValue)

Angular 前端代码打开与后端的套接字连接,用于http://frontend.mydomain.me/consultation

this.socket = new WebSocket("ws://MY_IP:12345/consultation");

Golang 后端直接在 MY_IP:12345 上提供服务,无需 Nginx。以下是 /consultation 上相关页面的代码,其中包含从前端请求 cookie 的部分:

func consultation(res http.ResponseWriter, req *http.Request) {
    // Upgrade connection to websocket.
    conn, _ := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(res, req, nil)

    // Print http request headers to console.
    output, _ := httputil.DumpRequest(req, true)
    fmt.Println(string(output))

    // This is the code that requests for the cookie that never arrives.
    var cookieName string
    var cookieValue string
    for _, cookie := range req.Cookies() {
        cookieName = cookie.Name
        cookieValue = cookie.Value
    }
   ...
}

当我打开端口 12345 和 8080 并让前端和后端直接在它们之间通信时,cookie 已成功发送和接收,如httputil.DumpRequest()上面的 Golang 代码转储的这些请求标头所示:

GET /consultation HTTP/1.1
Host: MY_IP:12345
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,en-GB;q=0.8
Cache-Control: no-cache
Connection: Upgrade

// Right here.
Cookie: client=f7da8732-6e15-4157-8cf7-a13b7ce2b5cf

Origin: http://MY_IP:8080
Pragma: no-cache
Sec-Websocket-Extensions: permessage-deflate; client_max_window_bits
Sec-Websocket-Key: wgMYe0jAVnutW9LEuSFFCg==
Sec-Websocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36

但是使用 Nginx 时,可能会发送但未收到。我不确定:

GET /consultation HTTP/1.1
Host: MY_IP:12345
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,en-GB;q=0.8
Cache-Control: no-cache
Connection: Upgrade
Origin: http://frontend.mydomain.me
Pragma: no-cache
Sec-Websocket-Extensions: permessage-deflate; client_max_window_bits
Sec-Websocket-Key: fm2Bkc8ZPoTcGeAZi/n+WA==
Sec-Websocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36

我肯定还遗漏了什么。那会是什么呢?

据我所知,我的 Nginx 代码是正确的。它基于 Nginx 文档以及其他人的解决方案。

2019 年 3 月 27 日更新:根据评论中的要求,结果如下sudo nginx -T

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
# configuration file /etc/nginx/nginx.conf:
user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
        worker_connections 768;
        # multi_accept on;
}

http {
        # Basic Settings

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        keepalive_timeout 65;
        types_hash_max_size 2048;

        server_names_hash_bucket_size 128;

        include /etc/nginx/mime.types;
        default_type application/octet-stream;

        # SSL Settings

        ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
        ssl_prefer_server_ciphers on;

        # Logging Settings

        access_log /var/log/nginx/access.log;
        error_log /var/log/nginx/error.log;

        # Gzip Settings

        gzip on;
        gzip_disable "msie6";

        # Virtual Host Configs

        include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/sites-enabled/*;
}

# configuration file /etc/nginx/mime.types:

types {
    # Removed for brevity.
}

# configuration file /etc/nginx/sites-enabled/subdomain1:

server {
listen 80;
server_name subdomain1.mydomain.me subdomain1.org;

listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/subdomain1.org/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/subdomain1.org/privkey.pem;

Redirect non-https traffic to https
if ($scheme != "https") {
return 301 https://$host$request_uri;
}

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;


location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/subdomain1/subdomain1;
}

location / {
include proxy_params;
proxy_pass http://unix:/home/subdomain1/subdomain1.sock;
}
}

# configuration file /etc/nginx/proxy_params:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

## This is the back end in this question on Server fault.
## It is currently not being used since the front end connects directly to MY_IP:12345.
# configuration file /etc/nginx/sites-enabled/backend:
server {
listen 80;
server_name backend.mydomain.me;

location / {
include proxy_params;
proxy_pass http://localhost:12345/;
}

location /consultation {
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

       if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'POST';
        add_header 'Access-Control-Allow-Headers' 'Content-Type';
        add_header 'Content-Type' 'application/json';
         return 204;
    }
     if ($request_method = 'POST') {
         add_header 'Access-Control-Allow-Origin' '*';
         add_header 'Access-Control-Allow-Methods' 'POST';
         add_header 'Access-Control-Allow-Headers' 'Content-Type';
         add_header 'Content-Type' 'application/json';
     }
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;

   proxy_pass "http://localhost:12345/consultation";
}
}

# configuration file /etc/nginx/proxy_params:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

## This is the frontend for this question on Server Fault.
# configuration file /etc/nginx/sites-enabled/frontend:
server {
listen 80;
server_name frontend.mydomain.me;

location / {
  include proxy_params;
  proxy_pass http://localhost:8080/;
}

location /consultation {
  include proxy_params;
  proxy_pass http://localhost:8080/consultation;
}
}

# configuration file /etc/nginx/proxy_params:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# configuration file /etc/nginx/proxy_params:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# Default server configuration

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /var/www/html;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
                # First attempt to serve request as file, then
                # as directory, then fall back to displaying a 404.
                try_files $uri $uri/ =404;
        }
        }
}

# configuration file /etc/nginx/sites-enabled/mydomain:
server {
listen 80;
server_name MY_IP mydomain.me;

listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/mydomain.me/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mydomain.me/privkey.pem;

Redirect non-https traffic to https
if ($scheme != "https") {
return 301 https://$host$request_uri;
}

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/mydomain/mydomain;
}

location / {
include proxy_params;
proxy_pass http://unix:/home/mydomain/mydomain.sock;
}
}

# configuration file /etc/nginx/proxy_params:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

# configuration file /etc/nginx/sites-enabled/subdomain2:

server {
listen 80;
server_name subdomain2.mydomain.me;

listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/mydomain.me-0001/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mydomain.me-0001/privkey.pem;

if ($scheme != "https") {
return 301 https://$host$request_uri;
}

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /home/subdomain2/subdomain2;
}

location / {
include proxy_params;
proxy_pass http://unix:/home/subdomain2/subdomain2.sock;
}
}

# configuration file /etc/nginx/proxy_params:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

答案1

事实证明,我们不能简单地在不同的域和子域之间传递 cookie。

就我而言,我尝试将其从 frontend.mydomain.me 传递到 MY_IP:12345。实际上,我还尝试将其从 frontend.mydomain.me 传递到 backend.mydomain.me。这些方法都不起作用,因为它们位于不同的域中。

为我进一步澄清一下。

最后,我将 Nginx 配置为在 projectname.mydomain.me/frontend 上为前端提供服务,在 projectname.mydomain.me/backend 上为后端提供服务。此方法有效,因为后端和前端现在都位于同一个域 projectname.mydomain.me 上。

感谢迈克尔·汉普顿 (Michael Hampton) 在评论中为我指明正确的方向。

相关内容