Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Use a dedicated port-80 server block for a permanent redirect, then let a separate TLS-enabled server block handle content and missing routes. The reliable pattern is return 301 https://$host$request_uri; on port 80, try_files ... =404 for static routing, and error_page 404 /404.html; for a custom page that still produces an HTTP 404.

The configuration pattern that solves both problems

HTTP-to-HTTPS redirection and not-found handling are separate jobs. Port 80 should do nothing except send the client to HTTPS. Port 443 should terminate TLS, locate the requested resource, and return either the resource or a genuine 404. Combining those responsibilities in one content location is a common cause of redirect loops, lost paths, and error pages that return status 200.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Create two server blocks. The first listens on port 80 and issues a permanent redirect. The second listens on 443 with SSL/TLS enabled and serves the site:

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com www.example.com;

    ssl_certificate     /etc/nginx/ssl/example.crt;
    ssl_certificate_key /etc/nginx/ssl/example.key;

    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ $uri.html =404;
    }

    error_page 404 /404.html;
    location = /404.html {
        internal;
    }
}

The redirect uses $request_uri, which carries the original path and query string. A request for http://example.com/docs/start?print=1 therefore becomes https://example.com/docs/start?print=1, rather than being reduced to the home page. The HTTPS block must have a usable certificate and private key; a redirect alone does not configure TLS.

This is the same basic arrangement shown in the NGINX deployment documentation, which uses return 301 https://$server_name$request_uri;. Either variable can be appropriate, but they have different host-selection behavior, explained below.

Build the port-80 redirect server

Use a permanent redirect for a permanent policy

Put the redirect in the server block that listens on port 80:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

return stops request processing immediately, so NGINX does not attempt to find a file, invoke an application, or generate a fallback response on the insecure connection. The status 301 tells clients and caches that the move is permanent. During a short migration test, a temporary status such as 302 can avoid committing that result to caches; change to 301 once the destination and certificates are confirmed.

Preserve the complete request target

Do not replace $request_uri with a hard-coded slash unless every URL is intentionally being collapsed to the home page. $request_uri includes the path and query string as received by NGINX. It is the part of this pattern that keeps deep links, search parameters, and file names intact.

Keep insecure and secure processing separate

A redirect placed inside the HTTPS content location is not an HTTP-to-HTTPS redirect; the request has already reached TLS. Such rules can bounce an already-secure request, interfere with application routing, or create a loop when a proxy is involved. Keep the port-80 block dedicated to the upgrade and put site logic in the 443 block.

Configure the HTTPS server before testing the redirect

Supply a TLS listener, certificate, and key

The secure block needs listen 443 ssl;, a certificate path, and a matching private-key path. The example uses:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
listen 443 ssl;
ssl_certificate     /etc/nginx/ssl/example.crt;
ssl_certificate_key /etc/nginx/ssl/example.key;

Use paths that exist on the server and contain credentials for the names in server_name. If the certificate is absent, unreadable, expired, or does not cover the requested host, the browser can fail the HTTPS connection before NGINX has an opportunity to return a page. HTTP redirection cannot repair a broken TLS configuration.

Define the document root and index

For a static site, set the root and, if needed, the index file:

root /var/www/html;
index index.html;

NGINX resolves a URI against this root when it checks for a file or directory. Confirm that the NGINX worker can read the files and traverse each parent directory. A permissions problem can look like a missing resource, so check the error log when an expected file unexpectedly returns 404.

Make missing static resources return a real 404

Use try_files with =404

The static-content pattern is:

location / {
    try_files $uri $uri/ $uri.html =404;
}

NGINX checks the candidates from left to right. First it tests the URI as a file, then as a directory, then as an HTML file with the same stem. If none exists, the final parameter, =404, ends processing with status 404. That final value is important: without an explicit error result, another fallback can turn an unknown URL into a generated page with a different status.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

For example, a request for /guide can resolve to a real file, a directory index, or /guide.html. A request for /does-not-exist reaches =404 and remains not found. The rule is suitable for a static site whose URL convention allows the optional .html candidate; remove that candidate if extensionless mapping is not part of the site design.

Do not disguise an application fallback as success

Frameworks and reverse-proxy applications often have a catch-all route that renders a page for every path. If that route returns status 200 for an unknown resource, search engines, monitoring systems, and clients see a successful response even though the page is absent. Configure the application or upstream router to emit 404 for an unknown route. If NGINX itself determines that a static resource is absent, retain the explicit =404 result.

Render a custom page without changing the status

Use an internal error-page redirect

Add this to the HTTPS server:

error_page 404 /404.html;
location = /404.html {
    internal;
}

error_page 404 /404.html; performs an internal redirect to the custom document while retaining the not-found response semantics. The client receives the custom content with status 404; the browser does not receive a second public redirect. NGINX’s core-module documentation notes that this internal redirect changes the client request method to GET for methods other than GET and HEAD, which is why an error document should be a safe, read-only resource.

Place the file under the configured root

With root /var/www/html;, create the actual file at /var/www/html/404.html. Keep its references to stylesheets, images, and scripts valid under the same HTTPS origin. A missing custom file can trigger another internal error and leave the client with an unhelpful default response, so verify the file exists and is readable.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prevent direct access to the handler URI

The exact location and internal; directive allow NGINX to use /404.html during error processing but reject a normal external request to that URI. This keeps the handler from becoming a separately browsable page while preserving its use as the error representation. Do not remove internal; merely to make the page testable; test it through a missing URL instead.

Do not use an external 301 for the error document

This alternative has a different meaning:

error_page 404 =301 https://example.com/404.html;

The =301 form explicitly changes the client-visible result to a redirect. A client first receives 301 and then requests the error page, which is not a proper 404 response for the original missing URL. Use the plain internal form when the requirement is a custom page with a 404 status. Use an external redirect only when changing the URL and status is intentional.

Choose the right redirect and host behavior

Decision Recommended choice What it means
Permanent HTTP-to-HTTPS migration 301 Clients are told the move is permanent.
Temporary testing or staged migration 302, or another temporary redirect selected for the client behavior you require Use while the destination is still being validated; change to a permanent status after testing.
Method-preserving permanent redirect 308 Use only when preserving the request method across the permanent move is required and all clients support the intended behavior.
Method-preserving temporary redirect 307 Use only when a temporary move must retain the method.
Host in the Location header $host Builds the destination from the host selected for the request.
One fixed canonical hostname A literal approved hostname Always sends clients to that one host, which is useful when consolidating www and non-www names.

The simplest two-name example accepts both example.com and www.example.com and preserves whichever host arrived in the request by using $host. That is not the same as canonicalization. If the site must always use one hostname, write that hostname explicitly in the redirect and create server-name coverage for the alternatives. Do not assume that listing two names automatically selects one canonical form.

The NGINX deployment example uses $server_name instead. That variable reflects the name selected by the server block, whereas $host reflects the request host after NGINX’s host-selection rules. Choose deliberately, especially when several names, aliases, or a fixed canonical domain are involved.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Account for TLS termination at a proxy or load balancer

Some deployments terminate TLS at a load balancer, CDN, or reverse proxy and forward traffic to NGINX over HTTP. In that architecture, NGINX may see an internal HTTP connection even though the visitor used HTTPS. A blanket port-80 redirect based only on the connection between the proxy and NGINX can redirect an already-secure visitor repeatedly.

Use the proxy’s trusted forwarded-protocol signal and configure the trust boundary so NGINX can distinguish an original HTTPS request from an original HTTP request. The exact header name, trusted proxy addresses, and configuration depend on the intermediary; there is no single header setup that is safe for every architecture. Never trust an arbitrary client-supplied forwarding header without restricting which proxy may set it.

When TLS terminates directly in NGINX, the two-block configuration above is sufficient: port 80 redirects, and port 443 serves the site. When TLS terminates upstream, document that decision and test both the edge response and the origin response so the redirect is enforced once, not at every hop.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Validate the configuration safely

Check syntax before reloading

  1. Save the server blocks in the enabled NGINX configuration location used by the distribution.
  2. Run nginx -t. Resolve every reported syntax error, missing file, certificate path, or duplicate/conflicting server definition before proceeding.
  3. Reload NGINX using the service manager for the host, for example systemctl reload nginx, so existing connections are not unnecessarily dropped.

The exact configuration directory and service command vary by operating system, but the order does not: test first, then reload. A reload that fails validation leaves the previous working configuration in place; do not assume that editing a file changed the running process.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Test the redirect and its URI preservation

Use a request that includes both a deep path and a query string:

curl -I 'http://example.com/docs/start?print=1'

Expect a 301 response with a Location value equivalent to https://example.com/docs/start?print=1. Check that the scheme changed, the path remained intact, and the query string was not dropped. Repeat the test for each hostname accepted by the port-80 server_name list.

Test an existing and a missing HTTPS resource

curl -s -o /dev/null -w '%{http_code}n' 'https://example.com/index.html'
curl -s -o /dev/null -w '%{http_code}n' 'https://example.com/does-not-exist'

The first request should return the status your site intends for an existing document. The second must return 404, not 200 and not 301. To inspect headers and confirm the custom body, use curl -i on the missing HTTPS URL. The response should contain the 404 status while displaying the contents of 404.html.

Check direct access to the error handler

curl -i 'https://example.com/404.html'

Because the location is marked internal, a direct client request should not be treated as a normal public document. Test the missing URL as the primary behavior; the handler URI itself is deliberately restricted.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Test through the real edge path

If a proxy terminates TLS, run the same checks against the public hostname rather than only against the origin address. Confirm that an original HTTPS request does not receive another HTTPS redirect and that an original HTTP request receives exactly one upgrade. Inspect proxy and NGINX logs together when the observed status differs between edge and origin.

Troubleshoot common failures

The browser reports a redirect loop

  • Check whether a redirect rule was added to the HTTPS content location instead of only to the port-80 server.
  • If a load balancer terminates TLS, verify that NGINX receives and trusts the intermediary’s original-protocol signal.
  • Look for multiple layers redirecting in opposite directions, such as an edge forcing one hostname while NGINX forces another.

The redirect loses the path or query string

  • Confirm that the return target ends with $request_uri.
  • Check for a second redirect generated by the application or proxy that replaces the original target.
  • Compare the first response’s Location header with the URL supplied to curl, rather than relying only on a browser’s final address.

The missing URL returns 200

  • Inspect the location / rule for a catch-all application fallback.
  • Ensure the static rule ends with =404 when no candidate exists.
  • Configure the upstream application to return 404 for unknown routes instead of rendering its normal home page.

The missing URL returns 301

  • Search for error_page 404 =301 or another explicit redirect attached to the 404 handler.
  • Replace it with error_page 404 /404.html; when the original URL must remain a 404.
  • Check whether a separate canonical-host rule is redirecting the request before the error handler runs.

The custom page is not displayed

  • Verify that /var/www/html/404.html matches the configured root and is readable by NGINX.
  • Confirm that the exact location is spelled location = /404.html and is inside the intended HTTPS server block.
  • Review the error log for a second missing file, permission denial, or a conflicting location that takes precedence.

HTTPS fails before any page appears

  • Check the certificate and key paths in the 443 server block.
  • Confirm that the certificate covers the hostname the redirect produces.
  • Run nginx -t and inspect startup or reload errors before debugging application routing.

Production checklist

  • Port 80 has its own server block with return 301 (or a deliberately temporary status during testing).
  • The redirect target contains $request_uri so paths and query strings survive.
  • Port 443 has a TLS listener, certificate, private key, server names, and the intended document root.
  • Static routing uses try_files and ends in =404 when no file or directory matches.
  • The custom file exists under the root and is served through error_page 404 /404.html;.
  • The exact /404.html location is marked internal when the handler should not be publicly browsable.
  • No =301 is attached to the 404 error page unless an external redirect is genuinely intended.
  • Application fallbacks return 404 for absent resources rather than a generated 200 page.
  • If a proxy terminates TLS, only trusted forwarded-protocol information controls redirect decisions.
  • nginx -t passes before reload, and both edge and origin behavior have been checked with a deep URL, query string, existing resource, and missing resource.

With those boundaries in place, the client sees one permanent upgrade from HTTP to HTTPS, the original request target remains intact, and an unknown HTTPS URL receives a custom response with the correct 404 status.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.