How to configure Nginx reverse proxy for web sockets?
I have ASP Core application running on Ubuntu.
In front of the application there is Nginx webserver used as a reverse proxy as follows :
# Part 1 : Redirect http tp https
server {
server_name example.com www.example.com;
listen 80;
listen [::]:80;
return 301 https://www.example.com$request_uri;
}
# Part 2 : Reverse proxy for https requests. Get the app response from localhost:5000
server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /home/aspuser/ssl/example.crt;
ssl_certificate_key /home/aspuser/ssl/example.key;
access_log /var/log/nginx/reverse-access.log;
error_log /var/log/nginx/reverse-error.log;
location / {
proxy_pass http://127.0.0.1:5000;
}
}
# Part 3 : Reverse proxy for websockets on port 3000. Get the app response from localhost:5000/ws
server {
listen 3000 ssl;
server_name example.com www.example.com
ssl_certificate /home/aspuser/ssl/example.crt;
ssl_certificate_key /home/aspuser/ssl/example.key;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:5000/ws;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Part 1 & 2 are working properly. The apps shows with ssl certificate correctly.
Part 3 : Websocket reverse proxy is not working. When I'm trying to send websocket message from Chrome Dev Tab using the following Javascript :
var socket = new WebSocket("wss://www.example.com:3000");
socket.onopen = function(e) {
console.log("Connection established");
};
I'm getting the error :
WebSocket connection to 'wss://www.example.com:3000/' failed: Error in connection establishment: net::ERR_CONNECTION_RESET
The same application works properly from localhost (I'm getting websocket response on localhost) but not when published on Ubuntu. (The port 3000 is allowed in the server firewall).
Can anyone help please ?
1 answer
-
answered 2021-01-11 16:10
Gerard H. Pille
I believe your
proxy_pass http://127.0.0.1:5000/ws;
should read
proxy_pass http://127.0.0.1:5000/ws/;
I seem to remember that when the location ends in /, so should the forwarded path.
See also questions close to this topic
-
Best practice for reading emails from ASP.NET application
Can anyone please advise what the current best practice is for reading an email mail box from an ASP.NET application?
Is it still ok to use
Microsoft.Office.Interop
or do I need to use theMicrosoft.Graph
API?Or alternatively can SQL Server Database Mail be used to receive emails into a database table. (I have tried this approach but without any success)
Any help greatly appreciated.
-
Checkbox to control rangevalidator or maximum input double of textbox
I have a checkbox and the goal is to limit the maximum input value of a textbox when the checkbox is ticked.
I have two solutions in mind , one is to alter the value of the rangevalidator when the checkbox is ticked or the other is to restrict the value parsed into the textbox when the checkbox is ticked.
But my latter solution has an error as seen in the image below. While i don't know how to implement the former solution.
Code of the TextBox
<div class="d flex justify-content-start"> <div class="col-6 "> Hours Spent <asp:TextBox placeholder="Hours Spent *" Width="" ID="txtHoursSpent" runat="server" MaxLength="3"></asp:TextBox> <asp:RangeValidator ID="RangeValidator1" runat="server" ErrorMessage="Value must be greater than zero." ValidationExpression="\d+" ControlToValidate="txtHoursSpent" MinimumValue="0.5" MaximumValue="12" Type="Double"></asp:RangeValidator> </div> </div>
Code Of the CheckBox
<asp:CheckBox Text="Half-Day" runat="server" OnCheckedChanged="HalfDay_CheckedChanged" />
As you can see the error in the codebehind causes me to be unable to restrict the textbox to a certain range. Code Behind of the Checkbox
Does anyone know the solution perhaps?
-
C# asp dotNet - Convert Datetime field to Date/String
I find myself unable to change the stored procedure, which produces a datetime field (among other fields) , but I want to display the datetime field in my gridview as either Date (MM/DD/YYYY) or a string in the same format (similar to SQL's: CONVERT(VARCHAR,[datetimeField],101)).
I have pulled the dataset but don't see any way to convert it for this specific field after pulling it, so it shows the time too. I welcome any advice. Here are pieces from my code:
aspx.cs:
DataSet ds = RunSQLString("SplitItemsDesc_Get '" + ddlSearchItem.Text + "', 'C7'", lsreplenDBConnection); if (ds != null && ds.Tables[0].Rows.Count > 0) { GridView2.DataSource = ds.Tables[0]; GridView2.DataBind(); GridView2.Visible = true; btnReset.Visible = true; }
aspx gridview:
<asp:gridview id="GridView1" autogeneratecolumns="False" EmptyDataText="Saved Record(s) not found" runat="server" Font-Size="10pt" Font-Names="Verdana" cellpadding="3" CellSpacing="0" BorderWidth="2" BorderColor="#5F6366" HeaderStyle-BackColor="#D8E0E2" ForeColor="#0A2D3B" OnRowDeleting="GridView1_RowDeleting"> <columns> <%--other fields here--%> <asp:TemplateField headertext="End Date"> <ItemTemplate> <asp:label id="endDate" Text= '<%# Eval("end_date") %>' runat="server" Width="378px"/> </ItemTemplate> </asp:TemplateField> </columns> </asp:gridview>
-
How to avoid hitting same endpoint in ASP.NET Webcore
I'm trying to create filters in my webapp controller. I want to get items by category, id or location. But they all use the same method: HTTP with an int as a parameter. What's the most elegant solution to hit only the method I want?
Here's an example of the problem. This code obviously gets the "hitting multiple endpoints" error.
[HttpGet("{id}", Name = "GetShiftById")] public ActionResult<Shift> GetShiftById(int id) { return _context.Shifts.Find(id); } [HttpGet("{locationId}", Name = "GetShiftByLocation")] public ActionResult<IEnumerable<Shift>> GetByLocation(int locationId) { return _context.Shifts .Include(s => s.Category) .Include(s => s.Location) .Where (s => s.Location.Id == locationId ) .ToList(); }
-
If this possible "the user is login to my website and in his browser the cookies saved
If this possible "the user is login to my website and in his browser the cookies saved and because some users are capable for write password and email for many times. So the issues is the session timeout in identity core 3.1 was out when the user delete her cookies
-
ArgumentNullException: Value cannot be null. (Parameter 'items') asp.core 3.1
An unhandled exception occurred while processing the request. ArgumentNullException: Value cannot be null. (Parameter 'items')
Microsoft.AspNetCore.Mvc.Rendering.MultiSelectList..ctor(IEnumerable items, string dataValueField, string dataTextField, IEnumerable selectedValues, string dataGroupField)
I have error in
<select asp-for="Input.Name" asp-items='new SelectList(roles,"Id","Name")' class="form-control">
register class
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; using NMTC_beta.Models; namespace NMTC_beta.Areas.Identity.Pages.Account { [AllowAnonymous] public class RegisterModel : PageModel { private readonly SignInManager<ApplicationUser> _signInManager; private readonly UserManager<ApplicationUser> _userManager; private readonly ILogger<RegisterModel> _logger; private readonly IEmailSender _emailSender; private readonly RoleManager<IdentityRole> _roleManager; public RegisterModel( UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, ILogger<RegisterModel> logger, IEmailSender emailSender, RoleManager<IdentityRole> roleManager) { _userManager = userManager; _signInManager = signInManager; _logger = logger; _emailSender = emailSender; _roleManager = roleManager; } [BindProperty] public InputModel Input { get; set; } public string ReturnUrl { get; set; } public IList<AuthenticationScheme> ExternalLogins { get; set; } public class InputModel { [Required] [StringLength(50)] [Display(Name = "First name")] public string FirstName { get; set; } [Required] [StringLength(100)] [Display(Name = "Last name")] public string LastName { get; set; } [Required] [DataType(DataType.PhoneNumber)] [Display(Name = "Phone Numbre ")] public string PhoneNumber { get; set; } [Required] [Display(Name = "Microsoft Club Member")] public string MicrosoftMember { get; set; } [Required] [Display(Name = "T-Shirt and Stickers")] public string TS { get; set; } [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } [Required] public string Name { get; set; } } public void OnGet(string returnUrl = null) { var roles = _roleManager.Roles.Where(a => !a.Name.Contains("Admin")).ToList(); ViewData["roles"] = roles; ReturnUrl = returnUrl; } public async Task<IActionResult> OnPostAsync(string returnUrl = null) { returnUrl = returnUrl ?? Url.Content("~/"); ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList(); if (ModelState.IsValid) { var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email }; var result = await _userManager.CreateAsync(user, Input.Password); var role = _roleManager.FindByIdAsync(Input.Name).Result; if (result.Succeeded) { _logger.LogInformation("User created a new account with password."); await _userManager.AddToRoleAsync(user, role.Name); // var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); // code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); // var callbackUrl = Url.Page( // "/Account/ConfirmEmail", // pageHandler: null, // values: new { area = "Identity", userId = user.Id, code = code, returnUrl = returnUrl }, // protocol: Request.Scheme); // await _emailSender.SendEmailAsync(Input.Email, "Confirm your email", // $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>."); // if (_userManager.Options.SignIn.RequireConfirmedAccount) // { // return RedirectToPage("RegisterConfirmation", new { email = Input.Email, returnUrl = returnUrl }); // } // else // { // await _signInManager.SignInAsync(user, isPersistent: false); // return LocalRedirect(returnUrl); // } await _signInManager.SignInAsync(user, isPersistent: false); return LocalRedirect(returnUrl); } foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } // If we got this far, something failed, redisplay form return Page(); } } enum Verif { YES, No } }
View page
@page @model RegisterModel @{ ViewData["Title"] = "Register"; var roles = (List<IdentityRole>)ViewData["roles"]; } <h1>@ViewData["Title"]</h1> <div class="row"> <div class="col-md-4"> <form asp-route-returnUrl="@Model.ReturnUrl" method="post"> <h4>Create a new account.</h4> <hr /> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group"> <label asp-for="Input.FirstName"></label> <input asp-for="Input.FirstName" class="form-control" /> <span asp-validation-for="Input.FirstName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.LastName"></label> <input asp-for="Input.LastName" class="form-control" /> <span asp-validation-for="Input.LastName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.PhoneNumber"></label> <input asp-for="Input.PhoneNumber" class="form-control" /> <span asp-validation-for="Input.PhoneNumber" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.TS"></label> <select asp-for="Input.TS" class="form-control"> <option value="YES">YES</option> <option value="NO">NO</option> </select> <span asp-validation-for="Input.TS" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Name"></label> <select asp-for="Input.Name" asp-items='new SelectList(roles,"Id","Name")' class="form-control"> </select> <span asp-validation-for="Input.Name" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Email"></label> <input asp-for="Input.Email" class="form-control" /> <span asp-validation-for="Input.Email" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Email"></label> <input asp-for="Input.Email" class="form-control" /> <span asp-validation-for="Input.Email" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.Password"></label> <input asp-for="Input.Password" class="form-control" /> <span asp-validation-for="Input.Password" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Input.ConfirmPassword"></label> <input asp-for="Input.ConfirmPassword" class="form-control" /> <span asp-validation-for="Input.ConfirmPassword" class="text-danger"></span> </div> <button type="submit" class="btn btn-primary">Register</button> </form> </div> <div class="col-md-6 col-md-offset-2"> <section> <h4>Use another service to register.</h4> <hr /> @{ if ((Model.ExternalLogins?.Count ?? 0) == 0) { <div> <p> There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a> for details on setting up this ASP.NET application to support logging in via external services. </p> </div> } else { <form id="external-account" asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal"> <div> <p> @foreach (var provider in Model.ExternalLogins) { <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button> } </p> </div> </form> } } </section> </div> </div> @section Scripts { <partial name="_ValidationScriptsPartial" /> }
-
Kubernetes - 'ERR_TOO_MANY_REDIRECTS' error
I am using ingress-nginx for my kubernetes cluster with AWS tls certificate .
Both the frontend and backend services are running inside the cluster.
I am able to view the frontend but when it interacts with backend it throws the error :
ERR_TOO_MANY_REDIRECTS
Ingress nginx
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: ingress-service annotations: nginx.ingress.kubernetes.io/default-backend: ingress-nginx-controller nginx.ingress.kubernetes.io/use-regex: "true" nginx.ingress.kubernetes.io/configuration-snippet: | add_header Access-Control-Allow-Methods "POST, GET, OPTIONS"; add_header Access-Control-Allow-Credentials true; nginx.ingress.kubernetes.io/enable-cors: "true" nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS" nginx.ingress.kubernetes.io/from-to-www-redirect: "true" nginx.ingress.kubernetes.io/ssl-redirect: "false" spec: tls: - hosts: - example.com - www.example.com rules: - host: www.example.com http: paths: - path: /api/upload/?(.*) backend: serviceName: aws-srv servicePort: 3000
Ingress nginx service
apiVersion: v1 kind: Service metadata: annotations: service.beta.kubernetes.io/aws-load-balancer-type: nlb service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*" service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:ap-south-1XXXXX" service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http" labels: helm.sh/chart: ingress-nginx-2.11.1 app.kubernetes.io/name: ingress-nginx app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/version: 0.34.1 app.kubernetes.io/managed-by: Helm app.kubernetes.io/component: controller name: ingress-nginx-controller namespace: ingress-nginx spec: type: LoadBalancer externalTrafficPolicy: Local ports: - name: http port: 80 protocol: TCP targetPort: http - name: https port: 443 protocol: TCP targetPort: http selector: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/component: controller
I am stuck here and unable to take this to production, thanks in advance!
-
what url hostname to use in front-end app downstream from nginx proxy
I'm a newb, don't know much about nginx. What url string do I need to use in order for the front-end application (shinyR) to reach any of the (flask) back-end API servers? Prior to have 2 API servers, I can just use the docker container name, like:
pyapi:5000/post
. Since I set up innginx.conf
2x api servers, how can it be load-balanced in the point of view of the front-end app? From host, I can type inlocalhost:1889
and it will round-robin select from my two API servers. But I want it to do the same from within the front-end app. Here is mynginx.conf
I took the example from example:# declare flask app upstream pyapi_app { least_conn; server pyapi1:5000; server pyapi2:5000; } # declare shiny app upstream shiny_app { server shinyapp:3838; } map $http_upgrade $connection_upgrade { default upgrade; '' close; } # shinyapp server server { listen 80 default_server; server_name shiny_app; client_max_body_size 50M; # normal requests go to shiny app location / { proxy_pass http://shiny_app; proxy_redirect off; proxy_set_header Host $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-Host $server_name; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 5d; proxy_buffering off; } } # python api server server { listen 81; server_name pyapi_app; # pyapi requests go to the flask app location / { proxy_pass http://pyapi_app; proxy_redirect off; proxy_set_header Host $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-Host $server_name; } }
-
Nginx does not display images on the page
I would need your help.
I have created web page, which I would like to run on subdomain (e.g. subdomain.example.com). I am using nginx server. For now everythiing works fine, only images (png) e.g. logos and favicon does not display on page. All files are stored in /var/www/app/static.
Here is my nginx app.conf file stored in /etc/nginx/conf.d
server { listen 80; server_name subdomain.example.com; root /var/www/app/; index index.php index.html index.htm; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location /static { root /var/www/app/static; } error_page 404 /index.php; access_log /var/log/nginx/app.access.log; error_log /var/log/nginx/app.error.log; location ~ \.php$ { fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/run/php/php7.4-fpm.sock; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } }
And here is part of index.php code, where image is refering.
<img src="static/image.png" class="center" alt="logo" style="max-width:100%;height:auto;"/>
I tried to change part <img src="static/image.png" .., on a hundred ways, but unfortunately image does not appear.
Please help me, this debugging really frustrating me.
TIA.