Fix: Nginx 1.22 Redirects Dropping Port Number
The Issue
Section titled “The Issue”Following an upgrade to Nginx 1.22 (common in Amazon Linux 2023, Ubuntu 22.04, and updated Docker images), many administrators notice that internal redirects—such as appending a trailing slash to a directory—strip the port number from the URL.
For example, a request to http://example.com:8080/dashboard is redirected to http://example.com/dashboard/, causing a “Connection Refused” error because the client attempts to reach the service on the default port 80 instead of the custom port 8080. This is primarily due to changes in how Nginx 1.22 handles absolute_redirect defaults and Host header inheritance.
Exact Error Log
Section titled “Exact Error Log”While Nginx logs may show a standard 301 Moved Permanently, the symptom is visible via curl or browser developer tools:
# Requesting a directory without a trailing slash$ curl -I http://api.internal.cloud:8443/v1/docs
HTTP/1.1 301 Moved PermanentlyServer: nginx/1.22.1Date: Wed, 24 May 2023 10:00:00 GMTContent-Type: text/htmlContent-Length: 169Location: http://api.internal.cloud/v1/docs/ <-- PORT 8443 IS MISSINGConnection: keep-aliveDiagnostic Checklist
Section titled “Diagnostic Checklist”- Check Listen Port vs. Host Header: Is Nginx listening on a port that differs from what the client sees (e.g., Docker port mapping
-p 8443:80)? - Verify
absolute_redirectState: In 1.22, the default ison. This forces Nginx to generate a full URL for redirects rather than a relative path. - Inspect Proxy Headers: If Nginx is behind an AWS ALB or NLB, verify if the
Hostheader passed to Nginx includes the$proxy_port. - Validate
port_in_redirect: Ensure Nginx is explicitly told to include the listening port in its redirect calculations. - Identify OS/Distribution: This behavior is prevalent in environments using NGINX Mainline or updated Stable packages on Debian 11/12 or RHEL-based systems.
The Fix
Section titled “The Fix”To resolve this, you must modify the Nginx configuration at the http, server, or location block level. There are two primary strategies depending on your Cloud architecture.
Option 1: Disable Absolute Redirects (Recommended for Local/Docker)
Section titled “Option 1: Disable Absolute Redirects (Recommended for Local/Docker)”By setting absolute_redirect off;, Nginx will issue a relative redirect (e.g., Location: /v1/docs/). The browser will then automatically append this to the existing domain and port.
Edit your nginx.conf or site-specific configuration:
http { # ... existing config ...
server { listen 8080; server_name _;
# Fix for Nginx 1.22+ port dropping absolute_redirect off;
location / { root /usr/share/nginx/html; index index.html; } }}Option 2: Force Port Inclusion (Recommended for Cloud LBs)
Section titled “Option 2: Force Port Inclusion (Recommended for Cloud LBs)”If your environment requires absolute URLs (e.g., specific security requirements or complex proxy chains), ensure Nginx acknowledges the port.
server { listen 8080; server_name example.com;
# Ensure the port is included in the Location header port_in_redirect on; absolute_redirect on;
# Ensure Host header includes the port if proxying proxy_set_header Host $host:$proxy_port;}Applying the fix in Docker Environments
Section titled “Applying the fix in Docker Environments”If you are using the official nginx:1.22-alpine image, you can use an environment variable substitution or a custom config snippet:
FROM nginx:1.22-alpine# Inject a custom config snippet to disable absolute redirects globallyRUN echo "absolute_redirect off;" > /etc/nginx/conf.d/fix-redirects.confCOPY ./html /usr/share/nginx/htmlVerification
Section titled “Verification”After applying the changes and reloading Nginx (nginx -s reload), verify the Location header again:
$ curl -I http://api.internal.cloud:8443/v1/docs
HTTP/1.1 301 Moved PermanentlyLocation: /v1/docs/ <-- Relative path preserves the original port# ORLocation: http://api.internal.cloud:8443/v1/docs/ <-- Port preserved