Thiết lập ASP.NET Core với Reverse Proxy Nginx

Khi triển khai các ứng dụng ASP.NET Core, đặc biệt là khi sử dụng các dịch vụ như IdentityServer và các bảng điều khiển quản trị, việc cấu hình một reverse proxy như Nginx có thể gặp phải một số thách thức. Bài viết này sẽ đi sâu vào các vấn đề thường gặp và giải pháp để thiết lập thành công.

Cấu hình Nginx để định tuyến yêu cầu

Giả sử chúng ta có một cấu trúc dự án với nhiều thành phần độc lập, ví dụ:

  • Id4.Admin.Api: API cung cấp tài nguyên cho IdentityServer.
  • Id4.Admin: Giao diện web quản trị IdentityServer.
  • Id4.STS.Identity: Dịch vụ STS (Security Token Service).

Mục tiêu là cho phép truy cập Id4.STS.Identity qua http://auth.xxx.com và Id4.Admin qua http://auth.xxx.com/admin, tất cả dưới cùng một tên miền. Điều này đòi hỏi cấu hình Nginx để xử lý nhiều trang web (multi-site) trên cùng một cổng.

Kestrel, máy chủ web mặc định của ASP.NET Core, khi hoạt động như một máy chủ biên, sẽ chiếm dụng một cổng IP. Nếu không có reverse proxy, Kestrel không hỗ trợ chia sẻ cổng giữa nhiều tiến trình và không xử lý việc chuyển tiếp yêu cầu dựa trên tiêu đề Host.

Để giải quyết vấn đề này, chúng ta cần sử dụng reverse proxy để chuyển tiếp yêu cầu đến các tiến trình Kestrel tương ứng. Cấu hình Nginx với chỉ thị location cho phép định tuyến dựa trên đường dẫn.

Cấu hình Nginx ban đầu có thể trông như sau:


server {
    listen 80;
    server_name mysite;

    location / {
        proxy_pass http://id4.sts.identity:80;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /admin/ {
        proxy_pass http://id4.admin:80/; # Lưu ý dấu '/' ở cuối
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
    

Sự khác biệt chính nằm ở proxy_pass trong khối location /admin/ có dấu gạch chéo (/) ở cuối. Dấu gạch chéo này có vai trò quan trọng: nó thay thế phần đường dẫn được chỉ định trong location (tức là /admin) bằng đường dẫn trỏ đến máy chủ backend. Ví dụ, yêu cầu http://auth.xxx.com/admin/dashboard sẽ được chuyển tiếp tới http://id4.admin:80/dashboard.

Tuy nhiên, chỉ cấu hình này có thể dẫn đến lỗi 404 sau khi tải lại cấu hình Nginx.

Kích hoạt Middleware UsePathBase

Để xử lý các ứng dụng chạy trong thư mục con (subdirectory), ASP.NET Core cung cấp middleware UsePathBase. Middleware này cho phép định nghĩa một đường dẫn cơ sở cho ứng dụng.

Việc kích hoạt UsePathBase được thực hiện bằng cách thêm một cấu hình trong appsettings.json và kích hoạt nó trong phương thức Configure của lớp Startup:


// appsettings.json
{
    "PATHBASE": "/admin"
}

// Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...
    app.UsePathBase(Configuration.GetValue<string>("PATHBASE"));
    // ...
}
    

Kích hoạt Middleware UseForwardedHeaders

Khi sử dụng reverse proxy, một số thông tin quan trọng về yêu cầu gốc có thể bị mất:

  1. Giao thức truyền tải ban đầu (HTTP/HTTPS) có thể bị ẩn đi khi yêu cầu đi qua proxy.
  2. Địa chỉ IP thực của máy khách cũng không còn hiển thị trực tiếp.

Đây là lý do tại sao cấu hình Nginx bao gồm proxy_set_header X-Forwarded-For và proxy_set_header X-Forwarded-Proto. Để ASP.NET Core nhận biết và sử dụng các thông tin này, chúng ta cần kích hoạt middleware UseForwardedHeaders.

Cần cài đặt gói NuGet Microsoft.AspNetCore.HttpOverrides. Sau đó, cấu hình middleware trong Startup.cs:


// Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...
    app.UseForwardedHeaders(new ForwardedHeadersOptions
    {
        ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
    });

    app.UsePathBase(Configuration.GetValue<string>("PATHBASE"));
    // ...
}
    

Lưu ý quan trọng: Middleware UseForwardedHeaders nên được đặt trước các middleware khác phụ thuộc vào thông tin giao thức hoặc IP gốc, chẳng hạn như xác thực, tạo liên kết, chuyển hướng, v.v.

Giải quyết vấn đề giới hạn kích thước Header của Nginx

Trong một số trường hợp, đặc biệt là khi có nhiều cookie được gửi kèm yêu cầu, kích thước header có thể vượt quá giới hạn mặc định của Nginx, gây ra lỗi "upstream sent too big header". Lỗi này thường được ghi lại trong file log của Nginx (ví dụ: /var/log/nginx/error.log trên Linux).

Để khắc phục, cần tăng kích thước buffer cho Nginx bằng cách thêm các chỉ thị sau vào cấu hình server hoặc http:


proxy_buffer_size          128k;
proxy_buffers              4 256k;
proxy_busy_buffers_size    256k;
    

Sửa lỗi đường dẫn cơ sở trong liên kết

Một vấn đề khác có thể xảy ra là các liên kết được tạo ra bởi các TagHelper (ví dụ: <a asp-controller="..." asp-action="...">) không bao gồm đường dẫn cơ sở (/admin), dẫn đến lỗi 404 khi người dùng điều hướng.

Nguyên nhân là do AnchorTagHelper lấy PathBase từ ngữ cảnh yêu cầu hiện tại. Khi Nginx sử dụng cấu hình proxy_pass http://id4.admin:80/;, nó đã loại bỏ phần /admin khỏi đường dẫn gốc, làm mất thông tin PathBase trong ngữ cảnh của ứng dụng backend.

Giải pháp là cấu hình lại proxy_pass để bao gồm đường dẫn cơ sở mà ứng dụng mong đợi:


location /admin/ {
    proxy_pass http://id4.admin:80/admin/; # Đảm bảo có /admin/ ở cuối
    # ... các header khác
}
    

Cấu hình Nginx hoàn chỉnh

Dưới đây là cấu hình Nginx hoàn chỉnh sau khi đã áp dụng các điều chỉnh:


server {
    listen 80;
    server_name mysite;

    location / {
        proxy_pass http://id4.sts.identity:80;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /admin/ {
        proxy_pass http://id4.admin:80/admin/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffer_size          128k;
        proxy_buffers              4 256k;
        proxy_busy_buffers_size    256k;
    }
}
    

Với cấu hình này, việc triển khai ASP.NET Core sử dụng reverse proxy Nginx cho nhiều ứng dụng trên cùng một tên miền sẽ hoạt động chính xác.

Thẻ: ASP.NET Core nginx reverse proxy kestrel deployment

Đăng vào ngày 24 tháng 10 lúc 06:22